Run clang-format
This commit is contained in:
104
.clang-format
Normal file
104
.clang-format
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
Language: Cpp
|
||||||
|
# BasedOnStyle: Google
|
||||||
|
AccessModifierOffset: -2
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignConsecutiveAssignments: true
|
||||||
|
AlignConsecutiveDeclarations: false
|
||||||
|
AlignEscapedNewlines: Right
|
||||||
|
AlignOperands: true
|
||||||
|
AlignTrailingComments: true
|
||||||
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortFunctionsOnASingleLine: Inline
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: true
|
||||||
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
|
AlwaysBreakAfterReturnType: TopLevelDefinitions
|
||||||
|
AlwaysBreakBeforeMultilineStrings: true
|
||||||
|
AlwaysBreakTemplateDeclarations: true
|
||||||
|
BinPackArguments: false
|
||||||
|
BinPackParameters: false
|
||||||
|
BreakBeforeBraces : Custom
|
||||||
|
BraceWrapping:
|
||||||
|
AfterClass: false
|
||||||
|
AfterControlStatement: false
|
||||||
|
AfterEnum: false
|
||||||
|
AfterFunction: true
|
||||||
|
AfterNamespace: false
|
||||||
|
AfterObjCDeclaration: false
|
||||||
|
AfterStruct: false
|
||||||
|
AfterUnion: false
|
||||||
|
BeforeCatch: true
|
||||||
|
BeforeElse: true
|
||||||
|
IndentBraces: false
|
||||||
|
SplitEmptyFunction: true
|
||||||
|
SplitEmptyRecord: true
|
||||||
|
SplitEmptyNamespace: true
|
||||||
|
BreakBeforeBinaryOperators: None
|
||||||
|
BreakBeforeInheritanceComma: true
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakAfterJavaFieldAnnotations: false
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 80
|
||||||
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
|
CompactNamespaces: false
|
||||||
|
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||||
|
ConstructorInitializerIndentWidth: 4
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
DisableFormat: false
|
||||||
|
ExperimentalAutoDetectBinPacking: false
|
||||||
|
FixNamespaceComments: true
|
||||||
|
ForEachMacros:
|
||||||
|
- foreach
|
||||||
|
- Q_FOREACH
|
||||||
|
- BOOST_FOREACH
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^<.*\.h>'
|
||||||
|
Priority: 1
|
||||||
|
- Regex: '^<.*'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '.*'
|
||||||
|
Priority: 3
|
||||||
|
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentWidth: 2
|
||||||
|
IndentWrappedFunctionNames: false
|
||||||
|
JavaScriptQuotes: Leave
|
||||||
|
JavaScriptWrapImports: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
MacroBlockBegin: ''
|
||||||
|
MacroBlockEnd: ''
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
ObjCBlockIndentWidth: 2
|
||||||
|
ObjCSpaceAfterProperty: false
|
||||||
|
ObjCSpaceBeforeProtocolList: false
|
||||||
|
PenaltyBreakAssignment: 2
|
||||||
|
PenaltyBreakBeforeFirstCallParameter: 1
|
||||||
|
PenaltyBreakComment: 300
|
||||||
|
PenaltyBreakFirstLessLess: 120
|
||||||
|
PenaltyBreakString: 1000
|
||||||
|
PenaltyExcessCharacter: 1000000
|
||||||
|
PenaltyReturnTypeOnItsOwnLine: 200
|
||||||
|
PointerAlignment: Left
|
||||||
|
ReflowComments: true
|
||||||
|
SortIncludes: true
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: true
|
||||||
|
SpaceAfterTemplateKeyword: true
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 2
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: Cpp11
|
||||||
|
TabWidth: 2
|
||||||
|
UseTab: Never
|
||||||
114
.cproject
Normal file
114
.cproject
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.settings">
|
||||||
|
<cconfiguration id="cdt.managedbuild.config.gnu.cross.exe.debug.1192994012">
|
||||||
|
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.cross.exe.debug.1192994012" moduleId="org.eclipse.cdt.core.settings" name="Debug">
|
||||||
|
<externalSettings/>
|
||||||
|
<extensions>
|
||||||
|
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
</extensions>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||||
|
<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.debug" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.cross.exe.debug.1192994012" name="Debug" parent="cdt.managedbuild.config.gnu.cross.exe.debug">
|
||||||
|
<folderInfo id="cdt.managedbuild.config.gnu.cross.exe.debug.1192994012." name="/" resourcePath="">
|
||||||
|
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.exe.debug.1122035376" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.exe.debug">
|
||||||
|
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.890879207" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
|
||||||
|
<builder buildPath="${workspace_loc:/concurrency-in-action}/Debug" id="cdt.managedbuild.builder.gnu.cross.730695170" managedBuildOn="true" name="Gnu Make Builder.Debug" superClass="cdt.managedbuild.builder.gnu.cross"/>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.680385655" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
|
||||||
|
<option defaultValue="gnu.c.optimization.level.none" id="gnu.c.compiler.option.optimization.level.1148144853" superClass="gnu.c.compiler.option.optimization.level" useByScannerDiscovery="false" valueType="enumerated"/>
|
||||||
|
<option defaultValue="gnu.c.debugging.level.max" id="gnu.c.compiler.option.debugging.level.1348513497" superClass="gnu.c.compiler.option.debugging.level" useByScannerDiscovery="false" valueType="enumerated"/>
|
||||||
|
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.2123668139" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
||||||
|
</tool>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.241274475" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
|
||||||
|
<option id="gnu.cpp.compiler.option.optimization.level.427163038" superClass="gnu.cpp.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.none" valueType="enumerated"/>
|
||||||
|
<option defaultValue="gnu.cpp.compiler.debugging.level.max" id="gnu.cpp.compiler.option.debugging.level.1633475294" superClass="gnu.cpp.compiler.option.debugging.level" useByScannerDiscovery="false" valueType="enumerated"/>
|
||||||
|
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1614770923" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
|
||||||
|
</tool>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.301173203" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1152604941" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
|
||||||
|
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.1534571725" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
|
||||||
|
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
|
||||||
|
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
|
||||||
|
</inputType>
|
||||||
|
</tool>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.1417897260" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.206754207" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
|
||||||
|
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.2135072614" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||||
|
</tool>
|
||||||
|
</toolChain>
|
||||||
|
</folderInfo>
|
||||||
|
</configuration>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||||
|
</cconfiguration>
|
||||||
|
<cconfiguration id="cdt.managedbuild.config.gnu.cross.exe.release.1308789762">
|
||||||
|
<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.config.gnu.cross.exe.release.1308789762" moduleId="org.eclipse.cdt.core.settings" name="Release">
|
||||||
|
<externalSettings/>
|
||||||
|
<extensions>
|
||||||
|
<extension id="org.eclipse.cdt.core.ELF" point="org.eclipse.cdt.core.BinaryParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
|
||||||
|
</extensions>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||||
|
<configuration artifactName="${ProjName}" buildArtefactType="org.eclipse.cdt.build.core.buildArtefactType.exe" buildProperties="org.eclipse.cdt.build.core.buildArtefactType=org.eclipse.cdt.build.core.buildArtefactType.exe,org.eclipse.cdt.build.core.buildType=org.eclipse.cdt.build.core.buildType.release" cleanCommand="rm -rf" description="" id="cdt.managedbuild.config.gnu.cross.exe.release.1308789762" name="Release" parent="cdt.managedbuild.config.gnu.cross.exe.release">
|
||||||
|
<folderInfo id="cdt.managedbuild.config.gnu.cross.exe.release.1308789762." name="/" resourcePath="">
|
||||||
|
<toolChain id="cdt.managedbuild.toolchain.gnu.cross.exe.release.2089802121" name="Cross GCC" superClass="cdt.managedbuild.toolchain.gnu.cross.exe.release">
|
||||||
|
<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.321811258" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
|
||||||
|
<builder buildPath="${workspace_loc:/concurrency-in-action}/Release" id="cdt.managedbuild.builder.gnu.cross.581862874" managedBuildOn="true" name="Gnu Make Builder.Release" superClass="cdt.managedbuild.builder.gnu.cross"/>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.c.compiler.1263617891" name="Cross GCC Compiler" superClass="cdt.managedbuild.tool.gnu.cross.c.compiler">
|
||||||
|
<option defaultValue="gnu.c.optimization.level.most" id="gnu.c.compiler.option.optimization.level.1570617197" superClass="gnu.c.compiler.option.optimization.level" useByScannerDiscovery="false" valueType="enumerated"/>
|
||||||
|
<option defaultValue="gnu.c.debugging.level.none" id="gnu.c.compiler.option.debugging.level.1573251332" superClass="gnu.c.compiler.option.debugging.level" useByScannerDiscovery="false" valueType="enumerated"/>
|
||||||
|
<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.1569558056" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
|
||||||
|
</tool>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.compiler.1348481540" name="Cross G++ Compiler" superClass="cdt.managedbuild.tool.gnu.cross.cpp.compiler">
|
||||||
|
<option id="gnu.cpp.compiler.option.optimization.level.100340695" superClass="gnu.cpp.compiler.option.optimization.level" useByScannerDiscovery="false" value="gnu.cpp.compiler.optimization.level.most" valueType="enumerated"/>
|
||||||
|
<option defaultValue="gnu.cpp.compiler.debugging.level.none" id="gnu.cpp.compiler.option.debugging.level.584780047" superClass="gnu.cpp.compiler.option.debugging.level" useByScannerDiscovery="false" valueType="enumerated"/>
|
||||||
|
<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.1437636074" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
|
||||||
|
</tool>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.c.linker.170044107" name="Cross GCC Linker" superClass="cdt.managedbuild.tool.gnu.cross.c.linker"/>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.cpp.linker.1428396919" name="Cross G++ Linker" superClass="cdt.managedbuild.tool.gnu.cross.cpp.linker">
|
||||||
|
<inputType id="cdt.managedbuild.tool.gnu.cpp.linker.input.948393561" superClass="cdt.managedbuild.tool.gnu.cpp.linker.input">
|
||||||
|
<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
|
||||||
|
<additionalInput kind="additionalinput" paths="$(LIBS)"/>
|
||||||
|
</inputType>
|
||||||
|
</tool>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.archiver.1625798495" name="Cross GCC Archiver" superClass="cdt.managedbuild.tool.gnu.cross.archiver"/>
|
||||||
|
<tool id="cdt.managedbuild.tool.gnu.cross.assembler.70580537" name="Cross GCC Assembler" superClass="cdt.managedbuild.tool.gnu.cross.assembler">
|
||||||
|
<inputType id="cdt.managedbuild.tool.gnu.assembler.input.226212603" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
|
||||||
|
</tool>
|
||||||
|
</toolChain>
|
||||||
|
</folderInfo>
|
||||||
|
</configuration>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
|
||||||
|
</cconfiguration>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="cdtBuildSystem" version="4.0.0">
|
||||||
|
<project id="concurrency-in-action.cdt.managedbuild.target.gnu.cross.exe.508515326" name="Executable" projectType="cdt.managedbuild.target.gnu.cross.exe"/>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="scannerConfiguration">
|
||||||
|
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||||
|
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.debug.1192994012;cdt.managedbuild.config.gnu.cross.exe.debug.1192994012.;cdt.managedbuild.tool.gnu.cross.c.compiler.680385655;cdt.managedbuild.tool.gnu.c.compiler.input.2123668139">
|
||||||
|
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||||
|
</scannerConfigBuildInfo>
|
||||||
|
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.debug.1192994012;cdt.managedbuild.config.gnu.cross.exe.debug.1192994012.;cdt.managedbuild.tool.gnu.cross.cpp.compiler.241274475;cdt.managedbuild.tool.gnu.cpp.compiler.input.1614770923">
|
||||||
|
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||||
|
</scannerConfigBuildInfo>
|
||||||
|
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.release.1308789762;cdt.managedbuild.config.gnu.cross.exe.release.1308789762.;cdt.managedbuild.tool.gnu.cross.cpp.compiler.1348481540;cdt.managedbuild.tool.gnu.cpp.compiler.input.1437636074">
|
||||||
|
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||||
|
</scannerConfigBuildInfo>
|
||||||
|
<scannerConfigBuildInfo instanceId="cdt.managedbuild.config.gnu.cross.exe.release.1308789762;cdt.managedbuild.config.gnu.cross.exe.release.1308789762.;cdt.managedbuild.tool.gnu.cross.c.compiler.1263617891;cdt.managedbuild.tool.gnu.c.compiler.input.1569558056">
|
||||||
|
<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
|
||||||
|
</scannerConfigBuildInfo>
|
||||||
|
</storageModule>
|
||||||
|
<storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
|
||||||
|
</cproject>
|
||||||
27
.project
Normal file
27
.project
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>concurrency-in-action</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
|
||||||
|
<triggers>clean,full,incremental,</triggers>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||||
|
<triggers>full,incremental,</triggers>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||||
|
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||||
|
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||||
|
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
25
.settings/language.settings.xml
Normal file
25
.settings/language.settings.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<project>
|
||||||
|
<configuration id="cdt.managedbuild.config.gnu.cross.exe.debug.1192994012" name="Debug">
|
||||||
|
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||||
|
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||||
|
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
|
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
|
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="564395207262711818" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||||
|
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||||
|
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||||
|
</provider>
|
||||||
|
</extension>
|
||||||
|
</configuration>
|
||||||
|
<configuration id="cdt.managedbuild.config.gnu.cross.exe.release.1308789762" name="Release">
|
||||||
|
<extension point="org.eclipse.cdt.core.LanguageSettingsProvider">
|
||||||
|
<provider copy-of="extension" id="org.eclipse.cdt.ui.UserLanguageSettingsProvider"/>
|
||||||
|
<provider-reference id="org.eclipse.cdt.core.ReferencedProjectsLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
|
<provider-reference id="org.eclipse.cdt.managedbuilder.core.MBSLanguageSettingsProvider" ref="shared-provider"/>
|
||||||
|
<provider class="org.eclipse.cdt.internal.build.crossgcc.CrossGCCBuiltinSpecsDetector" console="false" env-hash="564395207262711818" id="org.eclipse.cdt.build.crossgcc.CrossGCCBuiltinSpecsDetector" keep-relative-paths="false" name="CDT Cross GCC Built-in Compiler Settings" parameter="${COMMAND} ${FLAGS} -E -P -v -dD "${INPUTS}"" prefer-non-shared="true">
|
||||||
|
<language-scope id="org.eclipse.cdt.core.gcc"/>
|
||||||
|
<language-scope id="org.eclipse.cdt.core.g++"/>
|
||||||
|
</provider>
|
||||||
|
</extension>
|
||||||
|
</configuration>
|
||||||
|
</project>
|
||||||
@@ -1,18 +1,17 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
void hello()
|
void
|
||||||
|
hello()
|
||||||
{
|
{
|
||||||
std::cout << "Hello Concurrent World\n";
|
std::cout << "Hello Concurrent World\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
//boost::thread t([]()
|
// boost::thread t([]()
|
||||||
std::thread t([]()
|
std::thread t([]() { std::cout << "Hello Concurrent World\n"; });
|
||||||
{
|
|
||||||
std::cout << "Hello Concurrent World\n";
|
|
||||||
});
|
|
||||||
|
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,69 +1,69 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <numeric> // std::accumulate
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <numeric> // std::accumulate
|
#include <vector>
|
||||||
|
|
||||||
template <typename Iterator, typename T>
|
template <typename Iterator, typename T>
|
||||||
struct accumulate_block
|
struct accumulate_block {
|
||||||
{
|
void operator()(Iterator first, Iterator last, T& result)
|
||||||
void operator()(Iterator first, Iterator last, T& result)
|
{
|
||||||
{
|
result = std::accumulate(first, last, result);
|
||||||
result = std::accumulate(first,last,result);
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Iterator, typename T>
|
template <typename Iterator, typename T>
|
||||||
T parallel_accumulate(Iterator first, Iterator last, T init)
|
T
|
||||||
|
parallel_accumulate(Iterator first, Iterator last, T init)
|
||||||
{
|
{
|
||||||
unsigned long const length = std::distance(first,last);
|
unsigned long const length = std::distance(first, last);
|
||||||
|
|
||||||
if (!length) return init;
|
if (!length)
|
||||||
|
return init;
|
||||||
|
|
||||||
unsigned long const min_per_thread = 25;
|
unsigned long const min_per_thread = 25;
|
||||||
unsigned long const max_threads =
|
unsigned long const max_threads =
|
||||||
(length+min_per_thread-1)/min_per_thread;
|
(length + min_per_thread - 1) / min_per_thread;
|
||||||
|
|
||||||
unsigned long const hardware_threads =
|
unsigned long const hardware_threads = std::thread::hardware_concurrency();
|
||||||
std::thread::hardware_concurrency();
|
|
||||||
|
|
||||||
unsigned long const num_threads =
|
unsigned long const num_threads =
|
||||||
std::min(hardware_threads != 0 ? hardware_threads:2, max_threads);
|
std::min(hardware_threads != 0 ? hardware_threads : 2, max_threads);
|
||||||
|
|
||||||
unsigned long const block_size = length / num_threads;
|
unsigned long const block_size = length / num_threads;
|
||||||
|
|
||||||
std::vector<T> results(num_threads);
|
std::vector<T> results(num_threads);
|
||||||
std::vector<std::thread> threads(num_threads-1);
|
std::vector<std::thread> threads(num_threads - 1);
|
||||||
|
|
||||||
Iterator block_start = first;
|
Iterator block_start = first;
|
||||||
for (unsigned long i = 0; i < (num_threads-1); ++i)
|
for (unsigned long i = 0; i < (num_threads - 1); ++i) {
|
||||||
{
|
Iterator block_end = block_start;
|
||||||
Iterator block_end = block_start;
|
std::advance(block_end, block_size);
|
||||||
std::advance(block_end,block_size);
|
threads[i] = std::thread(accumulate_block<Iterator, T>(),
|
||||||
threads[i] = std::thread(
|
block_start,
|
||||||
accumulate_block<Iterator,T>(),
|
block_end,
|
||||||
block_start,block_end,std::ref(results[i]));
|
std::ref(results[i]));
|
||||||
block_start = block_end;
|
block_start = block_end;
|
||||||
}
|
}
|
||||||
accumulate_block<std::vector<int>::iterator,int>()(
|
accumulate_block<std::vector<int>::iterator, int>()(
|
||||||
block_start,last,results[num_threads-1]);
|
block_start, last, results[num_threads - 1]);
|
||||||
|
|
||||||
std::for_each(threads.begin(),threads.end(),
|
std::for_each(
|
||||||
std::mem_fn(&std::thread::join));
|
threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
|
||||||
|
|
||||||
return std::accumulate(results.begin(),results.end(),init);
|
return std::accumulate(results.begin(), results.end(), init);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
std::vector<int> numbers(50);
|
std::vector<int> numbers(50);
|
||||||
for (int i = 0; i < 50; ++i) {
|
for (int i = 0; i < 50; ++i) {
|
||||||
numbers[i] = i;
|
numbers[i] = i;
|
||||||
}
|
}
|
||||||
int result = parallel_accumulate<std::vector<int>::iterator,int>
|
int result = parallel_accumulate<std::vector<int>::iterator, int>(
|
||||||
(numbers.begin(),numbers.end(),0);
|
numbers.begin(), numbers.end(), 0);
|
||||||
|
|
||||||
std::cout << "result: " << result << std::endl;
|
std::cout << "result: " << result << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,57 +1,56 @@
|
|||||||
|
#include <climits>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <climits>
|
|
||||||
|
|
||||||
class hierarchical_mutex
|
class hierarchical_mutex {
|
||||||
{
|
std::mutex internal_mutex;
|
||||||
std::mutex internal_mutex;
|
unsigned long const hierarchy_value;
|
||||||
unsigned long const hierarchy_value;
|
unsigned long previous_hierarchy_value;
|
||||||
unsigned long previous_hierarchy_value;
|
static thread_local unsigned long this_thread_hierarchy_value;
|
||||||
static thread_local unsigned long this_thread_hierarchy_value;
|
|
||||||
|
|
||||||
void check_for_hierarchy_violation()
|
void check_for_hierarchy_violation()
|
||||||
{
|
{
|
||||||
if(this_thread_hierarchy_value <= hierarchy_value)
|
if (this_thread_hierarchy_value <= hierarchy_value) {
|
||||||
{
|
throw std::logic_error("mutex hierarchy violated");
|
||||||
throw std::logic_error("mutex hierarchy violated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void update_hierarchy_value()
|
|
||||||
{
|
|
||||||
previous_hierarchy_value=this_thread_hierarchy_value;
|
|
||||||
this_thread_hierarchy_value=hierarchy_value;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
void update_hierarchy_value()
|
||||||
|
{
|
||||||
|
previous_hierarchy_value = this_thread_hierarchy_value;
|
||||||
|
this_thread_hierarchy_value = hierarchy_value;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit hierarchical_mutex(unsigned long value):
|
explicit hierarchical_mutex(unsigned long value)
|
||||||
hierarchy_value(value),
|
: hierarchy_value(value), previous_hierarchy_value(0)
|
||||||
previous_hierarchy_value(0)
|
{
|
||||||
{}
|
}
|
||||||
void lock()
|
void lock()
|
||||||
{
|
{
|
||||||
check_for_hierarchy_violation();
|
check_for_hierarchy_violation();
|
||||||
internal_mutex.lock();
|
internal_mutex.lock();
|
||||||
update_hierarchy_value();
|
update_hierarchy_value();
|
||||||
}
|
}
|
||||||
void unlock()
|
void unlock()
|
||||||
{
|
{
|
||||||
this_thread_hierarchy_value=previous_hierarchy_value;
|
this_thread_hierarchy_value = previous_hierarchy_value;
|
||||||
internal_mutex.unlock();
|
internal_mutex.unlock();
|
||||||
}
|
}
|
||||||
bool try_lock()
|
bool try_lock()
|
||||||
{
|
{
|
||||||
check_for_hierarchy_violation();
|
check_for_hierarchy_violation();
|
||||||
if(!internal_mutex.try_lock())
|
if (!internal_mutex.try_lock())
|
||||||
return false;
|
return false;
|
||||||
update_hierarchy_value();
|
update_hierarchy_value();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
thread_local unsigned long
|
thread_local unsigned long hierarchical_mutex::this_thread_hierarchy_value(
|
||||||
hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);
|
ULONG_MAX);
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
hierarchical_mutex m1(42);
|
hierarchical_mutex m1(42);
|
||||||
hierarchical_mutex m2(2000);
|
hierarchical_mutex m2(2000);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
110
ch3/stack.cpp
110
ch3/stack.cpp
@@ -1,68 +1,64 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stack>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
struct empty_stack: std::exception
|
struct empty_stack : std::exception {
|
||||||
{
|
const char* what() const throw() { return "empty stack"; }
|
||||||
const char* what() const throw()
|
|
||||||
{
|
|
||||||
return "empty stack";
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_stack
|
class threadsafe_stack {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
std::stack<T> data;
|
std::stack<T> data;
|
||||||
mutable std::mutex m;
|
mutable std::mutex m;
|
||||||
public:
|
|
||||||
threadsafe_stack(){}
|
|
||||||
threadsafe_stack(const threadsafe_stack& other)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(other.m);
|
|
||||||
data=other.data;
|
|
||||||
}
|
|
||||||
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
|
|
||||||
|
|
||||||
void push(T new_value)
|
public:
|
||||||
{
|
threadsafe_stack() {}
|
||||||
std::lock_guard<std::mutex> lock(m);
|
threadsafe_stack(const threadsafe_stack& other)
|
||||||
data.push(new_value);
|
{
|
||||||
}
|
std::lock_guard<std::mutex> lock(other.m);
|
||||||
std::shared_ptr<T> pop()
|
data = other.data;
|
||||||
{
|
}
|
||||||
std::lock_guard<std::mutex> lock(m);
|
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
|
||||||
if(data.empty()) throw empty_stack();
|
|
||||||
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
|
void push(T new_value)
|
||||||
data.pop();
|
{
|
||||||
return res;
|
std::lock_guard<std::mutex> lock(m);
|
||||||
}
|
data.push(new_value);
|
||||||
void pop(T& value)
|
}
|
||||||
{
|
std::shared_ptr<T> pop()
|
||||||
std::lock_guard<std::mutex> lock(m);
|
{
|
||||||
if(data.empty()) throw empty_stack();
|
std::lock_guard<std::mutex> lock(m);
|
||||||
value=data.top();
|
if (data.empty())
|
||||||
data.pop();
|
throw empty_stack();
|
||||||
}
|
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
|
||||||
bool empty() const
|
data.pop();
|
||||||
{
|
return res;
|
||||||
std::lock_guard<std::mutex> lock(m);
|
}
|
||||||
return data.empty();
|
void pop(T& value)
|
||||||
}
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
if (data.empty())
|
||||||
|
throw empty_stack();
|
||||||
|
value = data.top();
|
||||||
|
data.pop();
|
||||||
|
}
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
return data.empty();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
threadsafe_stack<int> si;
|
threadsafe_stack<int> si;
|
||||||
si.push(5);
|
si.push(5);
|
||||||
si.pop();
|
si.pop();
|
||||||
if(!si.empty())
|
if (!si.empty()) {
|
||||||
{
|
int x;
|
||||||
int x;
|
si.pop(x);
|
||||||
si.pop(x);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
171
ch4/queue.cpp
171
ch4/queue.cpp
@@ -1,104 +1,105 @@
|
|||||||
#include <iostream>
|
#include <condition_variable>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <queue>
|
#include <iostream>
|
||||||
#include <thread>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class queue
|
class queue {
|
||||||
{
|
private:
|
||||||
private:
|
mutable std::mutex mut;
|
||||||
mutable std::mutex mut;
|
std::queue<T> data_queue;
|
||||||
std::queue<T> data_queue;
|
std::condition_variable data_cond;
|
||||||
std::condition_variable data_cond;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
queue(){}
|
queue() {}
|
||||||
|
|
||||||
queue(const queue& other)
|
queue(const queue& other)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(other.mut);
|
std::lock_guard<std::mutex> lk(other.mut);
|
||||||
data_queue = other.data_queue;
|
data_queue = other.data_queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(T new_value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
|
data_queue.push(new_value);
|
||||||
|
data_cond.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_and_pop(T& value)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
|
data_cond.wait(lk, [this] { return !data_queue.empty(); });
|
||||||
|
value = data_queue.front();
|
||||||
|
data_queue.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<T> wait_and_pop()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
|
data_cond.wait(lk, [this] { return !data_queue.empty(); });
|
||||||
|
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
|
||||||
|
data_queue.pop();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_pop(T& value)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
|
if (data_queue.empty()) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
value = data_queue.front();
|
||||||
|
data_queue.pop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void push(T new_value)
|
std::shared_ptr<T> try_pop()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
data_queue.push(new_value);
|
if (data_queue.empty()) {
|
||||||
data_cond.notify_one();
|
return std::shared_ptr<T>();
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
|
||||||
|
data_queue.pop();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void wait_and_pop(T& value)
|
bool empty() const
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
data_cond.wait(lk, [this]{return !data_queue.empty();});
|
return data_queue.empty();
|
||||||
value = data_queue.front();
|
}
|
||||||
data_queue.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<T> wait_and_pop()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
|
||||||
data_cond.wait(lk, [this]{return !data_queue.empty();});
|
|
||||||
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
|
|
||||||
data_queue.pop();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool try_pop(T& value)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
|
||||||
if (data_queue.empty()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
value = data_queue.front();
|
|
||||||
data_queue.pop();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
|
||||||
if (data_queue.empty()) {
|
|
||||||
return std::shared_ptr<T>();
|
|
||||||
}
|
|
||||||
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
|
|
||||||
data_queue.pop();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
|
||||||
return data_queue.empty();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void push(queue<int>* q)
|
void
|
||||||
|
push(queue<int>* q)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
//std::cout << "pushing " << i << std::endl;
|
// std::cout << "pushing " << i << std::endl;
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
q->push(i);
|
q->push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(queue<int>* q)
|
void
|
||||||
|
pop(queue<int>* q)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("poping %d\n", *q->wait_and_pop());
|
printf("poping %d\n", *q->wait_and_pop());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
queue<int> q;
|
|
||||||
std::thread t1(push,&q);
|
|
||||||
std::thread t2(pop,&q);
|
|
||||||
t1.join();
|
|
||||||
t2.join();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
queue<int> q;
|
||||||
|
std::thread t1(push, &q);
|
||||||
|
std::thread t2(pop, &q);
|
||||||
|
t1.join();
|
||||||
|
t2.join();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,74 +1,70 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <list>
|
|
||||||
#include <thread>
|
|
||||||
#include <memory>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
#include <future>
|
#include <future>
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
std::ostream& operator<<(std::ostream& ostr, const std::list<int>& list)
|
std::ostream&
|
||||||
|
operator<<(std::ostream& ostr, const std::list<int>& list)
|
||||||
{
|
{
|
||||||
for (auto &i : list) {
|
for (auto& i : list) {
|
||||||
ostr << " " << i;
|
ostr << " " << i;
|
||||||
}
|
}
|
||||||
return ostr;
|
return ostr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
std::list<T> sequential_quick_sort(std::list<T> input)
|
std::list<T>
|
||||||
|
sequential_quick_sort(std::list<T> input)
|
||||||
{
|
{
|
||||||
if(input.empty())
|
if (input.empty()) {
|
||||||
{
|
return input;
|
||||||
return input;
|
}
|
||||||
}
|
std::list<T> result;
|
||||||
std::list<T> result;
|
result.splice(result.begin(), input, input.begin());
|
||||||
result.splice(result.begin(),input,input.begin());
|
T const& pivot = *result.begin();
|
||||||
T const& pivot=*result.begin();
|
auto divide_point = std::partition(
|
||||||
auto divide_point=std::partition(input.begin(),input.end(),
|
input.begin(), input.end(), [&](T const& t) { return t < pivot; });
|
||||||
[&](T const& t){return t<pivot;});
|
std::list<T> lower_part;
|
||||||
std::list<T> lower_part;
|
lower_part.splice(lower_part.end(), input, input.begin(), divide_point);
|
||||||
lower_part.splice(lower_part.end(),input,input.begin(),
|
auto new_lower(sequential_quick_sort(std::move(lower_part)));
|
||||||
divide_point);
|
auto new_higher(sequential_quick_sort(std::move(input)));
|
||||||
auto new_lower(
|
result.splice(result.end(), new_higher);
|
||||||
sequential_quick_sort(std::move(lower_part)));
|
result.splice(result.begin(), new_lower);
|
||||||
auto new_higher(
|
return result;
|
||||||
sequential_quick_sort(std::move(input)));
|
|
||||||
result.splice(result.end(),new_higher);
|
|
||||||
result.splice(result.begin(),new_lower);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
std::list<T> parallel_quick_sort(std::list<T> input)
|
std::list<T>
|
||||||
|
parallel_quick_sort(std::list<T> input)
|
||||||
{
|
{
|
||||||
if(input.empty())
|
if (input.empty()) {
|
||||||
{
|
return input;
|
||||||
return input;
|
}
|
||||||
}
|
std::list<T> result;
|
||||||
std::list<T> result;
|
result.splice(result.begin(), input, input.begin());
|
||||||
result.splice(result.begin(),input,input.begin());
|
T const& pivot = *result.begin();
|
||||||
T const& pivot=*result.begin();
|
auto divide_point = std::partition(
|
||||||
auto divide_point=std::partition(input.begin(),input.end(),
|
input.begin(), input.end(), [&](T const& t) { return t < pivot; });
|
||||||
[&](T const& t){return t<pivot;});
|
std::list<T> lower_part;
|
||||||
std::list<T> lower_part;
|
lower_part.splice(lower_part.end(), input, input.begin(), divide_point);
|
||||||
lower_part.splice(lower_part.end(),input,input.begin(),
|
std::future<std::list<T>> new_lower(
|
||||||
divide_point);
|
std::async(¶llel_quick_sort<T>, std::move(lower_part)));
|
||||||
std::future<std::list<T> > new_lower(
|
auto new_higher(parallel_quick_sort(std::move(input)));
|
||||||
std::async(¶llel_quick_sort<T>,std::move(lower_part)));
|
result.splice(result.end(), new_higher);
|
||||||
auto new_higher(
|
result.splice(result.begin(), new_lower.get());
|
||||||
parallel_quick_sort(std::move(input)));
|
return result;
|
||||||
result.splice(result.end(),new_higher);
|
|
||||||
result.splice(result.begin(),new_lower.get());
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
std::list<int> list1 = {4, 2, 1, 5, 8, 9, 7};
|
std::list<int> list1 = {4, 2, 1, 5, 8, 9, 7};
|
||||||
std::list<int> sorted1 = sequential_quick_sort(list1);
|
std::list<int> sorted1 = sequential_quick_sort(list1);
|
||||||
std::list<int> sorted2 = parallel_quick_sort(list1);
|
std::list<int> sorted2 = parallel_quick_sort(list1);
|
||||||
std::cout << "sorted1: " << sorted1 << std::endl;
|
std::cout << "sorted1: " << sorted1 << std::endl;
|
||||||
std::cout << "sorted2: " << sorted2 << std::endl;
|
std::cout << "sorted2: " << sorted2 << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
201
ch6/list.cpp
201
ch6/list.cpp
@@ -1,126 +1,115 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <memory>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class list
|
class list {
|
||||||
{
|
struct node {
|
||||||
struct node
|
std::mutex m;
|
||||||
{
|
std::shared_ptr<T> data;
|
||||||
std::mutex m;
|
std::unique_ptr<node> next;
|
||||||
std::shared_ptr<T> data;
|
|
||||||
std::unique_ptr<node> next;
|
|
||||||
|
|
||||||
node()
|
node() : next() {}
|
||||||
: next()
|
|
||||||
{}
|
|
||||||
|
|
||||||
node(T const& value)
|
|
||||||
: data(std::make_shared<T>(value))
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
node head;
|
node(T const& value) : data(std::make_shared<T>(value)) {}
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
node head;
|
||||||
list()
|
|
||||||
{}
|
|
||||||
|
|
||||||
~list()
|
public:
|
||||||
{
|
list() {}
|
||||||
remove_if([](T const&){return true;});
|
|
||||||
|
~list()
|
||||||
|
{
|
||||||
|
remove_if([](T const&) { return true; });
|
||||||
|
}
|
||||||
|
|
||||||
|
list(list const& other) = delete;
|
||||||
|
list& operator=(list const& other) = delete;
|
||||||
|
|
||||||
|
void push_front(T const& value)
|
||||||
|
{
|
||||||
|
std::unique_ptr<node> new_node(new node(value));
|
||||||
|
std::lock_guard<std::mutex> lk(head.m);
|
||||||
|
new_node->next = std::move(head.next);
|
||||||
|
head.next = std::move(new_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Function>
|
||||||
|
void for_each(Function f)
|
||||||
|
{
|
||||||
|
node* current = &head;
|
||||||
|
std::unique_lock<std::mutex> lk(head.m);
|
||||||
|
while (node* const next = current->next.get()) {
|
||||||
|
std::unique_lock<std::mutex> next_lk(next->m);
|
||||||
|
lk.unlock();
|
||||||
|
f(*next->data);
|
||||||
|
current = next;
|
||||||
|
lk = std::move(next_lk);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
list(list const& other) = delete;
|
template <typename Predicate>
|
||||||
list& operator=(list const& other) = delete;
|
std::shared_ptr<T> find_first_of(Predicate p)
|
||||||
|
{
|
||||||
void push_front(T const& value)
|
node* current = &head;
|
||||||
{
|
std::unique_lock<std::mutex> lk(head.m);
|
||||||
std::unique_ptr<node> new_node(new node(value));
|
while (node* const next = current->next.get()) {
|
||||||
std::lock_guard<std::mutex> lk(head.m);
|
std::unique_lock<std::mutex> next_lk(next->m);
|
||||||
new_node->next = std::move(head.next);
|
lk.unlock();
|
||||||
head.next = std::move(new_node);
|
if (p(*next->data)) {
|
||||||
|
return next->data;
|
||||||
|
}
|
||||||
|
current = next;
|
||||||
|
lk = std::move(next_lk);
|
||||||
}
|
}
|
||||||
|
return std::shared_ptr<T>();
|
||||||
|
}
|
||||||
|
|
||||||
template <typename Function>
|
template <typename Predicate>
|
||||||
void for_each(Function f)
|
void remove_if(Predicate p)
|
||||||
{
|
{
|
||||||
node* current = &head;
|
node* current = &head;
|
||||||
std::unique_lock<std::mutex> lk(head.m);
|
std::unique_lock<std::mutex> lk(head.m);
|
||||||
while (node* const next = current->next.get())
|
while (node* const next = current->next.get()) {
|
||||||
{
|
std::unique_lock<std::mutex> next_lk(next->m);
|
||||||
std::unique_lock<std::mutex> next_lk(next->m);
|
if (p(*next->data)) {
|
||||||
lk.unlock();
|
std::unique_ptr<node> old_next = std::move(current->next);
|
||||||
f(*next->data);
|
current->next = std::move(next->next);
|
||||||
current = next;
|
next_lk.unlock();
|
||||||
lk = std::move(next_lk);
|
}
|
||||||
}
|
else {
|
||||||
}
|
lk.unlock();
|
||||||
|
current = next;
|
||||||
template <typename Predicate>
|
lk = std::move(next_lk);
|
||||||
std::shared_ptr<T> find_first_of(Predicate p)
|
}
|
||||||
{
|
|
||||||
node *current = &head;
|
|
||||||
std::unique_lock<std::mutex> lk(head.m);
|
|
||||||
while (node* const next = current->next.get())
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> next_lk(next->m);
|
|
||||||
lk.unlock();
|
|
||||||
if (p(*next->data))
|
|
||||||
{
|
|
||||||
return next->data;
|
|
||||||
}
|
|
||||||
current = next;
|
|
||||||
lk = std::move(next_lk);
|
|
||||||
}
|
|
||||||
return std::shared_ptr<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Predicate>
|
|
||||||
void remove_if(Predicate p)
|
|
||||||
{
|
|
||||||
node *current = &head;
|
|
||||||
std::unique_lock<std::mutex> lk(head.m);
|
|
||||||
while (node* const next = current->next.get())
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> next_lk(next->m);
|
|
||||||
if (p(*next->data))
|
|
||||||
{
|
|
||||||
std::unique_ptr<node> old_next = std::move(current->next);
|
|
||||||
current->next = std::move(next->next);
|
|
||||||
next_lk.unlock();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lk.unlock();
|
|
||||||
current = next;
|
|
||||||
lk = std::move(next_lk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void push(list<int>* l)
|
void
|
||||||
|
push(list<int>* l)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
l->push_front(i);
|
l->push_front(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(int i)
|
void
|
||||||
|
visit(int i)
|
||||||
{
|
{
|
||||||
printf("visit %d\n", i);
|
printf("visit %d\n", i);
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
list<int> l;
|
|
||||||
std::thread t(push,&l);
|
|
||||||
sleep(1);
|
|
||||||
l.for_each(visit);
|
|
||||||
t.join();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
list<int> l;
|
||||||
|
std::thread t(push, &l);
|
||||||
|
sleep(1);
|
||||||
|
l.for_each(visit);
|
||||||
|
t.join();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,133 +1,122 @@
|
|||||||
|
#include <boost/thread/shared_mutex.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <boost/thread/shared_mutex.hpp>
|
|
||||||
#include <memory>
|
|
||||||
#include <list>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
template <typename Key, typename Value, typename Hash=std::hash<Key> >
|
template <typename Key, typename Value, typename Hash = std::hash<Key>>
|
||||||
class lookup_table
|
class lookup_table {
|
||||||
{
|
private:
|
||||||
|
class bucket_type {
|
||||||
private:
|
private:
|
||||||
class bucket_type
|
typedef std::pair<Key, Value> bucket_value;
|
||||||
|
typedef std::list<bucket_value> bucket_data;
|
||||||
|
typedef typename bucket_data::iterator bucket_iterator;
|
||||||
|
|
||||||
|
bucket_data data;
|
||||||
|
mutable boost::shared_mutex mutex;
|
||||||
|
|
||||||
|
bucket_iterator find_entry_for(Key const& key) const
|
||||||
{
|
{
|
||||||
private:
|
return std::find_if(
|
||||||
typedef std::pair<Key, Value> bucket_value;
|
data.begin(), data.end(), [&](bucket_value const& item) {
|
||||||
typedef std::list<bucket_value> bucket_data;
|
return item.first == key;
|
||||||
typedef typename bucket_data::iterator bucket_iterator;
|
});
|
||||||
|
|
||||||
bucket_data data;
|
|
||||||
mutable boost::shared_mutex mutex;
|
|
||||||
|
|
||||||
bucket_iterator find_entry_for(Key const& key) const
|
|
||||||
{
|
|
||||||
return std::find_if(data.begin(), data.end(),
|
|
||||||
[&](bucket_value const& item)
|
|
||||||
{return item.first == key;});
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
Value value_for(Key const& key, Value const& default_value) const
|
|
||||||
{
|
|
||||||
boost::shared_lock<boost::shared_mutex> lock(mutex);
|
|
||||||
bucket_iterator const found_entry = find_entry_for(key);
|
|
||||||
return (found_entry == data.end()) ?
|
|
||||||
default_value : found_entry->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_or_update_mapping(Key const& key, Value const& value)
|
|
||||||
{
|
|
||||||
std::unique_lock<boost::shared_mutex> lock(mutex);
|
|
||||||
bucket_iterator const found_entry = find_entry_for(key);
|
|
||||||
if (found_entry == data.end()) {
|
|
||||||
data.push_back(bucket_value(key,value));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
found_entry->second = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_mapping(Key const& key)
|
|
||||||
{
|
|
||||||
std::unique_lock<boost::shared_mutex> lock(mutex);
|
|
||||||
bucket_iterator const found_entry = find_entry_for(key);
|
|
||||||
if (found_entry != data.end()) {
|
|
||||||
data.erase(found_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<bucket_type> > buckets;
|
|
||||||
Hash hasher;
|
|
||||||
|
|
||||||
bucket_type& get_bucket(Key const& key) const
|
|
||||||
{
|
|
||||||
const std::size_t bucket_index = hasher(key) % buckets.size();
|
|
||||||
return *buckets[bucket_index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef Key key_type;
|
Value value_for(Key const& key, Value const& default_value) const
|
||||||
typedef Value mapped_type;
|
|
||||||
typedef Hash hash_type;
|
|
||||||
|
|
||||||
lookup_table(unsigned num_buckets = 19, const Hash& hasher_ = Hash())
|
|
||||||
: buckets(num_buckets)
|
|
||||||
, hasher(hasher_)
|
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < num_buckets; ++i)
|
boost::shared_lock<boost::shared_mutex> lock(mutex);
|
||||||
{
|
bucket_iterator const found_entry = find_entry_for(key);
|
||||||
buckets[i].reset(new bucket_type);
|
return (found_entry == data.end()) ? default_value : found_entry->second;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lookup_table(const lookup_table& other) = delete;
|
void add_or_update_mapping(Key const& key, Value const& value)
|
||||||
lookup_table& operator=(const lookup_table& other) = delete;
|
|
||||||
|
|
||||||
Value value_for(Key const& key,
|
|
||||||
Value const& default_value = Value()) const
|
|
||||||
{
|
{
|
||||||
return get_bucket(key).value_for(key, default_value);
|
std::unique_lock<boost::shared_mutex> lock(mutex);
|
||||||
|
bucket_iterator const found_entry = find_entry_for(key);
|
||||||
|
if (found_entry == data.end()) {
|
||||||
|
data.push_back(bucket_value(key, value));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
found_entry->second = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_or_update_mapping(const Key& key, const Value& value)
|
void remove_mapping(Key const& key)
|
||||||
{
|
{
|
||||||
get_bucket(key).add_or_update_mapping(key, value);
|
std::unique_lock<boost::shared_mutex> lock(mutex);
|
||||||
|
bucket_iterator const found_entry = find_entry_for(key);
|
||||||
|
if (found_entry != data.end()) {
|
||||||
|
data.erase(found_entry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
void remove_mapping(const Key& key)
|
std::vector<std::unique_ptr<bucket_type>> buckets;
|
||||||
{
|
Hash hasher;
|
||||||
get_bucket(key).remove_mapping(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<Key,Value> get_map() const
|
bucket_type& get_bucket(Key const& key) const
|
||||||
{
|
{
|
||||||
std::vector<boost::unique_lock<boost::shared_mutex> > locks;
|
const std::size_t bucket_index = hasher(key) % buckets.size();
|
||||||
for (unsigned i = 0; i < buckets.size(); ++i)
|
return *buckets[bucket_index];
|
||||||
{
|
}
|
||||||
locks.push_back(boost::unique_lock<boost::shared_mutex>(
|
|
||||||
buckets[i].mutex));
|
public:
|
||||||
}
|
typedef Key key_type;
|
||||||
std::map<Key,Value> res;
|
typedef Value mapped_type;
|
||||||
for (unsigned i = 0; i < buckets.size(); ++i)
|
typedef Hash hash_type;
|
||||||
{
|
|
||||||
for (typename bucket_type::bucket_iterator it = buckets[i].data.begin();
|
lookup_table(unsigned num_buckets = 19, const Hash& hasher_ = Hash())
|
||||||
it != buckets[i].data.end();
|
: buckets(num_buckets), hasher(hasher_)
|
||||||
++it)
|
{
|
||||||
{
|
for (unsigned i = 0; i < num_buckets; ++i) {
|
||||||
res.insert(*it);
|
buckets[i].reset(new bucket_type);
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lookup_table(const lookup_table& other) = delete;
|
||||||
|
lookup_table& operator=(const lookup_table& other) = delete;
|
||||||
|
|
||||||
|
Value value_for(Key const& key, Value const& default_value = Value()) const
|
||||||
|
{
|
||||||
|
return get_bucket(key).value_for(key, default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_or_update_mapping(const Key& key, const Value& value)
|
||||||
|
{
|
||||||
|
get_bucket(key).add_or_update_mapping(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_mapping(const Key& key) { get_bucket(key).remove_mapping(key); }
|
||||||
|
|
||||||
|
std::map<Key, Value> get_map() const
|
||||||
|
{
|
||||||
|
std::vector<boost::unique_lock<boost::shared_mutex>> locks;
|
||||||
|
for (unsigned i = 0; i < buckets.size(); ++i) {
|
||||||
|
locks.push_back(
|
||||||
|
boost::unique_lock<boost::shared_mutex>(buckets[i].mutex));
|
||||||
|
}
|
||||||
|
std::map<Key, Value> res;
|
||||||
|
for (unsigned i = 0; i < buckets.size(); ++i) {
|
||||||
|
for (typename bucket_type::bucket_iterator it = buckets[i].data.begin();
|
||||||
|
it != buckets[i].data.end();
|
||||||
|
++it) {
|
||||||
|
res.insert(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
lookup_table<int,std::string> t;
|
lookup_table<int, std::string> t;
|
||||||
t.add_or_update_mapping(1,"hello");
|
t.add_or_update_mapping(1, "hello");
|
||||||
std::cout << "value for 1 is " << t.value_for(1) << std::endl;
|
std::cout << "value for 1 is " << t.value_for(1) << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
183
ch6/queue.cpp
183
ch6/queue.cpp
@@ -1,112 +1,113 @@
|
|||||||
#include <iostream>
|
#include <condition_variable>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <queue>
|
#include <iostream>
|
||||||
#include <thread>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <condition_variable>
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class queue
|
class queue {
|
||||||
{
|
private:
|
||||||
private:
|
mutable std::mutex mut;
|
||||||
mutable std::mutex mut;
|
std::queue<std::shared_ptr<T>> data_queue;
|
||||||
std::queue<std::shared_ptr<T> > data_queue;
|
std::condition_variable data_cond;
|
||||||
std::condition_variable data_cond;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
queue(){}
|
queue() {}
|
||||||
|
|
||||||
queue(const queue& other)
|
queue(const queue& other)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(other.mut);
|
std::lock_guard<std::mutex> lk(other.mut);
|
||||||
data_queue = other.data_queue;
|
data_queue = other.data_queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(T new_value)
|
||||||
|
{
|
||||||
|
std::shared_ptr<T> data(new T(new_value));
|
||||||
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
|
data_queue.push(data);
|
||||||
|
data_cond.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_and_pop(T& value)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
|
data_cond.wait(lk, [&] { return !data_queue.empty(); });
|
||||||
|
/*
|
||||||
|
while (data_queue.empty()) {
|
||||||
|
data_cond.wait(lk);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
value = *data_queue.front();
|
||||||
|
data_queue.pop();
|
||||||
|
}
|
||||||
|
|
||||||
void push(T new_value)
|
std::shared_ptr<T> wait_and_pop()
|
||||||
{
|
{
|
||||||
std::shared_ptr<T> data(new T(new_value));
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
data_cond.wait(lk, [&] { return !data_queue.empty(); });
|
||||||
data_queue.push(data);
|
/*
|
||||||
data_cond.notify_one();
|
while (data_queue.empty()) {
|
||||||
|
data_cond.wait(lk);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
std::shared_ptr<T> res = data_queue.front();
|
||||||
|
data_queue.pop();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void wait_and_pop(T& value)
|
bool try_pop(T& value)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
data_cond.wait(lk, [&]{return !data_queue.empty();});
|
if (data_queue.empty())
|
||||||
/*
|
return false;
|
||||||
while (data_queue.empty()) {
|
value = *data_queue.front();
|
||||||
data_cond.wait(lk);
|
data_queue.pop();
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
value = *data_queue.front();
|
|
||||||
data_queue.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<T> wait_and_pop()
|
std::shared_ptr<T> try_pop()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
data_cond.wait(lk, [&]{return !data_queue.empty();});
|
if (data_queue.empty())
|
||||||
/*
|
return std::shared_ptr<T>();
|
||||||
while (data_queue.empty()) {
|
std::shared_ptr<T> res = data_queue.front();
|
||||||
data_cond.wait(lk);
|
data_queue.pop();
|
||||||
}
|
return res;
|
||||||
*/
|
}
|
||||||
std::shared_ptr<T> res = data_queue.front();
|
|
||||||
data_queue.pop();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool try_pop(T& value)
|
bool empty() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
if (data_queue.empty())
|
return data_queue.empty();
|
||||||
return false;
|
}
|
||||||
value = *data_queue.front();
|
|
||||||
data_queue.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
|
||||||
if (data_queue.empty())
|
|
||||||
return std::shared_ptr<T>();
|
|
||||||
std::shared_ptr<T> res = data_queue.front();
|
|
||||||
data_queue.pop();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
|
||||||
return data_queue.empty();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void push(queue<int>* q)
|
void
|
||||||
|
push(queue<int>* q)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
//std::cout << "pushing " << i << std::endl;
|
// std::cout << "pushing " << i << std::endl;
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
q->push(i);
|
q->push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(queue<int>* q)
|
void
|
||||||
|
pop(queue<int>* q)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("poping %d\n", *q->wait_and_pop());
|
printf("poping %d\n", *q->wait_and_pop());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
queue<int> q;
|
|
||||||
std::thread t1(push,&q);
|
|
||||||
std::thread t2(pop,&q);
|
|
||||||
t1.join();
|
|
||||||
t2.join();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
queue<int> q;
|
||||||
|
std::thread t1(push, &q);
|
||||||
|
std::thread t2(pop, &q);
|
||||||
|
t1.join();
|
||||||
|
t2.join();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,94 +1,93 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <queue>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class queue
|
class queue {
|
||||||
{
|
private:
|
||||||
private:
|
struct node {
|
||||||
struct node
|
std::shared_ptr<T> data;
|
||||||
{
|
std::unique_ptr<node> next;
|
||||||
std::shared_ptr<T> data;
|
};
|
||||||
std::unique_ptr<node> next;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::mutex head_mutex;
|
std::mutex head_mutex;
|
||||||
std::unique_ptr<node> head;
|
std::unique_ptr<node> head;
|
||||||
std::mutex tail_mutex;
|
std::mutex tail_mutex;
|
||||||
node *tail;
|
node* tail;
|
||||||
|
|
||||||
node *get_tail()
|
node* get_tail()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||||
return tail;
|
return tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<node> pop_head()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||||
|
if (head.get() == get_tail()) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
std::unique_ptr<node> old_head = std::move(head);
|
||||||
|
head = std::move(old_head->next);
|
||||||
|
return old_head;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<node> pop_head()
|
public:
|
||||||
{
|
queue() : head(new node), tail(head.get()) {}
|
||||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
|
||||||
if (head.get() == get_tail()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
std::unique_ptr<node> old_head = std::move(head);
|
|
||||||
head = std::move(old_head->next);
|
|
||||||
return old_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
queue(const queue& other) = delete;
|
||||||
queue():head(new node),tail(head.get())
|
queue& operator=(const queue& other) = delete;
|
||||||
{}
|
|
||||||
|
|
||||||
queue(const queue& other) = delete;
|
std::shared_ptr<T> try_pop()
|
||||||
queue& operator=(const queue& other) = delete;
|
{
|
||||||
|
std::unique_ptr<node> old_head = pop_head();
|
||||||
|
return old_head ? old_head->data : std::shared_ptr<T>();
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop()
|
void push(T new_value)
|
||||||
{
|
{
|
||||||
std::unique_ptr<node> old_head = pop_head();
|
std::shared_ptr<T> new_data(std::make_shared<T>(std::move(new_value)));
|
||||||
return old_head ? old_head->data : std::shared_ptr<T>();
|
std::unique_ptr<node> p(new node);
|
||||||
}
|
node* const new_tail = p.get();
|
||||||
|
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||||
void push(T new_value)
|
tail->data = new_data;
|
||||||
{
|
tail->next = std::move(p);
|
||||||
std::shared_ptr<T> new_data(
|
tail = new_tail;
|
||||||
std::make_shared<T>(std::move(new_value)));
|
}
|
||||||
std::unique_ptr<node> p(new node);
|
|
||||||
node *const new_tail = p.get();
|
|
||||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
|
||||||
tail->data = new_data;
|
|
||||||
tail->next = std::move(p);
|
|
||||||
tail = new_tail;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void push(queue<int>* q)
|
void
|
||||||
|
push(queue<int>* q)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
q->push(i);
|
q->push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pop(queue<int>* q)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
while (true) {
|
||||||
|
if (std::shared_ptr<int> p = q->try_pop()) {
|
||||||
|
printf("poping %d\n", *p);
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
if (i == 10)
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(queue<int>* q)
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
int i = 0;
|
queue<int> q;
|
||||||
while (true) {
|
std::thread th1(push, &q);
|
||||||
if (std::shared_ptr<int> p = q->try_pop()) {
|
std::thread th2(pop, &q);
|
||||||
printf("poping %d\n", *p);
|
th1.join();
|
||||||
++i;
|
th2.join();
|
||||||
}
|
return 0;
|
||||||
if (i == 10) break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
queue<int> q;
|
|
||||||
std::thread th1(push,&q);
|
|
||||||
std::thread th2(pop,&q);
|
|
||||||
th1.join();
|
|
||||||
th2.join();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,154 +1,151 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <queue>
|
|
||||||
#include <memory>
|
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class queue
|
class queue {
|
||||||
{
|
private:
|
||||||
private:
|
struct node {
|
||||||
struct node
|
std::shared_ptr<T> data;
|
||||||
{
|
std::unique_ptr<node> next;
|
||||||
std::shared_ptr<T> data;
|
};
|
||||||
std::unique_ptr<node> next;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::mutex head_mutex;
|
std::mutex head_mutex;
|
||||||
std::unique_ptr<node> head;
|
std::unique_ptr<node> head;
|
||||||
std::mutex tail_mutex;
|
std::mutex tail_mutex;
|
||||||
node *tail;
|
node* tail;
|
||||||
std::condition_variable data_cond;
|
std::condition_variable data_cond;
|
||||||
|
|
||||||
node *get_tail()
|
node* get_tail()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||||
return tail;
|
return tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<node> pop_head()
|
||||||
|
{
|
||||||
|
std::unique_ptr<node> old_head = std::move(head);
|
||||||
|
head = std::move(old_head->next);
|
||||||
|
return old_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<node> try_pop_head()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||||
|
if (head.get() == get_tail()) {
|
||||||
|
return std::unique_ptr<node>();
|
||||||
}
|
}
|
||||||
|
return pop_head();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<node> pop_head()
|
std::unique_ptr<node> try_pop_head(T& value)
|
||||||
{
|
{
|
||||||
std::unique_ptr<node> old_head = std::move(head);
|
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||||
head = std::move(old_head->next);
|
if (head.get() == get_tail()) {
|
||||||
return old_head;
|
return std::unique_ptr<node>();
|
||||||
}
|
}
|
||||||
|
value = std::move(*head->data);
|
||||||
|
return pop_head();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<node> try_pop_head()
|
std::unique_lock<std::mutex> wait_for_data()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> head_lock(head_mutex);
|
||||||
|
data_cond.wait(head_lock, [&] { return head.get() != queue::get_tail(); });
|
||||||
|
return std::move(head_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<node> wait_pop_head()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> head_lock(wait_for_data());
|
||||||
|
return pop_head();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<node> wait_pop_head(T& value)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> head_lock(wait_for_data());
|
||||||
|
value = std::move(*head->data);
|
||||||
|
return pop_head();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
queue() : head(new node), tail(head.get()) {}
|
||||||
|
|
||||||
|
queue(const queue& other) = delete;
|
||||||
|
queue& operator=(const queue& other) = delete;
|
||||||
|
|
||||||
|
std::shared_ptr<T> try_pop()
|
||||||
|
{
|
||||||
|
std::unique_ptr<node> old_head = try_pop_head();
|
||||||
|
return old_head ? old_head->data : std::shared_ptr<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_pop(T& value)
|
||||||
|
{
|
||||||
|
std::unique_ptr<node> const old_head = try_pop_head(value);
|
||||||
|
return old_head;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<T> wait_and_pop()
|
||||||
|
{
|
||||||
|
std::unique_ptr<node> const old_head = wait_pop_head();
|
||||||
|
return old_head->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait_and_pop(T& value)
|
||||||
|
{
|
||||||
|
std::unique_ptr<node> const old_head = wait_pop_value(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(T new_value)
|
||||||
|
{
|
||||||
|
std::shared_ptr<T> new_data(std::make_shared<T>(std::move(new_value)));
|
||||||
|
std::unique_ptr<node> p(new node);
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||||
if (head.get() == get_tail()) {
|
tail->data = new_data;
|
||||||
return std::unique_ptr<node>();
|
node* const new_tail = p.get();
|
||||||
}
|
tail->next = std::move(p);
|
||||||
return pop_head();
|
tail = new_tail;
|
||||||
}
|
}
|
||||||
|
data_cond.notify_one();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<node> try_pop_head(T& value)
|
void empty()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||||
if (head.get() == get_tail()) {
|
return (head.get(0 == get_tail()));
|
||||||
return std::unique_ptr<node>();
|
}
|
||||||
}
|
|
||||||
value = std::move(*head->data);
|
|
||||||
return pop_head();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_lock<std::mutex> wait_for_data()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> head_lock(head_mutex);
|
|
||||||
data_cond.wait(head_lock, [&]{return head.get() != queue::get_tail();});
|
|
||||||
return std::move(head_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<node> wait_pop_head()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> head_lock(wait_for_data());
|
|
||||||
return pop_head();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<node> wait_pop_head(T& value)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> head_lock(wait_for_data());
|
|
||||||
value = std::move(*head->data);
|
|
||||||
return pop_head();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
queue():head(new node),tail(head.get())
|
|
||||||
{}
|
|
||||||
|
|
||||||
queue(const queue& other) = delete;
|
|
||||||
queue& operator=(const queue& other) = delete;
|
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop()
|
|
||||||
{
|
|
||||||
std::unique_ptr<node> old_head = try_pop_head();
|
|
||||||
return old_head ? old_head->data : std::shared_ptr<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool try_pop(T& value)
|
|
||||||
{
|
|
||||||
std::unique_ptr<node> const old_head = try_pop_head(value);
|
|
||||||
return old_head;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<T> wait_and_pop()
|
|
||||||
{
|
|
||||||
std::unique_ptr<node> const old_head = wait_pop_head();
|
|
||||||
return old_head->data;
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_and_pop(T& value)
|
|
||||||
{
|
|
||||||
std::unique_ptr<node> const old_head = wait_pop_value(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void push(T new_value)
|
|
||||||
{
|
|
||||||
std::shared_ptr<T> new_data(
|
|
||||||
std::make_shared<T>(std::move(new_value)));
|
|
||||||
std::unique_ptr<node> p(new node);
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
|
||||||
tail->data = new_data;
|
|
||||||
node *const new_tail = p.get();
|
|
||||||
tail->next = std::move(p);
|
|
||||||
tail = new_tail;
|
|
||||||
}
|
|
||||||
data_cond.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
void empty()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
|
||||||
return (head.get(0 == get_tail()));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void
|
||||||
void push(queue<int>* q)
|
push(queue<int>* q)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
q->push(i);
|
q->push(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(queue<int>* q)
|
void
|
||||||
|
pop(queue<int>* q)
|
||||||
{
|
{
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("poping %d\n", *q->wait_and_pop());
|
printf("poping %d\n", *q->wait_and_pop());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
queue<int> q;
|
queue<int> q;
|
||||||
std::thread th1(push,&q);
|
std::thread th1(push, &q);
|
||||||
std::thread th2(pop,&q);
|
std::thread th2(pop, &q);
|
||||||
th1.join();
|
th1.join();
|
||||||
th2.join();
|
th2.join();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +1,63 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stack>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <stack>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class stack
|
class stack {
|
||||||
{
|
private:
|
||||||
private:
|
std::stack<T> data;
|
||||||
std::stack<T> data;
|
mutable std::mutex m;
|
||||||
mutable std::mutex m;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
stack(){}
|
stack() {}
|
||||||
|
|
||||||
stack(const stack& other)
|
stack(const stack& other)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(other.m);
|
std::lock_guard<std::mutex> lock(other.m);
|
||||||
data = other.data;
|
data = other.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
stack& operator=(const stack&) = delete;
|
stack& operator=(const stack&) = delete;
|
||||||
|
|
||||||
void push(T new_value)
|
void push(T new_value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m);
|
std::lock_guard<std::mutex> lock(m);
|
||||||
data.push(new_value);
|
data.push(new_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<T> pop()
|
std::shared_ptr<T> pop()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m);
|
std::lock_guard<std::mutex> lock(m);
|
||||||
if (data.empty()) throw std::underflow_error("empty stack");
|
if (data.empty())
|
||||||
std::shared_ptr<T> const res(new T(data.top()));
|
throw std::underflow_error("empty stack");
|
||||||
data.pop();
|
std::shared_ptr<T> const res(new T(data.top()));
|
||||||
return res;
|
data.pop();
|
||||||
}
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
void pop(T& value)
|
void pop(T& value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m);
|
std::lock_guard<std::mutex> lock(m);
|
||||||
if (data.empty()) throw std::underflow_error("empty stack");
|
if (data.empty())
|
||||||
value = data.top();
|
throw std::underflow_error("empty stack");
|
||||||
data.pop();
|
value = data.top();
|
||||||
}
|
data.pop();
|
||||||
|
}
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m);
|
std::lock_guard<std::mutex> lock(m);
|
||||||
return data.empty();
|
return data.empty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
stack<int> s;
|
stack<int> s;
|
||||||
s.push(1);
|
s.push(1);
|
||||||
std::cout << *s.pop() << std::endl;
|
std::cout << *s.pop() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,63 +1,53 @@
|
|||||||
|
#include <cstdio>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
unsigned const max_hazard_pointers = 100;
|
unsigned const max_hazard_pointers = 100;
|
||||||
struct hazard_pointer
|
struct hazard_pointer {
|
||||||
{
|
std::atomic<std::thread::id> id;
|
||||||
std::atomic<std::thread::id> id;
|
std::atomic<void*> pointer;
|
||||||
std::atomic<void*> pointer;
|
|
||||||
};
|
};
|
||||||
hazard_pointer hazard_pointers[max_hazard_pointers];
|
hazard_pointer hazard_pointers[max_hazard_pointers];
|
||||||
|
|
||||||
class hp_owner
|
class hp_owner {
|
||||||
{
|
hazard_pointer* hp;
|
||||||
hazard_pointer* hp;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
hp_owner(hp_owner const&)=delete;
|
hp_owner(hp_owner const&) = delete;
|
||||||
hp_owner operator=(hp_owner const&)=delete;
|
hp_owner operator=(hp_owner const&) = delete;
|
||||||
hp_owner()
|
hp_owner() : hp(nullptr)
|
||||||
: hp(nullptr)
|
{
|
||||||
{
|
for (unsigned i = 0; i < max_hazard_pointers; ++i) {
|
||||||
for (unsigned i = 0; i < max_hazard_pointers; ++i) {
|
std::thread::id old_id;
|
||||||
std::thread::id old_id;
|
// try to claim ownership of a hazard pointer
|
||||||
// try to claim ownership of a hazard pointer
|
if (hazard_pointers[i].id.compare_exchange_strong(
|
||||||
if (hazard_pointers[i].id.compare_exchange_strong(
|
old_id, std::this_thread::get_id())) {
|
||||||
old_id, std::this_thread::get_id())) {
|
// successfully claimed the entry for the current thread,
|
||||||
// successfully claimed the entry for the current thread,
|
// store it and stop the search
|
||||||
// store it and stop the search
|
hp = &hazard_pointers[i];
|
||||||
hp = &hazard_pointers[i];
|
printf("hp: %ld\n", hp);
|
||||||
printf("hp: %ld\n", hp);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
// another threads owns this entry, move on to the next
|
||||||
// another threads owns this entry, move on to the next
|
|
||||||
}
|
|
||||||
// if you get to the end of the list without finding a free entry,
|
|
||||||
// there are too many threads using hazard pointers, so throw an
|
|
||||||
// exception
|
|
||||||
if (!hp) {
|
|
||||||
throw std::runtime_error("No hazard pointers available");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// if you get to the end of the list without finding a free entry,
|
||||||
|
// there are too many threads using hazard pointers, so throw an
|
||||||
|
// exception
|
||||||
|
if (!hp) {
|
||||||
|
throw std::runtime_error("No hazard pointers available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::atomic<void*>& get_pointer()
|
std::atomic<void*>& get_pointer() { return hp->pointer; }
|
||||||
{
|
|
||||||
return hp->pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
~hp_owner()
|
~hp_owner()
|
||||||
{
|
{
|
||||||
// when each thread exits, if an instance of hp_owner was created
|
// when each thread exits, if an instance of hp_owner was created
|
||||||
// for the thread, then it's destryoed. The destructor then resets
|
// for the thread, then it's destryoed. The destructor then resets
|
||||||
// the actual pointer to nullptr before setting the owner ID to
|
// the actual pointer to nullptr before setting the owner ID to
|
||||||
// std::thread::id(), allowing another thread to reuse the entry later.
|
// std::thread::id(), allowing another thread to reuse the entry later.
|
||||||
// TODO: this causes crash
|
// TODO: this causes crash
|
||||||
//hp->pointer.store(nullptr);
|
// hp->pointer.store(nullptr);
|
||||||
//hp->id.store(std::thread::id());
|
// hp->id.store(std::thread::id());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
@@ -9,232 +9,210 @@
|
|||||||
// mpmc queue
|
// mpmc queue
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class queue
|
class queue {
|
||||||
{
|
private:
|
||||||
private:
|
struct node;
|
||||||
struct node;
|
struct counted_node_ptr {
|
||||||
struct counted_node_ptr
|
int external_count;
|
||||||
|
node* ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::atomic<counted_node_ptr> head;
|
||||||
|
std::atomic<counted_node_ptr> tail;
|
||||||
|
|
||||||
|
struct node_counter {
|
||||||
|
unsigned internal_count : 30;
|
||||||
|
unsigned external_counters : 2;
|
||||||
|
// you need only 2 bits because there are at most two such
|
||||||
|
// counters (next and tail)
|
||||||
|
};
|
||||||
|
|
||||||
|
struct node {
|
||||||
|
std::atomic<T*> data;
|
||||||
|
std::atomic<node_counter> count;
|
||||||
|
std::atomic<counted_node_ptr> next;
|
||||||
|
|
||||||
|
node()
|
||||||
{
|
{
|
||||||
int external_count;
|
node_counter new_count;
|
||||||
node* ptr;
|
new_count.internal_count = 0;
|
||||||
};
|
new_count.external_counters = 2;
|
||||||
|
// because every node starts out referenced from tail and
|
||||||
|
// from the next pointer of the previous node once you've
|
||||||
|
// actually aadded it to the queue
|
||||||
|
count.store(new_count);
|
||||||
|
|
||||||
std::atomic<counted_node_ptr> head;
|
counted_node_ptr new_next;
|
||||||
std::atomic<counted_node_ptr> tail;
|
new_next.ptr = nullptr;
|
||||||
|
new_next.external_count = 0;
|
||||||
struct node_counter
|
next.store(new_next);
|
||||||
{
|
|
||||||
unsigned internal_count:30;
|
|
||||||
unsigned external_counters:2;
|
|
||||||
// you need only 2 bits because there are at most two such
|
|
||||||
// counters (next and tail)
|
|
||||||
};
|
|
||||||
|
|
||||||
struct node
|
|
||||||
{
|
|
||||||
std::atomic<T*> data;
|
|
||||||
std::atomic<node_counter> count;
|
|
||||||
std::atomic<counted_node_ptr> next;
|
|
||||||
|
|
||||||
node()
|
|
||||||
{
|
|
||||||
node_counter new_count;
|
|
||||||
new_count.internal_count = 0;
|
|
||||||
new_count.external_counters = 2;
|
|
||||||
// because every node starts out referenced from tail and
|
|
||||||
// from the next pointer of the previous node once you've
|
|
||||||
// actually aadded it to the queue
|
|
||||||
count.store(new_count);
|
|
||||||
|
|
||||||
counted_node_ptr new_next;
|
|
||||||
new_next.ptr = nullptr;
|
|
||||||
new_next.external_count = 0;
|
|
||||||
next.store(new_next);
|
|
||||||
}
|
|
||||||
|
|
||||||
void release_ref()
|
|
||||||
{
|
|
||||||
node_counter old_counter=
|
|
||||||
count.load(std::memory_order_relaxed);
|
|
||||||
node_counter new_counter;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
new_counter=old_counter;
|
|
||||||
--new_counter.internal_count;
|
|
||||||
}
|
|
||||||
while(!count.compare_exchange_strong(
|
|
||||||
old_counter,new_counter,
|
|
||||||
std::memory_order_acquire,std::memory_order_relaxed));
|
|
||||||
if(!new_counter.internal_count &&
|
|
||||||
!new_counter.external_counters)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
void set_new_tail(counted_node_ptr &old_tail,
|
|
||||||
counted_node_ptr const &new_tail)
|
|
||||||
{
|
|
||||||
node* const current_tail_ptr = old_tail.ptr;
|
|
||||||
while(!tail.compare_exchange_weak(old_tail,new_tail) &&
|
|
||||||
old_tail.ptr == current_tail_ptr);
|
|
||||||
if(old_tail.ptr == current_tail_ptr)
|
|
||||||
free_external_counter(old_tail);
|
|
||||||
else
|
|
||||||
current_tail_ptr->release_ref();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void increase_external_count(
|
void release_ref()
|
||||||
std::atomic<counted_node_ptr>& counter,
|
|
||||||
counted_node_ptr& old_counter)
|
|
||||||
{
|
{
|
||||||
counted_node_ptr new_counter;
|
node_counter old_counter = count.load(std::memory_order_relaxed);
|
||||||
do
|
node_counter new_counter;
|
||||||
{
|
do {
|
||||||
new_counter = old_counter;
|
new_counter = old_counter;
|
||||||
++new_counter.external_count;
|
--new_counter.internal_count;
|
||||||
}
|
} while (!count.compare_exchange_strong(old_counter,
|
||||||
while(!counter.compare_exchange_strong(
|
new_counter,
|
||||||
old_counter,new_counter,
|
std::memory_order_acquire,
|
||||||
std::memory_order_acquire,std::memory_order_relaxed));
|
std::memory_order_relaxed));
|
||||||
old_counter.external_count = new_counter.external_count;
|
if (!new_counter.internal_count && !new_counter.external_counters) {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
static void free_external_counter(counted_node_ptr &old_node_ptr)
|
void set_new_tail(counted_node_ptr& old_tail,
|
||||||
{
|
counted_node_ptr const& new_tail)
|
||||||
node* const ptr = old_node_ptr.ptr;
|
{
|
||||||
int const count_increase = old_node_ptr.external_count-2;
|
node* const current_tail_ptr = old_tail.ptr;
|
||||||
node_counter old_counter =
|
while (!tail.compare_exchange_weak(old_tail, new_tail) &&
|
||||||
ptr->count.load(std::memory_order_relaxed);
|
old_tail.ptr == current_tail_ptr)
|
||||||
node_counter new_counter;
|
;
|
||||||
do
|
if (old_tail.ptr == current_tail_ptr)
|
||||||
{
|
free_external_counter(old_tail);
|
||||||
new_counter=old_counter;
|
else
|
||||||
--new_counter.external_counters;
|
current_tail_ptr->release_ref();
|
||||||
new_counter.internal_count+=count_increase;
|
}
|
||||||
}
|
|
||||||
while(!ptr->count.compare_exchange_strong(
|
static void increase_external_count(std::atomic<counted_node_ptr>& counter,
|
||||||
old_counter,new_counter,
|
counted_node_ptr& old_counter)
|
||||||
std::memory_order_acquire,std::memory_order_relaxed));
|
{
|
||||||
if(!new_counter.internal_count &&
|
counted_node_ptr new_counter;
|
||||||
!new_counter.external_counters)
|
do {
|
||||||
{
|
new_counter = old_counter;
|
||||||
delete ptr;
|
++new_counter.external_count;
|
||||||
}
|
} while (!counter.compare_exchange_strong(old_counter,
|
||||||
|
new_counter,
|
||||||
|
std::memory_order_acquire,
|
||||||
|
std::memory_order_relaxed));
|
||||||
|
old_counter.external_count = new_counter.external_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_external_counter(counted_node_ptr& old_node_ptr)
|
||||||
|
{
|
||||||
|
node* const ptr = old_node_ptr.ptr;
|
||||||
|
int const count_increase = old_node_ptr.external_count - 2;
|
||||||
|
node_counter old_counter = ptr->count.load(std::memory_order_relaxed);
|
||||||
|
node_counter new_counter;
|
||||||
|
do {
|
||||||
|
new_counter = old_counter;
|
||||||
|
--new_counter.external_counters;
|
||||||
|
new_counter.internal_count += count_increase;
|
||||||
|
} while (!ptr->count.compare_exchange_strong(old_counter,
|
||||||
|
new_counter,
|
||||||
|
std::memory_order_acquire,
|
||||||
|
std::memory_order_relaxed));
|
||||||
|
if (!new_counter.internal_count && !new_counter.external_counters) {
|
||||||
|
delete ptr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
queue()
|
queue() : head(), tail() {}
|
||||||
: head()
|
|
||||||
, tail()
|
|
||||||
{}
|
|
||||||
|
|
||||||
queue(const queue& other)=delete;
|
queue(const queue& other) = delete;
|
||||||
queue& operator=(const queue& other)=delete;
|
queue& operator=(const queue& other) = delete;
|
||||||
|
|
||||||
~queue()
|
~queue()
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
while (node* const old_head = head.load()) {
|
while (node* const old_head = head.load()) {
|
||||||
head.store(old_head->next);
|
head.store(old_head->next);
|
||||||
delete old_head;
|
delete old_head;
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
void push(T new_value)
|
void push(T new_value)
|
||||||
{
|
{
|
||||||
std::unique_ptr<T> new_data(new T(new_value));
|
std::unique_ptr<T> new_data(new T(new_value));
|
||||||
counted_node_ptr new_next;
|
counted_node_ptr new_next;
|
||||||
new_next.ptr = new node;
|
new_next.ptr = new node;
|
||||||
new_next.external_count=1;
|
new_next.external_count = 1;
|
||||||
counted_node_ptr old_tail=tail.load();
|
counted_node_ptr old_tail = tail.load();
|
||||||
for(;;)
|
for (;;) {
|
||||||
{
|
increase_external_count(tail, old_tail);
|
||||||
increase_external_count(tail,old_tail);
|
T* old_data = nullptr;
|
||||||
T* old_data=nullptr;
|
if (old_tail.ptr->data.compare_exchange_strong(old_data,
|
||||||
if(old_tail.ptr->data.compare_exchange_strong(
|
new_data.get())) {
|
||||||
old_data,new_data.get()))
|
counted_node_ptr old_next = {0};
|
||||||
{
|
if (!old_tail.ptr->next.compare_exchange_strong(old_next, new_next)) {
|
||||||
counted_node_ptr old_next={0};
|
delete new_next.ptr;
|
||||||
if(!old_tail.ptr->next.compare_exchange_strong(
|
new_next = old_next;
|
||||||
old_next,new_next))
|
|
||||||
{
|
|
||||||
delete new_next.ptr;
|
|
||||||
new_next=old_next;
|
|
||||||
}
|
|
||||||
set_new_tail(old_tail, new_next);
|
|
||||||
new_data.release();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
counted_node_ptr old_next={0};
|
|
||||||
if(old_tail.ptr->next.compare_exchange_strong(
|
|
||||||
old_next,new_next))
|
|
||||||
{
|
|
||||||
old_next=new_next;
|
|
||||||
new_next.ptr=new node;
|
|
||||||
}
|
|
||||||
set_new_tail(old_tail, old_next);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
set_new_tail(old_tail, new_next);
|
||||||
|
new_data.release();
|
||||||
std::unique_ptr<T> pop() {
|
break;
|
||||||
counted_node_ptr old_head = head.load(std::memory_order_relaxed);
|
}
|
||||||
|
else {
|
||||||
for(;;) {
|
counted_node_ptr old_next = {0};
|
||||||
increase_external_count(head, old_head);
|
if (old_tail.ptr->next.compare_exchange_strong(old_next, new_next)) {
|
||||||
|
old_next = new_next;
|
||||||
node* const ptr = old_head.ptr;
|
new_next.ptr = new node;
|
||||||
if (ptr==tail.load().ptr) {
|
|
||||||
ptr->release_ref();
|
|
||||||
return std::unique_ptr<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
counted_node_ptr next = ptr->next.load();
|
|
||||||
|
|
||||||
if (head.compare_exchange_strong(old_head, next)) {
|
|
||||||
T* const res=ptr->data.exchange(nullptr);
|
|
||||||
free_external_counter(old_head);
|
|
||||||
return std::unique_ptr<T>(res);
|
|
||||||
}
|
|
||||||
ptr->release_ref();
|
|
||||||
}
|
}
|
||||||
|
set_new_tail(old_tail, old_next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<T> pop()
|
||||||
|
{
|
||||||
|
counted_node_ptr old_head = head.load(std::memory_order_relaxed);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
increase_external_count(head, old_head);
|
||||||
|
|
||||||
|
node* const ptr = old_head.ptr;
|
||||||
|
if (ptr == tail.load().ptr) {
|
||||||
|
ptr->release_ref();
|
||||||
|
return std::unique_ptr<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
counted_node_ptr next = ptr->next.load();
|
||||||
|
|
||||||
|
if (head.compare_exchange_strong(old_head, next)) {
|
||||||
|
T* const res = ptr->data.exchange(nullptr);
|
||||||
|
free_external_counter(old_head);
|
||||||
|
return std::unique_ptr<T>(res);
|
||||||
|
}
|
||||||
|
ptr->release_ref();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void push(queue<int>* q)
|
void
|
||||||
|
push(queue<int>* q)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
q->push(i);
|
q->push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pop(queue<int>* q)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
while (i < 10) {
|
||||||
|
std::shared_ptr<int> p = q->pop();
|
||||||
|
if (p) {
|
||||||
|
printf("poping %d\n", *p);
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(queue<int>* q)
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
int i = 0;
|
queue<int> q;
|
||||||
while (i < 10) {
|
std::thread t1(push, &q);
|
||||||
std::shared_ptr<int> p = q->pop();
|
std::thread t2(pop, &q);
|
||||||
if (p) {
|
t1.join();
|
||||||
printf("poping %d\n", *p);
|
t2.join();
|
||||||
++i;
|
return 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
queue<int> q;
|
|
||||||
std::thread t1(push, &q);
|
|
||||||
std::thread t2(pop, &q);
|
|
||||||
t1.join();
|
|
||||||
t2.join();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,102 +1,98 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class queue
|
class queue {
|
||||||
{
|
private:
|
||||||
private:
|
struct node {
|
||||||
struct node
|
std::shared_ptr<T> data;
|
||||||
{
|
node* next;
|
||||||
std::shared_ptr<T> data;
|
|
||||||
node* next;
|
|
||||||
|
|
||||||
node()
|
node() : next(nullptr) {}
|
||||||
: next(nullptr)
|
};
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::atomic<node*> head;
|
std::atomic<node*> head;
|
||||||
std::atomic<node*> tail;
|
std::atomic<node*> tail;
|
||||||
|
|
||||||
node* pop_head()
|
node* pop_head()
|
||||||
{
|
{
|
||||||
node* const old_head = head.load();
|
node* const old_head = head.load();
|
||||||
if (old_head == tail.load()) {
|
if (old_head == tail.load()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
|
||||||
head.store(old_head->next);
|
|
||||||
return old_head;
|
|
||||||
}
|
}
|
||||||
public:
|
head.store(old_head->next);
|
||||||
queue()
|
return old_head;
|
||||||
: head(new node)
|
}
|
||||||
, tail(head.load())
|
|
||||||
{}
|
|
||||||
|
|
||||||
queue(const queue& other)=delete;
|
public:
|
||||||
queue& operator=(const queue& other)=delete;
|
queue() : head(new node), tail(head.load()) {}
|
||||||
|
|
||||||
~queue()
|
queue(const queue& other) = delete;
|
||||||
{
|
queue& operator=(const queue& other) = delete;
|
||||||
while (node* const old_head = head.load()) {
|
|
||||||
head.store(old_head->next);
|
~queue()
|
||||||
delete old_head;
|
{
|
||||||
}
|
while (node* const old_head = head.load()) {
|
||||||
|
head.store(old_head->next);
|
||||||
|
delete old_head;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<T> pop()
|
||||||
|
{
|
||||||
|
node* old_head = pop_head();
|
||||||
|
if (!old_head) {
|
||||||
|
return std::shared_ptr<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<T> pop()
|
std::shared_ptr<T> const res(old_head->data);
|
||||||
{
|
delete old_head;
|
||||||
node* old_head = pop_head();
|
return res;
|
||||||
if (!old_head) {
|
}
|
||||||
return std::shared_ptr<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<T> const res(old_head->data);
|
void push(T new_value)
|
||||||
delete old_head;
|
{
|
||||||
return res;
|
std::shared_ptr<T> new_data(std::make_shared<T>(new_value));
|
||||||
}
|
node* p = new node;
|
||||||
|
node* const old_tail = tail.load();
|
||||||
void push(T new_value)
|
old_tail->data.swap(new_data);
|
||||||
{
|
old_tail->next = p;
|
||||||
std::shared_ptr<T> new_data(std::make_shared<T>(new_value));
|
tail.store(p);
|
||||||
node* p = new node;
|
}
|
||||||
node* const old_tail = tail.load();
|
|
||||||
old_tail->data.swap(new_data);
|
|
||||||
old_tail->next = p;
|
|
||||||
tail.store(p);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void push(queue<int>* q)
|
void
|
||||||
|
push(queue<int>* q)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
q->push(i);
|
q->push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pop(queue<int>* q)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
while (i < 10) {
|
||||||
|
std::shared_ptr<int> p = q->pop();
|
||||||
|
if (p) {
|
||||||
|
printf("poping %d\n", *p);
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(queue<int>* q)
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
int i = 0;
|
queue<int> q;
|
||||||
while (i < 10) {
|
std::thread t1(push, &q);
|
||||||
std::shared_ptr<int> p = q->pop();
|
std::thread t2(pop, &q);
|
||||||
if (p) {
|
t1.join();
|
||||||
printf("poping %d\n", *p);
|
t2.join();
|
||||||
++i;
|
return 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
queue<int> q;
|
|
||||||
std::thread t1(push, &q);
|
|
||||||
std::thread t2(pop, &q);
|
|
||||||
t1.join();
|
|
||||||
t2.join();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
267
ch7/stack.cpp
267
ch7/stack.cpp
@@ -7,168 +7,161 @@
|
|||||||
// to_be_deleted list would grow without bounds, and you'd be essentailly
|
// to_be_deleted list would grow without bounds, and you'd be essentailly
|
||||||
// leaking memory again. If there aren't going to be any quiescent periods,
|
// leaking memory again. If there aren't going to be any quiescent periods,
|
||||||
// you need to find an alternative mechanism for reclaming the nodes. The key
|
// you need to find an alternative mechanism for reclaming the nodes. The key
|
||||||
// is to identify when no more threads are accesing a particular node so that
|
// is to identify when no more threads are accesing a particular node so that
|
||||||
// it can reclaimed. By far the easiest such mechanism to reason about is the
|
// it can reclaimed. By far the easiest such mechanism to reason about is the
|
||||||
// use of hazard ponters.
|
// use of hazard ponters.
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class stack
|
class stack {
|
||||||
{
|
private:
|
||||||
private:
|
struct node {
|
||||||
struct node
|
std::shared_ptr<T> data;
|
||||||
{
|
node* next;
|
||||||
std::shared_ptr<T> data;
|
|
||||||
node *next;
|
|
||||||
|
|
||||||
node(const T& data_)
|
node(const T& data_) : data(std::make_shared<T>(data_)) {}
|
||||||
: data(std::make_shared<T>(data_))
|
};
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::atomic<node *> head;
|
std::atomic<node*> head;
|
||||||
std::atomic<unsigned> threads_in_pop;
|
std::atomic<unsigned> threads_in_pop;
|
||||||
std::atomic<node *> to_be_deleted;
|
std::atomic<node*> to_be_deleted;
|
||||||
|
|
||||||
|
static void delete_nodes(node* nodes)
|
||||||
static void delete_nodes(node *nodes)
|
{
|
||||||
{
|
while (nodes) {
|
||||||
while (nodes) {
|
node* next = nodes->next;
|
||||||
node *next = nodes->next;
|
delete nodes;
|
||||||
delete nodes;
|
nodes = next;
|
||||||
nodes = next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void try_reclaim(node* old_head)
|
void try_reclaim(node* old_head)
|
||||||
{
|
{
|
||||||
if (threads_in_pop == 1) {
|
if (threads_in_pop == 1) {
|
||||||
// claim list of to-be-deleted nodes
|
// claim list of to-be-deleted nodes
|
||||||
node* nodes_to_delete = to_be_deleted.exchange(nullptr);
|
node* nodes_to_delete = to_be_deleted.exchange(nullptr);
|
||||||
|
|
||||||
// are you the only thread in pop()?
|
// are you the only thread in pop()?
|
||||||
if (!--threads_in_pop) {
|
if (!--threads_in_pop) {
|
||||||
// on other thread can be accessing this list of pending nodes.
|
// on other thread can be accessing this list of pending nodes.
|
||||||
// There may be new pending nodes, but you're not bothered
|
// There may be new pending nodes, but you're not bothered
|
||||||
// about them for now, as long as it's safe to reclaim your
|
// about them for now, as long as it's safe to reclaim your
|
||||||
// list.
|
// list.
|
||||||
delete_nodes(nodes_to_delete);
|
delete_nodes(nodes_to_delete);
|
||||||
}
|
}
|
||||||
else if (nodes_to_delete) {
|
else if (nodes_to_delete) {
|
||||||
// not safe to reclaim the nodes, so if there are any,
|
// not safe to reclaim the nodes, so if there are any,
|
||||||
// you must chain them back onto the list of nodes
|
// you must chain them back onto the list of nodes
|
||||||
// pending deletion.
|
// pending deletion.
|
||||||
// This can happen if there are multiple threads accessing the
|
// This can happen if there are multiple threads accessing the
|
||||||
// data structure concurrently. Other threads might have
|
// data structure concurrently. Other threads might have
|
||||||
// called pop() in between the first tet of thread_in_pop and
|
// called pop() in between the first tet of thread_in_pop and
|
||||||
// the "claiming" of the list, potentially adding new nodes to
|
// the "claiming" of the list, potentially adding new nodes to
|
||||||
// the list that are still being accesed by one or more of
|
// the list that are still being accesed by one or more of
|
||||||
// those other threads.
|
// those other threads.
|
||||||
chain_pending_nodes(nodes_to_delete);
|
chain_pending_nodes(nodes_to_delete);
|
||||||
}
|
}
|
||||||
delete old_head;
|
delete old_head;
|
||||||
}
|
|
||||||
else {
|
|
||||||
// not safe to delete any nodes, add the node to the pending list
|
|
||||||
chain_pending_node(old_head);
|
|
||||||
--threads_in_pop;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
void chain_pending_nodes(node* nodes)
|
// not safe to delete any nodes, add the node to the pending list
|
||||||
{
|
chain_pending_node(old_head);
|
||||||
node* last = nodes;
|
--threads_in_pop;
|
||||||
// traverse the chain to find the end
|
|
||||||
while (node* const next = last->next) {
|
|
||||||
last = next;
|
|
||||||
}
|
|
||||||
chain_pending_nodes(nodes, last);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void chain_pending_nodes(node* first, node* last)
|
void chain_pending_nodes(node* nodes)
|
||||||
{
|
{
|
||||||
// replace the next pointer from the last node with
|
node* last = nodes;
|
||||||
// the current to_be_deleted pointer
|
// traverse the chain to find the end
|
||||||
last->next = to_be_deleted;
|
while (node* const next = last->next) {
|
||||||
// store the first node in the chain as the new to_be_deleted pointer
|
last = next;
|
||||||
// have to use compare_exchange_weak in a loop here in order to ensure
|
|
||||||
// that you don't leak any nodes that have been added by another thread
|
|
||||||
while (!to_be_deleted.compare_exchange_weak(
|
|
||||||
last->next,first));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
chain_pending_nodes(nodes, last);
|
||||||
|
}
|
||||||
|
|
||||||
void chain_pending_node(node* n)
|
void chain_pending_nodes(node* first, node* last)
|
||||||
{
|
{
|
||||||
// adding a single node onto the list is a special case where the
|
// replace the next pointer from the last node with
|
||||||
// first node onto the list is a special case where the first node
|
// the current to_be_deleted pointer
|
||||||
// in the chain to be added is the same as the last one.
|
last->next = to_be_deleted;
|
||||||
chain_pending_nodes(n,n);
|
// store the first node in the chain as the new to_be_deleted pointer
|
||||||
}
|
// have to use compare_exchange_weak in a loop here in order to ensure
|
||||||
|
// that you don't leak any nodes that have been added by another thread
|
||||||
public:
|
while (!to_be_deleted.compare_exchange_weak(last->next, first))
|
||||||
stack()
|
;
|
||||||
: head(nullptr)
|
}
|
||||||
, threads_in_pop(0)
|
|
||||||
, to_be_deleted(nullptr)
|
void chain_pending_node(node* n)
|
||||||
{}
|
{
|
||||||
|
// adding a single node onto the list is a special case where the
|
||||||
void push(const T& data)
|
// first node onto the list is a special case where the first node
|
||||||
{
|
// in the chain to be added is the same as the last one.
|
||||||
node *const new_node = new node(data);
|
chain_pending_nodes(n, n);
|
||||||
new_node->next = head.load();
|
}
|
||||||
// loop to gurantee that last->next is correct
|
|
||||||
while (!head.compare_exchange_weak(new_node->next, new_node));
|
public:
|
||||||
}
|
stack() : head(nullptr), threads_in_pop(0), to_be_deleted(nullptr) {}
|
||||||
|
|
||||||
std::shared_ptr<T> pop()
|
void push(const T& data)
|
||||||
{
|
{
|
||||||
++threads_in_pop; // increase counter before doing anything else
|
node* const new_node = new node(data);
|
||||||
node *old_head = head.load();
|
new_node->next = head.load();
|
||||||
while (old_head &&
|
// loop to gurantee that last->next is correct
|
||||||
!head.compare_exchange_weak(old_head, old_head->next));
|
while (!head.compare_exchange_weak(new_node->next, new_node))
|
||||||
std::shared_ptr<T> res;
|
;
|
||||||
if (old_head) {
|
}
|
||||||
res.swap(old_head->data); // extract data from node rather than
|
|
||||||
// coping pointer
|
std::shared_ptr<T> pop()
|
||||||
}
|
{
|
||||||
try_reclaim(old_head); // reclaim deleted nodes if you can
|
++threads_in_pop; // increase counter before doing anything else
|
||||||
return res;
|
node* old_head = head.load();
|
||||||
|
while (old_head && !head.compare_exchange_weak(old_head, old_head->next))
|
||||||
|
;
|
||||||
|
std::shared_ptr<T> res;
|
||||||
|
if (old_head) {
|
||||||
|
res.swap(old_head->data); // extract data from node rather than
|
||||||
|
// coping pointer
|
||||||
}
|
}
|
||||||
|
try_reclaim(old_head); // reclaim deleted nodes if you can
|
||||||
|
return res;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void push(stack<int>* s)
|
void
|
||||||
|
push(stack<int>* s)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
s->push(i);
|
s->push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pop(stack<int>* s)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
std::shared_ptr<int> e;
|
||||||
|
while (count < 10) {
|
||||||
|
if (e = s->pop()) {
|
||||||
|
printf("popping %d\n", *e);
|
||||||
|
++count;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(stack<int>* s)
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
int count = 0;
|
stack<int> s;
|
||||||
std::shared_ptr<int> e;
|
std::thread t1(push, &s);
|
||||||
while (count < 10) {
|
std::thread t2(pop, &s);
|
||||||
if (e = s->pop()) {
|
t1.join();
|
||||||
printf("popping %d\n", *e);
|
t2.join();
|
||||||
++count;
|
return 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
stack<int> s;
|
|
||||||
std::thread t1(push, &s);
|
|
||||||
std::thread t2(pop, &s);
|
|
||||||
t1.join();
|
|
||||||
t2.join();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
306
ch7/stack_hp.cpp
306
ch7/stack_hp.cpp
@@ -1,193 +1,191 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include "hazard_pointer.h"
|
#include "hazard_pointer.h"
|
||||||
|
|
||||||
std::atomic<void*>& get_hazard_pointer_for_current_thread()
|
std::atomic<void*>&
|
||||||
|
get_hazard_pointer_for_current_thread()
|
||||||
{
|
{
|
||||||
// The first time each thread calls this function, a new instance of
|
// The first time each thread calls this function, a new instance of
|
||||||
// hp_owner is created. The constructor for this new instance then
|
// hp_owner is created. The constructor for this new instance then
|
||||||
// searchs through the table of owner/pointer pairs looking for an entry
|
// searchs through the table of owner/pointer pairs looking for an entry
|
||||||
// without an owner. It uses compare_exchange_strong() to check for an
|
// without an owner. It uses compare_exchange_strong() to check for an
|
||||||
// entry without an owner and claim it in one go.
|
// entry without an owner and claim it in one go.
|
||||||
//
|
//
|
||||||
// Once the hp_owner instance has been created for a given thread, further
|
// Once the hp_owner instance has been created for a given thread, further
|
||||||
// accesses are much faster because the pointer is cached, so the table
|
// accesses are much faster because the pointer is cached, so the table
|
||||||
// doesn't have to be scanned again.
|
// doesn't have to be scanned again.
|
||||||
|
|
||||||
printf("get harzard pointer for current thread, thread id: %d\n", std::this_thread::get_id());
|
printf("get harzard pointer for current thread, thread id: %d\n",
|
||||||
thread_local static hp_owner hazard;
|
std::this_thread::get_id());
|
||||||
return hazard.get_pointer();
|
thread_local static hp_owner hazard;
|
||||||
|
return hazard.get_pointer();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool outstanding_hazard_pointers_for(void *p)
|
bool
|
||||||
|
outstanding_hazard_pointers_for(void* p)
|
||||||
{
|
{
|
||||||
for (unsigned i = 0; i < max_hazard_pointers; ++i) {
|
for (unsigned i = 0; i < max_hazard_pointers; ++i) {
|
||||||
if (hazard_pointers[i].pointer.load() == p) {
|
if (hazard_pointers[i].pointer.load() == p) {
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void do_delete(void* p)
|
void
|
||||||
|
do_delete(void* p)
|
||||||
{
|
{
|
||||||
delete static_cast<T*>(p);
|
delete static_cast<T*>(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct data_to_reclaim
|
struct data_to_reclaim {
|
||||||
{
|
void* data;
|
||||||
void* data;
|
std::function<void(void*)> deleter;
|
||||||
std::function<void(void *)> deleter;
|
data_to_reclaim* next;
|
||||||
data_to_reclaim* next;
|
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
data_to_reclaim(T* p)
|
data_to_reclaim(T* p) : data(p), deleter(&do_delete<T>), next(0)
|
||||||
: data(p)
|
{
|
||||||
, deleter(&do_delete<T>)
|
}
|
||||||
, next(0)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~data_to_reclaim()
|
~data_to_reclaim() { deleter(data); }
|
||||||
{
|
|
||||||
deleter(data);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::atomic<data_to_reclaim*> nodes_to_reclaim;
|
std::atomic<data_to_reclaim*> nodes_to_reclaim;
|
||||||
void add_to_reclaim_list(data_to_reclaim* node)
|
void
|
||||||
|
add_to_reclaim_list(data_to_reclaim* node)
|
||||||
{
|
{
|
||||||
node->next = nodes_to_reclaim.load();
|
node->next = nodes_to_reclaim.load();
|
||||||
while (!nodes_to_reclaim.compare_exchange_weak(node->next, node));
|
while (!nodes_to_reclaim.compare_exchange_weak(node->next, node))
|
||||||
}
|
;
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void reclaim_later(T* data)
|
|
||||||
{
|
|
||||||
add_to_reclaim_list(new data_to_reclaim(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
void delete_nodes_with_no_hazards()
|
|
||||||
{
|
|
||||||
// first claims the entire list of nodes to be reclaimed;
|
|
||||||
// ensures that this is the only thread trying to reclaim
|
|
||||||
// this particular set of nodes; other threads are now free
|
|
||||||
// to add futher nodes to the list or event try to reclaim
|
|
||||||
// them without impacting the operation of this thread.
|
|
||||||
data_to_reclaim* current = nodes_to_reclaim.exchange(nullptr);
|
|
||||||
|
|
||||||
while (current) {
|
|
||||||
data_to_reclaim* const next = current->next;
|
|
||||||
|
|
||||||
// check each node in turn to see if there are any outstanding
|
|
||||||
// hazard pointers.
|
|
||||||
if (!outstanding_hazard_pointers_for(current->data)) {
|
|
||||||
// if there aren't, delete the entry
|
|
||||||
delete current;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// otherwise, just add the item back on the list for
|
|
||||||
// reclaiming later
|
|
||||||
add_to_reclaim_list(current);
|
|
||||||
}
|
|
||||||
current=next;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class stack
|
void
|
||||||
|
reclaim_later(T* data)
|
||||||
{
|
{
|
||||||
private:
|
add_to_reclaim_list(new data_to_reclaim(data));
|
||||||
struct node
|
}
|
||||||
{
|
|
||||||
std::shared_ptr<T> data;
|
|
||||||
node *next;
|
|
||||||
|
|
||||||
node(const T& data_)
|
void
|
||||||
: data(std::make_shared<T>(data_))
|
delete_nodes_with_no_hazards()
|
||||||
{}
|
{
|
||||||
};
|
// first claims the entire list of nodes to be reclaimed;
|
||||||
|
// ensures that this is the only thread trying to reclaim
|
||||||
|
// this particular set of nodes; other threads are now free
|
||||||
|
// to add futher nodes to the list or event try to reclaim
|
||||||
|
// them without impacting the operation of this thread.
|
||||||
|
data_to_reclaim* current = nodes_to_reclaim.exchange(nullptr);
|
||||||
|
|
||||||
std::atomic<node *> head;
|
while (current) {
|
||||||
|
data_to_reclaim* const next = current->next;
|
||||||
|
|
||||||
public:
|
// check each node in turn to see if there are any outstanding
|
||||||
stack()
|
// hazard pointers.
|
||||||
: head(nullptr)
|
if (!outstanding_hazard_pointers_for(current->data)) {
|
||||||
{}
|
// if there aren't, delete the entry
|
||||||
|
delete current;
|
||||||
void push(const T& data)
|
|
||||||
{
|
|
||||||
node *const new_node = new node(data);
|
|
||||||
new_node->next = head.load();
|
|
||||||
while (!head.compare_exchange_weak(new_node->next, new_node));
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
std::shared_ptr<T> pop()
|
// otherwise, just add the item back on the list for
|
||||||
{
|
// reclaiming later
|
||||||
std::atomic<void*>& hp = get_hazard_pointer_for_current_thread();
|
add_to_reclaim_list(current);
|
||||||
node* old_head = head.load();
|
|
||||||
do {
|
|
||||||
node* temp;
|
|
||||||
do { // loop until you've set the harzard pointer to head
|
|
||||||
temp = old_head;
|
|
||||||
hp.store(old_head);
|
|
||||||
old_head = head.load();
|
|
||||||
} while (old_head != temp);
|
|
||||||
}
|
|
||||||
while (old_head &&
|
|
||||||
!head.compare_exchange_strong(old_head, old_head->next));
|
|
||||||
hp.store(nullptr); // clear hazard pointer once you're finished
|
|
||||||
std::shared_ptr<T> res;
|
|
||||||
if (old_head) {
|
|
||||||
res.swap(old_head->data);
|
|
||||||
if (outstanding_hazard_pointers_for(old_head)) {
|
|
||||||
// check for hazard pointers referencing
|
|
||||||
// a node before you delete it
|
|
||||||
reclaim_later(old_head);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
delete old_head;
|
|
||||||
}
|
|
||||||
delete_nodes_with_no_hazards();
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class stack {
|
||||||
|
private:
|
||||||
|
struct node {
|
||||||
|
std::shared_ptr<T> data;
|
||||||
|
node* next;
|
||||||
|
|
||||||
|
node(const T& data_) : data(std::make_shared<T>(data_)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::atomic<node*> head;
|
||||||
|
|
||||||
|
public:
|
||||||
|
stack() : head(nullptr) {}
|
||||||
|
|
||||||
|
void push(const T& data)
|
||||||
|
{
|
||||||
|
node* const new_node = new node(data);
|
||||||
|
new_node->next = head.load();
|
||||||
|
while (!head.compare_exchange_weak(new_node->next, new_node))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<T> pop()
|
||||||
|
{
|
||||||
|
std::atomic<void*>& hp = get_hazard_pointer_for_current_thread();
|
||||||
|
node* old_head = head.load();
|
||||||
|
do {
|
||||||
|
node* temp;
|
||||||
|
do { // loop until you've set the harzard pointer to head
|
||||||
|
temp = old_head;
|
||||||
|
hp.store(old_head);
|
||||||
|
old_head = head.load();
|
||||||
|
} while (old_head != temp);
|
||||||
|
} while (old_head &&
|
||||||
|
!head.compare_exchange_strong(old_head, old_head->next));
|
||||||
|
hp.store(nullptr); // clear hazard pointer once you're finished
|
||||||
|
std::shared_ptr<T> res;
|
||||||
|
if (old_head) {
|
||||||
|
res.swap(old_head->data);
|
||||||
|
if (outstanding_hazard_pointers_for(old_head)) {
|
||||||
|
// check for hazard pointers referencing
|
||||||
|
// a node before you delete it
|
||||||
|
reclaim_later(old_head);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete old_head;
|
||||||
|
}
|
||||||
|
delete_nodes_with_no_hazards();
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void push(stack<int>* s)
|
void
|
||||||
|
push(stack<int>* s)
|
||||||
{
|
{
|
||||||
printf("starting push\n");
|
printf("starting push\n");
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
s->push(i);
|
s->push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pop(stack<int>* s)
|
||||||
|
{
|
||||||
|
printf("starting pop\n");
|
||||||
|
int count = 0;
|
||||||
|
std::shared_ptr<int> e;
|
||||||
|
while (count < 10) {
|
||||||
|
if (e = s->pop()) {
|
||||||
|
printf("popping %d\n", *e);
|
||||||
|
++count;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(stack<int>* s)
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
printf("starting pop\n");
|
printf("creating stack\n");
|
||||||
int count = 0;
|
stack<int> s;
|
||||||
std::shared_ptr<int> e;
|
printf("stack created\n");
|
||||||
while (count < 10) {
|
std::thread t1(push, &s);
|
||||||
if (e = s->pop()) {
|
std::thread t2(pop, &s);
|
||||||
printf("popping %d\n", *e);
|
t1.join();
|
||||||
++count;
|
t2.join();
|
||||||
}
|
return 0;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
printf("creating stack\n");
|
|
||||||
stack<int> s;
|
|
||||||
printf("stack created\n");
|
|
||||||
std::thread t1(push, &s);
|
|
||||||
std::thread t2(pop, &s);
|
|
||||||
t1.join();
|
|
||||||
t2.join();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,183 +1,171 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class stack
|
class stack {
|
||||||
{
|
private:
|
||||||
private:
|
struct node;
|
||||||
struct node;
|
|
||||||
|
|
||||||
// the external count is kept alongside the poiner to the node and is
|
|
||||||
// increased every time the pointer is read. When the reader is finished
|
|
||||||
// with the node, it decraes the internal count. A simple operation that
|
|
||||||
// reads the pointer will thus leave the external count increaesd by one
|
|
||||||
// and the internal count decreased by one when it's finished.
|
|
||||||
//
|
|
||||||
// When the external count/pointer pairing is no longer required (that is,
|
|
||||||
// the node is no longer accessbile from a location accessible to multple
|
|
||||||
// threads), the internal count is increased by the value of the external
|
|
||||||
// count minus one and external counter is discarded. Once the internal
|
|
||||||
// count is equal to zero, there are no outstanding references to the node
|
|
||||||
// and it can be safely deleted.
|
|
||||||
struct counted_node_ptr
|
|
||||||
{
|
|
||||||
int external_count;
|
|
||||||
node *ptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct node
|
// the external count is kept alongside the poiner to the node and is
|
||||||
{
|
// increased every time the pointer is read. When the reader is finished
|
||||||
std::shared_ptr<T> data;
|
// with the node, it decraes the internal count. A simple operation that
|
||||||
std::atomic_int internal_count;
|
// reads the pointer will thus leave the external count increaesd by one
|
||||||
counted_node_ptr next;
|
// and the internal count decreased by one when it's finished.
|
||||||
|
//
|
||||||
|
// When the external count/pointer pairing is no longer required (that is,
|
||||||
|
// the node is no longer accessbile from a location accessible to multple
|
||||||
|
// threads), the internal count is increased by the value of the external
|
||||||
|
// count minus one and external counter is discarded. Once the internal
|
||||||
|
// count is equal to zero, there are no outstanding references to the node
|
||||||
|
// and it can be safely deleted.
|
||||||
|
struct counted_node_ptr {
|
||||||
|
int external_count;
|
||||||
|
node* ptr;
|
||||||
|
};
|
||||||
|
|
||||||
node(const T& data_)
|
struct node {
|
||||||
: data(new T(data_))
|
std::shared_ptr<T> data;
|
||||||
, internal_count(0)
|
std::atomic_int internal_count;
|
||||||
{}
|
counted_node_ptr next;
|
||||||
};
|
|
||||||
|
|
||||||
std::atomic<counted_node_ptr> head;
|
node(const T& data_) : data(new T(data_)), internal_count(0) {}
|
||||||
// NOTE: on those platforms that support a double-word-compare-and-swap
|
};
|
||||||
// operation, counted_node_ptr will be small enough for
|
|
||||||
// std::atomic<counted_node_ptr> to be lock-free. If it isn't on your
|
|
||||||
// platform, you might be better off using the std::shared_ptr<> version,
|
|
||||||
// because std::atomic<> will use a mutex to guarantee atomicity when the
|
|
||||||
// type is too large for the pltaform's atomic instrcutions (thus rending
|
|
||||||
// your "lock-free" algorithm lock-based after all). Alternatively, if
|
|
||||||
// you are're willing to limit the size of the counter, and you know that
|
|
||||||
// your platform has spare bits in a pointer (for example, because the
|
|
||||||
// address space is only 48 bits but a pointer is 64 bits), you can store
|
|
||||||
// the count inside the spare bits of the pointer to fit it all back in a
|
|
||||||
// single machine word.
|
|
||||||
|
|
||||||
|
std::atomic<counted_node_ptr> head;
|
||||||
|
// NOTE: on those platforms that support a double-word-compare-and-swap
|
||||||
|
// operation, counted_node_ptr will be small enough for
|
||||||
|
// std::atomic<counted_node_ptr> to be lock-free. If it isn't on your
|
||||||
|
// platform, you might be better off using the std::shared_ptr<> version,
|
||||||
|
// because std::atomic<> will use a mutex to guarantee atomicity when the
|
||||||
|
// type is too large for the pltaform's atomic instrcutions (thus rending
|
||||||
|
// your "lock-free" algorithm lock-based after all). Alternatively, if
|
||||||
|
// you are're willing to limit the size of the counter, and you know that
|
||||||
|
// your platform has spare bits in a pointer (for example, because the
|
||||||
|
// address space is only 48 bits but a pointer is 64 bits), you can store
|
||||||
|
// the count inside the spare bits of the pointer to fit it all back in a
|
||||||
|
// single machine word.
|
||||||
|
|
||||||
// once you've loaded the value of head, you must first increaes the count
|
// once you've loaded the value of head, you must first increaes the count
|
||||||
// of external references to the head node to indicate that you're
|
// of external references to the head node to indicate that you're
|
||||||
// referencing it and to ensure that it's safe to deference it.
|
// referencing it and to ensure that it's safe to deference it.
|
||||||
// By incrementing the external reference count, you ensure that the
|
// By incrementing the external reference count, you ensure that the
|
||||||
// pointer remains valid for the duration of your access.
|
// pointer remains valid for the duration of your access.
|
||||||
void increase_head_count(counted_node_ptr& old_counter)
|
void increase_head_count(counted_node_ptr& old_counter)
|
||||||
{
|
{
|
||||||
counted_node_ptr new_counter;
|
counted_node_ptr new_counter;
|
||||||
|
|
||||||
do
|
do {
|
||||||
{
|
new_counter = old_counter;
|
||||||
new_counter = old_counter;
|
++new_counter.external_count;
|
||||||
++new_counter.external_count;
|
} while (!head.compare_exchange_strong(old_counter, new_counter));
|
||||||
|
|
||||||
|
old_counter.external_count = new_counter.external_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
~stack()
|
||||||
|
{
|
||||||
|
while (pop())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(const T& data)
|
||||||
|
{
|
||||||
|
counted_node_ptr new_node;
|
||||||
|
new_node.ptr = new node(data);
|
||||||
|
// internal_count is zeor, and the external_count is one;
|
||||||
|
// because this is a new node, there's currently only one
|
||||||
|
// external reference to the node (the head pointer itself).
|
||||||
|
new_node.external_count = 1;
|
||||||
|
new_node.ptr->next = head.load();
|
||||||
|
while (!head.compare_exchange_weak(new_node.ptr->next, new_node))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<T> pop()
|
||||||
|
{
|
||||||
|
counted_node_ptr old_head = head.load();
|
||||||
|
for (;;) {
|
||||||
|
increase_head_count(old_head);
|
||||||
|
node* ptr = old_head.ptr;
|
||||||
|
// if the pointer is a null pointer, you're at the end of list:
|
||||||
|
// no more entires
|
||||||
|
if (!ptr) {
|
||||||
|
return std::shared_ptr<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the pointer isn't a null pointer, try to remove the node
|
||||||
|
if (head.compare_exchange_strong(old_head, ptr->next)) {
|
||||||
|
// you've taken the ownership of the node and can swap out
|
||||||
|
// the data in prepration for returning it.
|
||||||
|
std::shared_ptr<T> res;
|
||||||
|
res.swap(ptr->data);
|
||||||
|
|
||||||
|
// you've removed the node from the list, so you drop one
|
||||||
|
// off the count for that, and you're no longer accessing
|
||||||
|
// the node from this thread, so you drop another off the
|
||||||
|
// count for that.
|
||||||
|
const int count_increase = old_head.external_count - 2;
|
||||||
|
|
||||||
|
// if the reference count is now zero, the previous value
|
||||||
|
// (which is what fetch_add returns) was the negative of what
|
||||||
|
// you just added, in which case you can delete the node.
|
||||||
|
if (ptr->internal_count.fetch_add(count_increase) == -count_increase) {
|
||||||
|
delete ptr;
|
||||||
}
|
}
|
||||||
while (!head.compare_exchange_strong(old_counter, new_counter));
|
|
||||||
|
|
||||||
old_counter.external_count = new_counter.external_count;
|
// whether or not you deleted the node, you've finished.
|
||||||
}
|
return res;
|
||||||
|
}
|
||||||
public:
|
// if the compare/exchange fails, another therad removed your node
|
||||||
~stack()
|
// before you did, or another thread added a new node to the stack.
|
||||||
{
|
// Either way, you need start again with the fresh value of head
|
||||||
while (pop());
|
// returned by the compare/exchange call. But first you must
|
||||||
}
|
// decrease the reference count on the node you were trying to
|
||||||
|
// remove. This thread won't access it anymore. If you're the last
|
||||||
void push(const T& data)
|
// thread to hold a reference (because another thread removed it
|
||||||
{
|
// from the stack), the internal reference count will be 1, so
|
||||||
counted_node_ptr new_node;
|
// subtracing 1 will set the count to zero. In this case, you can
|
||||||
new_node.ptr = new node(data);
|
// delete the node here before you loop.
|
||||||
// internal_count is zeor, and the external_count is one;
|
else if (ptr->internal_count.fetch_add(-1) == 1) {
|
||||||
// because this is a new node, there's currently only one
|
delete ptr;
|
||||||
// external reference to the node (the head pointer itself).
|
}
|
||||||
new_node.external_count = 1;
|
|
||||||
new_node.ptr->next = head.load();
|
|
||||||
while (!head.compare_exchange_weak(new_node.ptr->next, new_node));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<T> pop()
|
|
||||||
{
|
|
||||||
counted_node_ptr old_head = head.load();
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
increase_head_count(old_head);
|
|
||||||
node* ptr = old_head.ptr;
|
|
||||||
// if the pointer is a null pointer, you're at the end of list:
|
|
||||||
// no more entires
|
|
||||||
if (!ptr)
|
|
||||||
{
|
|
||||||
return std::shared_ptr<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the pointer isn't a null pointer, try to remove the node
|
|
||||||
if (head.compare_exchange_strong(old_head,ptr->next))
|
|
||||||
{
|
|
||||||
// you've taken the ownership of the node and can swap out
|
|
||||||
// the data in prepration for returning it.
|
|
||||||
std::shared_ptr<T> res;
|
|
||||||
res.swap(ptr->data);
|
|
||||||
|
|
||||||
// you've removed the node from the list, so you drop one
|
|
||||||
// off the count for that, and you're no longer accessing
|
|
||||||
// the node from this thread, so you drop another off the
|
|
||||||
// count for that.
|
|
||||||
const int count_increase = old_head.external_count - 2;
|
|
||||||
|
|
||||||
// if the reference count is now zero, the previous value
|
|
||||||
// (which is what fetch_add returns) was the negative of what
|
|
||||||
// you just added, in which case you can delete the node.
|
|
||||||
if (ptr->internal_count.fetch_add(count_increase) ==
|
|
||||||
-count_increase)
|
|
||||||
{
|
|
||||||
delete ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// whether or not you deleted the node, you've finished.
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
// if the compare/exchange fails, another therad removed your node
|
|
||||||
// before you did, or another thread added a new node to the stack.
|
|
||||||
// Either way, you need start again with the fresh value of head
|
|
||||||
// returned by the compare/exchange call. But first you must
|
|
||||||
// decrease the reference count on the node you were trying to
|
|
||||||
// remove. This thread won't access it anymore. If you're the last
|
|
||||||
// thread to hold a reference (because another thread removed it
|
|
||||||
// from the stack), the internal reference count will be 1, so
|
|
||||||
// subtracing 1 will set the count to zero. In this case, you can
|
|
||||||
// delete the node here before you loop.
|
|
||||||
else if (ptr->internal_count.fetch_add(-1) == 1)
|
|
||||||
{
|
|
||||||
delete ptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void push(stack<int>* s)
|
void
|
||||||
|
push(stack<int>* s)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
s->push(i);
|
s->push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pop(stack<int>* s)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
std::shared_ptr<int> e;
|
||||||
|
while (count < 10) {
|
||||||
|
if (e = s->pop()) {
|
||||||
|
printf("popping %d\n", *e);
|
||||||
|
++count;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(stack<int>* s)
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
int count = 0;
|
stack<int> s;
|
||||||
std::shared_ptr<int> e;
|
std::thread t1(push, &s);
|
||||||
while (count < 10) {
|
std::thread t2(pop, &s);
|
||||||
if (e = s->pop()) {
|
t1.join();
|
||||||
printf("popping %d\n", *e);
|
t2.join();
|
||||||
++count;
|
return 0;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
stack<int> s;
|
|
||||||
std::thread t1(push, &s);
|
|
||||||
std::thread t2(pop, &s);
|
|
||||||
t1.join();
|
|
||||||
t2.join();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
112
ch7/stack_sp.cpp
112
ch7/stack_sp.cpp
@@ -1,70 +1,70 @@
|
|||||||
#include <thread>
|
|
||||||
#include <memory>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class stack
|
class stack {
|
||||||
{
|
private:
|
||||||
private:
|
struct node {
|
||||||
struct node
|
std::shared_ptr<T> data;
|
||||||
{
|
std::shared_ptr<node> next;
|
||||||
std::shared_ptr<T> data;
|
|
||||||
std::shared_ptr<node> next;
|
|
||||||
|
|
||||||
node(T const& data_)
|
node(T const& data_) : data(std::make_shared<T>(data_)) {}
|
||||||
: data(std::make_shared<T>(data_))
|
};
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<node> head;
|
std::shared_ptr<node> head;
|
||||||
public:
|
|
||||||
void push(T const& data)
|
public:
|
||||||
{
|
void push(T const& data)
|
||||||
std::shared_ptr<node> const new_node = std::make_shared<node>(data);
|
{
|
||||||
new_node->next = std::atomic_load(head);
|
std::shared_ptr<node> const new_node = std::make_shared<node>(data);
|
||||||
while (!std::atomic_compare_exchange_weak(&head,
|
new_node->next = std::atomic_load(head);
|
||||||
&new_node->next, new_node));
|
while (!std::atomic_compare_exchange_weak(&head, &new_node->next, new_node))
|
||||||
}
|
;
|
||||||
std::shared_ptr<T> pop()
|
}
|
||||||
{
|
std::shared_ptr<T> pop()
|
||||||
std::shared_ptr<node> old_head = std::atomic_load(head);
|
{
|
||||||
while (old_head && !std::atomic_compare_exchange_weak(&head,
|
std::shared_ptr<node> old_head = std::atomic_load(head);
|
||||||
&old_head, old_head->next));
|
while (old_head &&
|
||||||
return old_head ? old_head->data : std::shared_ptr<T>();
|
!std::atomic_compare_exchange_weak(&head, &old_head, old_head->next))
|
||||||
}
|
;
|
||||||
|
return old_head ? old_head->data : std::shared_ptr<T>();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void push(stack<int>* s)
|
void
|
||||||
|
push(stack<int>* s)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 10; ++i) {
|
for (int i = 0; i < 10; ++i) {
|
||||||
printf("pushing %d\n", i);
|
printf("pushing %d\n", i);
|
||||||
s->push(i);
|
s->push(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pop(stack<int>* s)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
std::shared_ptr<int> e;
|
||||||
|
while (count < 10) {
|
||||||
|
if (e = s->pop()) {
|
||||||
|
printf("popping %d\n", *e);
|
||||||
|
++count;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void pop(stack<int>* s)
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
int count = 0;
|
std::shared_ptr<int> sp = std::make_shared<int>(1);
|
||||||
std::shared_ptr<int> e;
|
// printf("std::atomic_is_lock_free(std::shared_ptr): %d\n",
|
||||||
while (count < 10) {
|
// std::atomic_is_lock_free(&sp));
|
||||||
if (e = s->pop()) {
|
stack<int> s;
|
||||||
printf("popping %d\n", *e);
|
std::thread t1(push, &s);
|
||||||
++count;
|
std::thread t2(pop, &s);
|
||||||
}
|
t1.join();
|
||||||
}
|
t2.join();
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
|
||||||
std::shared_ptr<int> sp = std::make_shared<int>(1);
|
|
||||||
//printf("std::atomic_is_lock_free(std::shared_ptr): %d\n",
|
|
||||||
// std::atomic_is_lock_free(&sp));
|
|
||||||
stack<int> s;
|
|
||||||
std::thread t1(push, &s);
|
|
||||||
std::thread t2(pop, &s);
|
|
||||||
t1.join();
|
|
||||||
t2.join();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
void hello()
|
void
|
||||||
|
hello()
|
||||||
{
|
{
|
||||||
std::cout<<"Hello Concurrent World\n";
|
std::cout << "Hello Concurrent World\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
std::thread t(hello);
|
std::thread t(hello);
|
||||||
t.join();
|
t.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,35 @@
|
|||||||
void test_concurrent_push_and_pop_on_empty_queue()
|
void
|
||||||
|
test_concurrent_push_and_pop_on_empty_queue()
|
||||||
{
|
{
|
||||||
threadsafe_queue<int> q;
|
threadsafe_queue<int> q;
|
||||||
|
|
||||||
std::promise<void> go,push_ready,pop_ready;
|
std::promise<void> go, push_ready, pop_ready;
|
||||||
std::shared_future<void> ready(go.get_future());
|
std::shared_future<void> ready(go.get_future());
|
||||||
|
|
||||||
std::future<void> push_done;
|
std::future<void> push_done;
|
||||||
std::future<int> pop_done;
|
std::future<int> pop_done;
|
||||||
|
|
||||||
try
|
try {
|
||||||
{
|
push_done = std::async(std::launch::async, [&q, ready, &push_ready]() {
|
||||||
push_done=std::async(std::launch::async,
|
push_ready.set_value();
|
||||||
[&q,ready,&push_ready]()
|
ready.wait();
|
||||||
{
|
q.push(42);
|
||||||
push_ready.set_value();
|
});
|
||||||
ready.wait();
|
pop_done = std::async(std::launch::async, [&q, ready, &pop_ready]() {
|
||||||
q.push(42);
|
pop_ready.set_value();
|
||||||
}
|
ready.wait();
|
||||||
);
|
return q.pop();
|
||||||
pop_done=std::async(std::launch::async,
|
});
|
||||||
[&q,ready,&pop_ready]()
|
push_ready.get_future().wait();
|
||||||
{
|
pop_ready.get_future().wait();
|
||||||
pop_ready.set_value();
|
go.set_value();
|
||||||
ready.wait();
|
|
||||||
return q.pop();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
push_ready.get_future().wait();
|
|
||||||
pop_ready.get_future().wait();
|
|
||||||
go.set_value();
|
|
||||||
|
|
||||||
push_done.get();
|
push_done.get();
|
||||||
assert(pop_done.get()==42);
|
assert(pop_done.get() == 42);
|
||||||
assert(q.empty());
|
assert(q.empty());
|
||||||
}
|
}
|
||||||
catch(...)
|
catch (...) {
|
||||||
{
|
go.set_value();
|
||||||
go.set_value();
|
throw;
|
||||||
throw;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
void do_something(int& i)
|
void
|
||||||
|
do_something(int& i)
|
||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct func
|
struct func {
|
||||||
{
|
int& i;
|
||||||
int& i;
|
|
||||||
|
|
||||||
func(int& i_):i(i_){}
|
func(int& i_) : i(i_) {}
|
||||||
|
|
||||||
void operator()()
|
void operator()()
|
||||||
{
|
{
|
||||||
for(unsigned j=0;j<1000000;++j)
|
for (unsigned j = 0; j < 1000000; ++j) {
|
||||||
{
|
do_something(i);
|
||||||
do_something(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void
|
||||||
void oops()
|
oops()
|
||||||
{
|
{
|
||||||
int some_local_state=0;
|
int some_local_state = 0;
|
||||||
func my_func(some_local_state);
|
func my_func(some_local_state);
|
||||||
std::thread my_thread(my_func);
|
std::thread my_thread(my_func);
|
||||||
my_thread.detach();
|
my_thread.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
oops();
|
oops();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,47 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
void do_something(int& i)
|
void
|
||||||
|
do_something(int& i)
|
||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct func
|
struct func {
|
||||||
{
|
int& i;
|
||||||
int& i;
|
|
||||||
|
|
||||||
func(int& i_):i(i_){}
|
func(int& i_) : i(i_) {}
|
||||||
|
|
||||||
void operator()()
|
void operator()()
|
||||||
{
|
{
|
||||||
for(unsigned j=0;j<1000000;++j)
|
for (unsigned j = 0; j < 1000000; ++j) {
|
||||||
{
|
do_something(i);
|
||||||
do_something(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void do_something_in_current_thread()
|
void
|
||||||
{}
|
do_something_in_current_thread()
|
||||||
|
|
||||||
void f()
|
|
||||||
{
|
{
|
||||||
int some_local_state=0;
|
}
|
||||||
func my_func(some_local_state);
|
|
||||||
std::thread t(my_func);
|
void
|
||||||
try
|
f()
|
||||||
{
|
{
|
||||||
do_something_in_current_thread();
|
int some_local_state = 0;
|
||||||
}
|
func my_func(some_local_state);
|
||||||
catch(...)
|
std::thread t(my_func);
|
||||||
{
|
try {
|
||||||
t.join();
|
do_something_in_current_thread();
|
||||||
throw;
|
}
|
||||||
}
|
catch (...) {
|
||||||
t.join();
|
t.join();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
t.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
f();
|
f();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +1,57 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
class thread_guard
|
class thread_guard {
|
||||||
{
|
std::thread& t;
|
||||||
std::thread& t;
|
|
||||||
public:
|
public:
|
||||||
explicit thread_guard(std::thread& t_):
|
explicit thread_guard(std::thread& t_) : t(t_) {}
|
||||||
t(t_)
|
~thread_guard()
|
||||||
{}
|
{
|
||||||
~thread_guard()
|
if (t.joinable()) {
|
||||||
{
|
t.join();
|
||||||
if(t.joinable())
|
|
||||||
{
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
thread_guard(thread_guard const&)=delete;
|
}
|
||||||
thread_guard& operator=(thread_guard const&)=delete;
|
thread_guard(thread_guard const&) = delete;
|
||||||
|
thread_guard& operator=(thread_guard const&) = delete;
|
||||||
};
|
};
|
||||||
|
|
||||||
void do_something(int& i)
|
void
|
||||||
|
do_something(int& i)
|
||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct func
|
struct func {
|
||||||
{
|
int& i;
|
||||||
int& i;
|
|
||||||
|
|
||||||
func(int& i_):i(i_){}
|
func(int& i_) : i(i_) {}
|
||||||
|
|
||||||
void operator()()
|
void operator()()
|
||||||
{
|
{
|
||||||
for(unsigned j=0;j<1000000;++j)
|
for (unsigned j = 0; j < 1000000; ++j) {
|
||||||
{
|
do_something(i);
|
||||||
do_something(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void do_something_in_current_thread()
|
void
|
||||||
{}
|
do_something_in_current_thread()
|
||||||
|
|
||||||
|
|
||||||
void f()
|
|
||||||
{
|
{
|
||||||
int some_local_state;
|
|
||||||
func my_func(some_local_state);
|
|
||||||
std::thread t(my_func);
|
|
||||||
thread_guard g(t);
|
|
||||||
|
|
||||||
do_something_in_current_thread();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
void
|
||||||
|
f()
|
||||||
{
|
{
|
||||||
f();
|
int some_local_state;
|
||||||
|
func my_func(some_local_state);
|
||||||
|
std::thread t(my_func);
|
||||||
|
thread_guard g(t);
|
||||||
|
|
||||||
|
do_something_in_current_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
f();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
#include <thread>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
void open_document_and_display_gui(std::string const& filename)
|
void
|
||||||
{}
|
open_document_and_display_gui(std::string const& filename)
|
||||||
|
|
||||||
bool done_editing()
|
|
||||||
{
|
{
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum command_type{
|
bool
|
||||||
open_new_document
|
done_editing()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum command_type { open_new_document };
|
||||||
|
|
||||||
|
struct user_command {
|
||||||
|
command_type type;
|
||||||
|
|
||||||
|
user_command() : type(open_new_document) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
user_command
|
||||||
struct user_command
|
get_user_input()
|
||||||
{
|
{
|
||||||
command_type type;
|
return user_command();
|
||||||
|
|
||||||
user_command():
|
|
||||||
type(open_new_document)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
user_command get_user_input()
|
|
||||||
{
|
|
||||||
return user_command();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string get_filename_from_user()
|
std::string
|
||||||
|
get_filename_from_user()
|
||||||
{
|
{
|
||||||
return "foo.doc";
|
return "foo.doc";
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_user_input(user_command const& cmd)
|
void
|
||||||
{}
|
process_user_input(user_command const& cmd)
|
||||||
|
|
||||||
void edit_document(std::string const& filename)
|
|
||||||
{
|
{
|
||||||
open_document_and_display_gui(filename);
|
}
|
||||||
while(!done_editing())
|
|
||||||
{
|
void
|
||||||
user_command cmd=get_user_input();
|
edit_document(std::string const& filename)
|
||||||
if(cmd.type==open_new_document)
|
{
|
||||||
{
|
open_document_and_display_gui(filename);
|
||||||
std::string const new_name=get_filename_from_user();
|
while (!done_editing()) {
|
||||||
std::thread t(edit_document,new_name);
|
user_command cmd = get_user_input();
|
||||||
t.detach();
|
if (cmd.type == open_new_document) {
|
||||||
}
|
std::string const new_name = get_filename_from_user();
|
||||||
else
|
std::thread t(edit_document, new_name);
|
||||||
{
|
t.detach();
|
||||||
process_user_input(cmd);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
process_user_input(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
edit_document("bar.doc");
|
edit_document("bar.doc");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,34 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
void some_function()
|
void
|
||||||
{}
|
some_function()
|
||||||
|
|
||||||
void some_other_function(int)
|
|
||||||
{}
|
|
||||||
|
|
||||||
std::thread f()
|
|
||||||
{
|
{
|
||||||
void some_function();
|
|
||||||
return std::thread(some_function);
|
|
||||||
}
|
|
||||||
std::thread g()
|
|
||||||
{
|
|
||||||
void some_other_function(int);
|
|
||||||
std::thread t(some_other_function,42);
|
|
||||||
return t;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
void
|
||||||
|
some_other_function(int)
|
||||||
{
|
{
|
||||||
std::thread t1=f();
|
}
|
||||||
t1.join();
|
|
||||||
std::thread t2=g();
|
std::thread
|
||||||
t2.join();
|
f()
|
||||||
|
{
|
||||||
|
void some_function();
|
||||||
|
return std::thread(some_function);
|
||||||
|
}
|
||||||
|
std::thread
|
||||||
|
g()
|
||||||
|
{
|
||||||
|
void some_other_function(int);
|
||||||
|
std::thread t(some_other_function, 42);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
std::thread t1 = f();
|
||||||
|
t1.join();
|
||||||
|
std::thread t2 = g();
|
||||||
|
t2.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,55 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
class scoped_thread
|
class scoped_thread {
|
||||||
{
|
std::thread t;
|
||||||
std::thread t;
|
|
||||||
public:
|
public:
|
||||||
explicit scoped_thread(std::thread t_):
|
explicit scoped_thread(std::thread t_) : t(std::move(t_))
|
||||||
t(std::move(t_))
|
{
|
||||||
{
|
if (!t.joinable())
|
||||||
if(!t.joinable())
|
throw std::logic_error("No thread");
|
||||||
throw std::logic_error("No thread");
|
}
|
||||||
}
|
~scoped_thread() { t.join(); }
|
||||||
~scoped_thread()
|
scoped_thread(scoped_thread const&) = delete;
|
||||||
{
|
scoped_thread& operator=(scoped_thread const&) = delete;
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
scoped_thread(scoped_thread const&)=delete;
|
|
||||||
scoped_thread& operator=(scoped_thread const&)=delete;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
void do_something(int& i)
|
void
|
||||||
|
do_something(int& i)
|
||||||
{
|
{
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct func
|
struct func {
|
||||||
{
|
int& i;
|
||||||
int& i;
|
|
||||||
|
|
||||||
func(int& i_):i(i_){}
|
func(int& i_) : i(i_) {}
|
||||||
|
|
||||||
void operator()()
|
void operator()()
|
||||||
{
|
{
|
||||||
for(unsigned j=0;j<1000000;++j)
|
for (unsigned j = 0; j < 1000000; ++j) {
|
||||||
{
|
do_something(i);
|
||||||
do_something(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void do_something_in_current_thread()
|
void
|
||||||
{}
|
do_something_in_current_thread()
|
||||||
|
|
||||||
void f()
|
|
||||||
{
|
{
|
||||||
int some_local_state;
|
|
||||||
scoped_thread t(std::thread(func(some_local_state)));
|
|
||||||
|
|
||||||
do_something_in_current_thread();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
void
|
||||||
|
f()
|
||||||
{
|
{
|
||||||
f();
|
int some_local_state;
|
||||||
|
scoped_thread t(std::thread(func(some_local_state)));
|
||||||
|
|
||||||
|
do_something_in_current_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
f();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
#include <vector>
|
|
||||||
#include <thread>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
void do_work(unsigned id)
|
void
|
||||||
{}
|
do_work(unsigned id)
|
||||||
|
|
||||||
void f()
|
|
||||||
{
|
{
|
||||||
std::vector<std::thread> threads;
|
|
||||||
for(unsigned i=0;i<20;++i)
|
|
||||||
{
|
|
||||||
threads.push_back(std::thread(do_work,i));
|
|
||||||
}
|
|
||||||
std::for_each(threads.begin(),threads.end(),
|
|
||||||
std::mem_fn(&std::thread::join));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
void
|
||||||
|
f()
|
||||||
{
|
{
|
||||||
f();
|
std::vector<std::thread> threads;
|
||||||
|
for (unsigned i = 0; i < 20; ++i) {
|
||||||
|
threads.push_back(std::thread(do_work, i));
|
||||||
|
}
|
||||||
|
std::for_each(
|
||||||
|
threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
f();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +1,66 @@
|
|||||||
#include <thread>
|
|
||||||
#include <numeric>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <numeric>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
template<typename Iterator,typename T>
|
template <typename Iterator, typename T>
|
||||||
struct accumulate_block
|
struct accumulate_block {
|
||||||
{
|
void operator()(Iterator first, Iterator last, T& result)
|
||||||
void operator()(Iterator first,Iterator last,T& result)
|
{
|
||||||
{
|
result = std::accumulate(first, last, result);
|
||||||
result=std::accumulate(first,last,result);
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Iterator,typename T>
|
template <typename Iterator, typename T>
|
||||||
T parallel_accumulate(Iterator first,Iterator last,T init)
|
T
|
||||||
|
parallel_accumulate(Iterator first, Iterator last, T init)
|
||||||
{
|
{
|
||||||
unsigned long const length=std::distance(first,last);
|
unsigned long const length = std::distance(first, last);
|
||||||
|
|
||||||
if(!length)
|
if (!length)
|
||||||
return init;
|
return init;
|
||||||
|
|
||||||
unsigned long const min_per_thread=25;
|
unsigned long const min_per_thread = 25;
|
||||||
unsigned long const max_threads=
|
unsigned long const max_threads =
|
||||||
(length+min_per_thread-1)/min_per_thread;
|
(length + min_per_thread - 1) / min_per_thread;
|
||||||
|
|
||||||
unsigned long const hardware_threads=
|
unsigned long const hardware_threads = std::thread::hardware_concurrency();
|
||||||
std::thread::hardware_concurrency();
|
|
||||||
|
|
||||||
unsigned long const num_threads=
|
unsigned long const num_threads =
|
||||||
std::min(hardware_threads!=0?hardware_threads:2,max_threads);
|
std::min(hardware_threads != 0 ? hardware_threads : 2, max_threads);
|
||||||
|
|
||||||
unsigned long const block_size=length/num_threads;
|
unsigned long const block_size = length / num_threads;
|
||||||
|
|
||||||
std::vector<T> results(num_threads);
|
std::vector<T> results(num_threads);
|
||||||
std::vector<std::thread> threads(num_threads-1);
|
std::vector<std::thread> threads(num_threads - 1);
|
||||||
|
|
||||||
Iterator block_start=first;
|
Iterator block_start = first;
|
||||||
for(unsigned long i=0;i<(num_threads-1);++i)
|
for (unsigned long i = 0; i < (num_threads - 1); ++i) {
|
||||||
{
|
Iterator block_end = block_start;
|
||||||
Iterator block_end=block_start;
|
std::advance(block_end, block_size);
|
||||||
std::advance(block_end,block_size);
|
threads[i] = std::thread(accumulate_block<Iterator, T>(),
|
||||||
threads[i]=std::thread(
|
block_start,
|
||||||
accumulate_block<Iterator,T>(),
|
block_end,
|
||||||
block_start,block_end,std::ref(results[i]));
|
std::ref(results[i]));
|
||||||
block_start=block_end;
|
block_start = block_end;
|
||||||
}
|
}
|
||||||
accumulate_block<Iterator,T>()(block_start,last,results[num_threads-1]);
|
accumulate_block<Iterator, T>()(block_start, last, results[num_threads - 1]);
|
||||||
|
|
||||||
std::for_each(threads.begin(),threads.end(),
|
|
||||||
std::mem_fn(&std::thread::join));
|
|
||||||
|
|
||||||
return std::accumulate(results.begin(),results.end(),init);
|
std::for_each(
|
||||||
|
threads.begin(), threads.end(), std::mem_fn(&std::thread::join));
|
||||||
|
|
||||||
|
return std::accumulate(results.begin(), results.end(), init);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
std::vector<int> vi;
|
std::vector<int> vi;
|
||||||
for(int i=0;i<10;++i)
|
for (int i = 0; i < 10; ++i) {
|
||||||
{
|
vi.push_back(10);
|
||||||
vi.push_back(10);
|
}
|
||||||
}
|
int sum = parallel_accumulate(vi.begin(), vi.end(), 5);
|
||||||
int sum=parallel_accumulate(vi.begin(),vi.end(),5);
|
std::cout << "sum=" << sum << std::endl;
|
||||||
std::cout<<"sum="<<sum<<std::endl;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,30 @@
|
|||||||
|
#include <algorithm>
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
std::list<int> some_list;
|
std::list<int> some_list;
|
||||||
std::mutex some_mutex;
|
std::mutex some_mutex;
|
||||||
|
|
||||||
void add_to_list(int new_value)
|
void
|
||||||
|
add_to_list(int new_value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(some_mutex);
|
std::lock_guard<std::mutex> guard(some_mutex);
|
||||||
some_list.push_back(new_value);
|
some_list.push_back(new_value);
|
||||||
}
|
}
|
||||||
bool list_contains(int value_to_find)
|
bool
|
||||||
|
list_contains(int value_to_find)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> guard(some_mutex);
|
std::lock_guard<std::mutex> guard(some_mutex);
|
||||||
return std::find(some_list.begin(),some_list.end(),value_to_find)
|
return std::find(some_list.begin(), some_list.end(), value_to_find) !=
|
||||||
!= some_list.end();
|
some_list.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
add_to_list(42);
|
add_to_list(42);
|
||||||
std::cout<<"contains(1)="<<list_contains(1)<<", contains(42)="<<list_contains(42)<<std::endl;
|
std::cout << "contains(1)=" << list_contains(1)
|
||||||
|
<< ", contains(42)=" << list_contains(42) << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,29 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
class Y
|
class Y {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
int some_detail;
|
int some_detail;
|
||||||
mutable std::mutex m;
|
mutable std::mutex m;
|
||||||
|
|
||||||
|
int get_detail() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock_a(m);
|
||||||
|
return some_detail;
|
||||||
|
}
|
||||||
|
|
||||||
int get_detail() const
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock_a(m);
|
|
||||||
return some_detail;
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
Y(int sd):some_detail(sd){}
|
Y(int sd) : some_detail(sd) {}
|
||||||
|
|
||||||
friend bool operator==(Y const& lhs, Y const& rhs)
|
friend bool operator==(Y const& lhs, Y const& rhs)
|
||||||
{
|
{
|
||||||
if(&lhs==&rhs)
|
if (&lhs == &rhs)
|
||||||
return true;
|
return true;
|
||||||
int const lhs_value=lhs.get_detail();
|
int const lhs_value = lhs.get_detail();
|
||||||
int const rhs_value=rhs.get_detail();
|
int const rhs_value = rhs.get_detail();
|
||||||
return lhs_value==rhs_value;
|
return lhs_value == rhs_value;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
{}
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,28 +1,25 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
struct some_resource
|
struct some_resource {
|
||||||
{
|
void do_something() {}
|
||||||
void do_something()
|
|
||||||
{}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<some_resource> resource_ptr;
|
std::shared_ptr<some_resource> resource_ptr;
|
||||||
std::mutex resource_mutex;
|
std::mutex resource_mutex;
|
||||||
void foo()
|
void
|
||||||
|
foo()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(resource_mutex);
|
std::unique_lock<std::mutex> lk(resource_mutex);
|
||||||
if(!resource_ptr)
|
if (!resource_ptr) {
|
||||||
{
|
resource_ptr.reset(new some_resource);
|
||||||
resource_ptr.reset(new some_resource);
|
}
|
||||||
}
|
lk.unlock();
|
||||||
lk.unlock();
|
resource_ptr->do_something();
|
||||||
resource_ptr->do_something();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
foo();
|
foo();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,49 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
struct connection_info
|
struct connection_info {
|
||||||
{};
|
|
||||||
|
|
||||||
struct data_packet
|
|
||||||
{};
|
|
||||||
|
|
||||||
struct connection_handle
|
|
||||||
{
|
|
||||||
void send_data(data_packet const&)
|
|
||||||
{}
|
|
||||||
data_packet receive_data()
|
|
||||||
{
|
|
||||||
return data_packet();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct remote_connection_manager
|
struct data_packet {
|
||||||
{
|
};
|
||||||
connection_handle open(connection_info const&)
|
|
||||||
{
|
struct connection_handle {
|
||||||
return connection_handle();
|
void send_data(data_packet const&) {}
|
||||||
}
|
data_packet receive_data() { return data_packet(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct remote_connection_manager {
|
||||||
|
connection_handle open(connection_info const&) { return connection_handle(); }
|
||||||
} connection_manager;
|
} connection_manager;
|
||||||
|
|
||||||
|
class X {
|
||||||
class X
|
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
connection_info connection_details;
|
connection_info connection_details;
|
||||||
connection_handle connection;
|
connection_handle connection;
|
||||||
std::once_flag connection_init_flag;
|
std::once_flag connection_init_flag;
|
||||||
|
|
||||||
|
void open_connection()
|
||||||
|
{
|
||||||
|
connection = connection_manager.open(connection_details);
|
||||||
|
}
|
||||||
|
|
||||||
void open_connection()
|
|
||||||
{
|
|
||||||
connection=connection_manager.open(connection_details);
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
X(connection_info const& connection_details_):
|
X(connection_info const& connection_details_)
|
||||||
connection_details(connection_details_)
|
: connection_details(connection_details_)
|
||||||
{}
|
{
|
||||||
void send_data(data_packet const& data)
|
}
|
||||||
{
|
void send_data(data_packet const& data)
|
||||||
std::call_once(connection_init_flag,&X::open_connection,this);
|
{
|
||||||
connection.send_data(data);
|
std::call_once(connection_init_flag, &X::open_connection, this);
|
||||||
}
|
connection.send_data(data);
|
||||||
data_packet receive_data()
|
}
|
||||||
{
|
data_packet receive_data()
|
||||||
std::call_once(connection_init_flag,&X::open_connection,this);
|
{
|
||||||
return connection.receive_data();
|
std::call_once(connection_init_flag, &X::open_connection, this);
|
||||||
}
|
return connection.receive_data();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
{}
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,30 +1,32 @@
|
|||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <mutex>
|
|
||||||
#include <boost/thread/shared_mutex.hpp>
|
#include <boost/thread/shared_mutex.hpp>
|
||||||
|
#include <map>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class dns_entry
|
class dns_entry {
|
||||||
{};
|
|
||||||
|
|
||||||
class dns_cache
|
|
||||||
{
|
|
||||||
std::map<std::string,dns_entry> entries;
|
|
||||||
boost::shared_mutex entry_mutex;
|
|
||||||
public:
|
|
||||||
dns_entry find_entry(std::string const& domain)
|
|
||||||
{
|
|
||||||
boost::shared_lock<boost::shared_mutex> lk(entry_mutex);
|
|
||||||
std::map<std::string,dns_entry>::const_iterator const it=
|
|
||||||
entries.find(domain);
|
|
||||||
return (it==entries.end())?dns_entry():it->second;
|
|
||||||
}
|
|
||||||
void update_or_add_entry(std::string const& domain,
|
|
||||||
dns_entry const& dns_details)
|
|
||||||
{
|
|
||||||
std::lock_guard<boost::shared_mutex> lk(entry_mutex);
|
|
||||||
entries[domain]=dns_details;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
class dns_cache {
|
||||||
{}
|
std::map<std::string, dns_entry> entries;
|
||||||
|
boost::shared_mutex entry_mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
dns_entry find_entry(std::string const& domain)
|
||||||
|
{
|
||||||
|
boost::shared_lock<boost::shared_mutex> lk(entry_mutex);
|
||||||
|
std::map<std::string, dns_entry>::const_iterator const it =
|
||||||
|
entries.find(domain);
|
||||||
|
return (it == entries.end()) ? dns_entry() : it->second;
|
||||||
|
}
|
||||||
|
void update_or_add_entry(std::string const& domain,
|
||||||
|
dns_entry const& dns_details)
|
||||||
|
{
|
||||||
|
std::lock_guard<boost::shared_mutex> lk(entry_mutex);
|
||||||
|
entries[domain] = dns_details;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,44 +1,46 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
class some_data
|
class some_data {
|
||||||
{
|
int a;
|
||||||
int a;
|
std::string b;
|
||||||
std::string b;
|
|
||||||
public:
|
public:
|
||||||
void do_something()
|
void do_something() {}
|
||||||
{}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class data_wrapper
|
class data_wrapper {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
some_data data;
|
some_data data;
|
||||||
std::mutex m;
|
std::mutex m;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename Function>
|
template <typename Function>
|
||||||
void process_data(Function func)
|
void process_data(Function func)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> l(m);
|
std::lock_guard<std::mutex> l(m);
|
||||||
func(data);
|
func(data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
some_data* unprotected;
|
some_data* unprotected;
|
||||||
|
|
||||||
void malicious_function(some_data& protected_data)
|
void
|
||||||
|
malicious_function(some_data& protected_data)
|
||||||
{
|
{
|
||||||
unprotected=&protected_data;
|
unprotected = &protected_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
data_wrapper x;
|
data_wrapper x;
|
||||||
|
|
||||||
void foo()
|
void
|
||||||
|
foo()
|
||||||
{
|
{
|
||||||
x.process_data(malicious_function);
|
x.process_data(malicious_function);
|
||||||
unprotected->do_something();
|
unprotected->do_something();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
foo();
|
foo();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,29 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
template<typename T,typename Container=std::deque<T> >
|
template <typename T, typename Container = std::deque<T>>
|
||||||
class stack
|
class stack {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
explicit stack(const Container&);
|
explicit stack(const Container&);
|
||||||
explicit stack(Container&& = Container());
|
explicit stack(Container&& = Container());
|
||||||
template <class Alloc> explicit stack(const Alloc&);
|
template <class Alloc>
|
||||||
template <class Alloc> stack(const Container&, const Alloc&);
|
explicit stack(const Alloc&);
|
||||||
template <class Alloc> stack(Container&&, const Alloc&);
|
template <class Alloc>
|
||||||
template <class Alloc> stack(stack&&, const Alloc&);
|
stack(const Container&, const Alloc&);
|
||||||
|
template <class Alloc>
|
||||||
|
stack(Container&&, const Alloc&);
|
||||||
|
template <class Alloc>
|
||||||
|
stack(stack&&, const Alloc&);
|
||||||
|
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
size_t size() const;
|
size_t size() const;
|
||||||
T& top();
|
T& top();
|
||||||
T const& top() const;
|
T const& top() const;
|
||||||
void push(T const&);
|
void push(T const&);
|
||||||
void push(T&&);
|
void push(T&&);
|
||||||
void pop();
|
void pop();
|
||||||
void swap(stack&&);
|
void swap(stack&&);
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
{}
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
struct empty_stack: std::exception
|
struct empty_stack : std::exception {
|
||||||
{
|
const char* what() const throw();
|
||||||
const char* what() const throw();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_stack
|
class threadsafe_stack {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
threadsafe_stack();
|
threadsafe_stack();
|
||||||
threadsafe_stack(const threadsafe_stack&);
|
threadsafe_stack(const threadsafe_stack&);
|
||||||
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
|
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
|
||||||
|
|
||||||
void push(T new_value);
|
void push(T new_value);
|
||||||
std::shared_ptr<T> pop();
|
std::shared_ptr<T> pop();
|
||||||
void pop(T& value);
|
void pop(T& value);
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
{}
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,68 +1,64 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stack>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
struct empty_stack: std::exception
|
struct empty_stack : std::exception {
|
||||||
{
|
const char* what() const throw() { return "empty stack"; }
|
||||||
const char* what() const throw()
|
|
||||||
{
|
|
||||||
return "empty stack";
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_stack
|
class threadsafe_stack {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
std::stack<T> data;
|
std::stack<T> data;
|
||||||
mutable std::mutex m;
|
mutable std::mutex m;
|
||||||
public:
|
|
||||||
threadsafe_stack(){}
|
|
||||||
threadsafe_stack(const threadsafe_stack& other)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(other.m);
|
|
||||||
data=other.data;
|
|
||||||
}
|
|
||||||
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
|
|
||||||
|
|
||||||
void push(T new_value)
|
public:
|
||||||
{
|
threadsafe_stack() {}
|
||||||
std::lock_guard<std::mutex> lock(m);
|
threadsafe_stack(const threadsafe_stack& other)
|
||||||
data.push(new_value);
|
{
|
||||||
}
|
std::lock_guard<std::mutex> lock(other.m);
|
||||||
std::shared_ptr<T> pop()
|
data = other.data;
|
||||||
{
|
}
|
||||||
std::lock_guard<std::mutex> lock(m);
|
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
|
||||||
if(data.empty()) throw empty_stack();
|
|
||||||
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
|
void push(T new_value)
|
||||||
data.pop();
|
{
|
||||||
return res;
|
std::lock_guard<std::mutex> lock(m);
|
||||||
}
|
data.push(new_value);
|
||||||
void pop(T& value)
|
}
|
||||||
{
|
std::shared_ptr<T> pop()
|
||||||
std::lock_guard<std::mutex> lock(m);
|
{
|
||||||
if(data.empty()) throw empty_stack();
|
std::lock_guard<std::mutex> lock(m);
|
||||||
value=data.top();
|
if (data.empty())
|
||||||
data.pop();
|
throw empty_stack();
|
||||||
}
|
std::shared_ptr<T> const res(std::make_shared<T>(data.top()));
|
||||||
bool empty() const
|
data.pop();
|
||||||
{
|
return res;
|
||||||
std::lock_guard<std::mutex> lock(m);
|
}
|
||||||
return data.empty();
|
void pop(T& value)
|
||||||
}
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
if (data.empty())
|
||||||
|
throw empty_stack();
|
||||||
|
value = data.top();
|
||||||
|
data.pop();
|
||||||
|
}
|
||||||
|
bool empty() const
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(m);
|
||||||
|
return data.empty();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
threadsafe_stack<int> si;
|
threadsafe_stack<int> si;
|
||||||
si.push(5);
|
si.push(5);
|
||||||
si.pop();
|
si.pop();
|
||||||
if(!si.empty())
|
if (!si.empty()) {
|
||||||
{
|
int x;
|
||||||
int x;
|
si.pop(x);
|
||||||
si.pop(x);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,33 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
class some_big_object
|
class some_big_object {
|
||||||
{};
|
|
||||||
|
|
||||||
void swap(some_big_object& lhs,some_big_object& rhs)
|
|
||||||
{}
|
|
||||||
|
|
||||||
class X
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
some_big_object some_detail;
|
|
||||||
mutable std::mutex m;
|
|
||||||
public:
|
|
||||||
X(some_big_object const& sd):some_detail(sd){}
|
|
||||||
|
|
||||||
friend void swap(X& lhs, X& rhs)
|
|
||||||
{
|
|
||||||
if(&lhs==&rhs)
|
|
||||||
return;
|
|
||||||
std::lock(lhs.m,rhs.m);
|
|
||||||
std::lock_guard<std::mutex> lock_a(lhs.m,std::adopt_lock);
|
|
||||||
std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock);
|
|
||||||
swap(lhs.some_detail,rhs.some_detail);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
void
|
||||||
{}
|
swap(some_big_object& lhs, some_big_object& rhs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class X {
|
||||||
|
private:
|
||||||
|
some_big_object some_detail;
|
||||||
|
mutable std::mutex m;
|
||||||
|
|
||||||
|
public:
|
||||||
|
X(some_big_object const& sd) : some_detail(sd) {}
|
||||||
|
|
||||||
|
friend void swap(X& lhs, X& rhs)
|
||||||
|
{
|
||||||
|
if (&lhs == &rhs)
|
||||||
|
return;
|
||||||
|
std::lock(lhs.m, rhs.m);
|
||||||
|
std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);
|
||||||
|
std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);
|
||||||
|
swap(lhs.some_detail, rhs.some_detail);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,64 +1,68 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
class hierarchical_mutex
|
class hierarchical_mutex {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
explicit hierarchical_mutex(unsigned level)
|
explicit hierarchical_mutex(unsigned level) {}
|
||||||
{}
|
|
||||||
|
|
||||||
void lock()
|
|
||||||
{}
|
|
||||||
void unlock()
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
void lock() {}
|
||||||
|
void unlock() {}
|
||||||
|
};
|
||||||
|
|
||||||
hierarchical_mutex high_level_mutex(10000);
|
hierarchical_mutex high_level_mutex(10000);
|
||||||
hierarchical_mutex low_level_mutex(5000);
|
hierarchical_mutex low_level_mutex(5000);
|
||||||
|
|
||||||
int do_low_level_stuff()
|
int
|
||||||
|
do_low_level_stuff()
|
||||||
{
|
{
|
||||||
return 42;
|
return 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
int low_level_func()
|
low_level_func()
|
||||||
{
|
{
|
||||||
std::lock_guard<hierarchical_mutex> lk(low_level_mutex);
|
std::lock_guard<hierarchical_mutex> lk(low_level_mutex);
|
||||||
return do_low_level_stuff();
|
return do_low_level_stuff();
|
||||||
}
|
}
|
||||||
|
|
||||||
void high_level_stuff(int some_param)
|
void
|
||||||
{}
|
high_level_stuff(int some_param)
|
||||||
|
|
||||||
|
|
||||||
void high_level_func()
|
|
||||||
{
|
{
|
||||||
std::lock_guard<hierarchical_mutex> lk(high_level_mutex);
|
|
||||||
high_level_stuff(low_level_func());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void thread_a()
|
void
|
||||||
|
high_level_func()
|
||||||
{
|
{
|
||||||
high_level_func();
|
std::lock_guard<hierarchical_mutex> lk(high_level_mutex);
|
||||||
|
high_level_stuff(low_level_func());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
thread_a()
|
||||||
|
{
|
||||||
|
high_level_func();
|
||||||
}
|
}
|
||||||
|
|
||||||
hierarchical_mutex other_mutex(100);
|
hierarchical_mutex other_mutex(100);
|
||||||
void do_other_stuff()
|
void
|
||||||
{}
|
do_other_stuff()
|
||||||
|
|
||||||
|
|
||||||
void other_stuff()
|
|
||||||
{
|
{
|
||||||
high_level_func();
|
|
||||||
do_other_stuff();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void thread_b()
|
void
|
||||||
|
other_stuff()
|
||||||
{
|
{
|
||||||
std::lock_guard<hierarchical_mutex> lk(other_mutex);
|
high_level_func();
|
||||||
other_stuff();
|
do_other_stuff();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
void
|
||||||
{}
|
thread_b()
|
||||||
|
{
|
||||||
|
std::lock_guard<hierarchical_mutex> lk(other_mutex);
|
||||||
|
other_stuff();
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,56 +1,55 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
class hierarchical_mutex
|
class hierarchical_mutex {
|
||||||
{
|
std::mutex internal_mutex;
|
||||||
std::mutex internal_mutex;
|
unsigned long const hierarchy_value;
|
||||||
unsigned long const hierarchy_value;
|
unsigned long previous_hierarchy_value;
|
||||||
unsigned long previous_hierarchy_value;
|
static thread_local unsigned long this_thread_hierarchy_value;
|
||||||
static thread_local unsigned long this_thread_hierarchy_value;
|
|
||||||
|
|
||||||
void check_for_hierarchy_violation()
|
void check_for_hierarchy_violation()
|
||||||
{
|
{
|
||||||
if(this_thread_hierarchy_value <= hierarchy_value)
|
if (this_thread_hierarchy_value <= hierarchy_value) {
|
||||||
{
|
throw std::logic_error("mutex hierarchy violated");
|
||||||
throw std::logic_error("mutex hierarchy violated");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void update_hierarchy_value()
|
|
||||||
{
|
|
||||||
previous_hierarchy_value=this_thread_hierarchy_value;
|
|
||||||
this_thread_hierarchy_value=hierarchy_value;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
void update_hierarchy_value()
|
||||||
|
{
|
||||||
|
previous_hierarchy_value = this_thread_hierarchy_value;
|
||||||
|
this_thread_hierarchy_value = hierarchy_value;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit hierarchical_mutex(unsigned long value):
|
explicit hierarchical_mutex(unsigned long value)
|
||||||
hierarchy_value(value),
|
: hierarchy_value(value), previous_hierarchy_value(0)
|
||||||
previous_hierarchy_value(0)
|
{
|
||||||
{}
|
}
|
||||||
void lock()
|
void lock()
|
||||||
{
|
{
|
||||||
check_for_hierarchy_violation();
|
check_for_hierarchy_violation();
|
||||||
internal_mutex.lock();
|
internal_mutex.lock();
|
||||||
update_hierarchy_value();
|
update_hierarchy_value();
|
||||||
}
|
}
|
||||||
void unlock()
|
void unlock()
|
||||||
{
|
{
|
||||||
this_thread_hierarchy_value=previous_hierarchy_value;
|
this_thread_hierarchy_value = previous_hierarchy_value;
|
||||||
internal_mutex.unlock();
|
internal_mutex.unlock();
|
||||||
}
|
}
|
||||||
bool try_lock()
|
bool try_lock()
|
||||||
{
|
{
|
||||||
check_for_hierarchy_violation();
|
check_for_hierarchy_violation();
|
||||||
if(!internal_mutex.try_lock())
|
if (!internal_mutex.try_lock())
|
||||||
return false;
|
return false;
|
||||||
update_hierarchy_value();
|
update_hierarchy_value();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
thread_local unsigned long
|
thread_local unsigned long hierarchical_mutex::this_thread_hierarchy_value(
|
||||||
hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);
|
ULONG_MAX);
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
hierarchical_mutex m1(42);
|
hierarchical_mutex m1(42);
|
||||||
hierarchical_mutex m2(2000);
|
hierarchical_mutex m2(2000);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,33 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
class some_big_object
|
class some_big_object {
|
||||||
{};
|
|
||||||
|
|
||||||
void swap(some_big_object& lhs,some_big_object& rhs)
|
|
||||||
{}
|
|
||||||
|
|
||||||
class X
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
some_big_object some_detail;
|
|
||||||
mutable std::mutex m;
|
|
||||||
public:
|
|
||||||
X(some_big_object const& sd):some_detail(sd){}
|
|
||||||
|
|
||||||
friend void swap(X& lhs, X& rhs)
|
|
||||||
{
|
|
||||||
if(&lhs==&rhs)
|
|
||||||
return;
|
|
||||||
std::unique_lock<std::mutex> lock_a(lhs.m,std::defer_lock);
|
|
||||||
std::unique_lock<std::mutex> lock_b(rhs.m,std::defer_lock);
|
|
||||||
std::lock(lock_a,lock_b);
|
|
||||||
swap(lhs.some_detail,rhs.some_detail);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
void
|
||||||
{}
|
swap(some_big_object& lhs, some_big_object& rhs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
class X {
|
||||||
|
private:
|
||||||
|
some_big_object some_detail;
|
||||||
|
mutable std::mutex m;
|
||||||
|
|
||||||
|
public:
|
||||||
|
X(some_big_object const& sd) : some_detail(sd) {}
|
||||||
|
|
||||||
|
friend void swap(X& lhs, X& rhs)
|
||||||
|
{
|
||||||
|
if (&lhs == &rhs)
|
||||||
|
return;
|
||||||
|
std::unique_lock<std::mutex> lock_a(lhs.m, std::defer_lock);
|
||||||
|
std::unique_lock<std::mutex> lock_b(rhs.m, std::defer_lock);
|
||||||
|
std::lock(lock_a, lock_b);
|
||||||
|
swap(lhs.some_detail, rhs.some_detail);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,64 +1,70 @@
|
|||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <thread>
|
#include <mutex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
bool more_data_to_prepare()
|
bool
|
||||||
|
more_data_to_prepare()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct data_chunk
|
struct data_chunk {
|
||||||
{};
|
};
|
||||||
|
|
||||||
data_chunk prepare_data()
|
data_chunk
|
||||||
|
prepare_data()
|
||||||
{
|
{
|
||||||
return data_chunk();
|
return data_chunk();
|
||||||
}
|
}
|
||||||
|
|
||||||
void process(data_chunk&)
|
void
|
||||||
{}
|
process(data_chunk&)
|
||||||
|
|
||||||
bool is_last_chunk(data_chunk&)
|
|
||||||
{
|
{
|
||||||
return true;
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
is_last_chunk(data_chunk&)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::mutex mut;
|
std::mutex mut;
|
||||||
std::queue<data_chunk> data_queue;
|
std::queue<data_chunk> data_queue;
|
||||||
std::condition_variable data_cond;
|
std::condition_variable data_cond;
|
||||||
|
|
||||||
void data_preparation_thread()
|
void
|
||||||
|
data_preparation_thread()
|
||||||
{
|
{
|
||||||
while(more_data_to_prepare())
|
while (more_data_to_prepare()) {
|
||||||
{
|
data_chunk const data = prepare_data();
|
||||||
data_chunk const data=prepare_data();
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
data_queue.push(data);
|
||||||
data_queue.push(data);
|
data_cond.notify_one();
|
||||||
data_cond.notify_one();
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void data_processing_thread()
|
void
|
||||||
|
data_processing_thread()
|
||||||
{
|
{
|
||||||
while(true)
|
while (true) {
|
||||||
{
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
data_cond.wait(lk, [] { return !data_queue.empty(); });
|
||||||
data_cond.wait(lk,[]{return !data_queue.empty();});
|
data_chunk data = data_queue.front();
|
||||||
data_chunk data=data_queue.front();
|
data_queue.pop();
|
||||||
data_queue.pop();
|
lk.unlock();
|
||||||
lk.unlock();
|
process(data);
|
||||||
process(data);
|
if (is_last_chunk(data))
|
||||||
if(is_last_chunk(data))
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
std::thread t1(data_preparation_thread);
|
std::thread t1(data_preparation_thread);
|
||||||
std::thread t2(data_processing_thread);
|
std::thread t2(data_processing_thread);
|
||||||
|
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +1,22 @@
|
|||||||
#include <future>
|
#include <future>
|
||||||
void process_connections(connection_set& connections)
|
void
|
||||||
|
process_connections(connection_set& connections)
|
||||||
{
|
{
|
||||||
while(!done(connections))
|
while (!done(connections)) {
|
||||||
{
|
for (connection_iterator connection = connections.begin(),
|
||||||
for(connection_iterator
|
end = connections.end();
|
||||||
connection=connections.begin(),end=connections.end();
|
connection != end;
|
||||||
connection!=end;
|
++connection) {
|
||||||
++connection)
|
if (connection->has_incoming_data()) {
|
||||||
{
|
data_packet data = connection->incoming();
|
||||||
if(connection->has_incoming_data())
|
std::promise<payload_type>& p = connection->get_promise(data.id);
|
||||||
{
|
p.set_value(data.payload);
|
||||||
data_packet data=connection->incoming();
|
}
|
||||||
std::promise<payload_type>& p=
|
if (connection->has_outgoing_data()) {
|
||||||
connection->get_promise(data.id);
|
outgoing_packet data = connection->top_of_outgoing_queue();
|
||||||
p.set_value(data.payload);
|
connection->send(data.payload);
|
||||||
}
|
data.promise.set_value(true);
|
||||||
if(connection->has_outgoing_data())
|
}
|
||||||
{
|
|
||||||
outgoing_packet data=
|
|
||||||
connection->top_of_outgoing_queue();
|
|
||||||
connection->send(data.payload);
|
|
||||||
data.promise.set_value(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <chrono>
|
|
||||||
std::condition_variable cv;
|
std::condition_variable cv;
|
||||||
bool done;
|
bool done;
|
||||||
std::mutex m;
|
std::mutex m;
|
||||||
bool wait_loop()
|
bool
|
||||||
|
wait_loop()
|
||||||
{
|
{
|
||||||
auto const timeout= std::chrono::steady_clock::now()+
|
auto const timeout =
|
||||||
std::chrono::milliseconds(500);
|
std::chrono::steady_clock::now() + std::chrono::milliseconds(500);
|
||||||
std::unique_lock<std::mutex> lk(m);
|
std::unique_lock<std::mutex> lk(m);
|
||||||
while(!done)
|
while (!done) {
|
||||||
{
|
if (cv.wait_until(lk, timeout) == std::cv_status::timeout)
|
||||||
if(cv.wait_until(lk,timeout)==std::cv_status::timeout)
|
break;
|
||||||
break;
|
}
|
||||||
}
|
return done;
|
||||||
return done;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,21 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
std::list<T> sequential_quick_sort(std::list<T> input)
|
std::list<T>
|
||||||
|
sequential_quick_sort(std::list<T> input)
|
||||||
{
|
{
|
||||||
if(input.empty())
|
if (input.empty()) {
|
||||||
{
|
return input;
|
||||||
return input;
|
}
|
||||||
}
|
std::list<T> result;
|
||||||
std::list<T> result;
|
result.splice(result.begin(), input, input.begin());
|
||||||
result.splice(result.begin(),input,input.begin());
|
T const& pivot = *result.begin();
|
||||||
T const& pivot=*result.begin();
|
auto divide_point = std::partition(
|
||||||
auto divide_point=std::partition(input.begin(),input.end(),
|
input.begin(), input.end(), [&](T const& t) { return t < pivot; });
|
||||||
[&](T const& t){return t<pivot;});
|
std::list<T> lower_part;
|
||||||
std::list<T> lower_part;
|
lower_part.splice(lower_part.end(), input, input.begin(), divide_point);
|
||||||
lower_part.splice(lower_part.end(),input,input.begin(),
|
auto new_lower(sequential_quick_sort(std::move(lower_part)));
|
||||||
divide_point);
|
auto new_higher(sequential_quick_sort(std::move(input)));
|
||||||
auto new_lower(
|
result.splice(result.end(), new_higher);
|
||||||
sequential_quick_sort(std::move(lower_part)));
|
Using synchronization of operations to simplify code result.splice(
|
||||||
auto new_higher(
|
result.begin(), new_lower);
|
||||||
sequential_quick_sort(std::move(input)));
|
return result;
|
||||||
result.splice(result.end(),new_higher);
|
|
||||||
Using synchronization of operations to simplify code
|
|
||||||
result.splice(result.begin(),new_lower);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
std::list<T> parallel_quick_sort(std::list<T> input)
|
std::list<T>
|
||||||
|
parallel_quick_sort(std::list<T> input)
|
||||||
{
|
{
|
||||||
if(input.empty())
|
if (input.empty()) {
|
||||||
{
|
return input;
|
||||||
return input;
|
}
|
||||||
}
|
std::list<T> result;
|
||||||
std::list<T> result;
|
result.splice(result.begin(), input, input.begin());
|
||||||
result.splice(result.begin(),input,input.begin());
|
T const& pivot = *result.begin();
|
||||||
T const& pivot=*result.begin();
|
auto divide_point = std::partition(
|
||||||
auto divide_point=std::partition(input.begin(),input.end(),
|
input.begin(), input.end(), [&](T const& t) { return t < pivot; });
|
||||||
[&](T const& t){return t<pivot;});
|
std::list<T> lower_part;
|
||||||
std::list<T> lower_part;
|
lower_part.splice(lower_part.end(), input, input.begin(), divide_point);
|
||||||
lower_part.splice(lower_part.end(),input,input.begin(),
|
std::future<std::list<T>> new_lower(
|
||||||
divide_point);
|
std::async(¶llel_quick_sort<T>, std::move(lower_part)));
|
||||||
std::future<std::list<T> > new_lower(
|
auto new_higher(parallel_quick_sort(std::move(input)));
|
||||||
std::async(¶llel_quick_sort<T>,std::move(lower_part)));
|
result.splice(result.end(), new_higher);
|
||||||
auto new_higher(
|
result.splice(result.begin(), new_lower.get());
|
||||||
parallel_quick_sort(std::move(input)));
|
return result;
|
||||||
result.splice(result.end(),new_higher);
|
|
||||||
result.splice(result.begin(),new_lower.get());
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
template<typename F,typename A>
|
template <typename F, typename A>
|
||||||
std::future<std::result_of<F(A&&)>::type>
|
std::future<std::result_of<F(A&&)>::type>
|
||||||
spawn_task(F&& f,A&& a)
|
spawn_task(F&& f, A&& a)
|
||||||
{
|
{
|
||||||
typedef std::result_of<F(A&&)>::type result_type;
|
typedef std::result_of<F(A &&)>::type result_type;
|
||||||
std::packaged_task<result_type(A&&)>
|
std::packaged_task<result_type(A &&)> task(std::move(f));
|
||||||
task(std::move(f));
|
std::future<result_type> res(task.get_future());
|
||||||
std::future<result_type> res(task.get_future());
|
std::thread t(std::move(task), std::move(a));
|
||||||
std::thread t(std::move(task),std::move(a));
|
t.detach();
|
||||||
t.detach();
|
return res;
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +1,35 @@
|
|||||||
struct card_inserted
|
struct card_inserted {
|
||||||
{
|
std::string account;
|
||||||
std::string account;
|
|
||||||
};
|
};
|
||||||
class atm
|
class atm {
|
||||||
{
|
messaging::receiver incoming;
|
||||||
messaging::receiver incoming;
|
messaging::sender bank;
|
||||||
messaging::sender bank;
|
messaging::sender interface_hardware;
|
||||||
messaging::sender interface_hardware;
|
void (atm::*state)();
|
||||||
void (atm::*state)();
|
std::string account;
|
||||||
std::string account;
|
std::string pin;
|
||||||
std::string pin;
|
void waiting_for_card()
|
||||||
void waiting_for_card()
|
{
|
||||||
{
|
interface_hardware.send(display_enter_card());
|
||||||
interface_hardware.send(display_enter_card());
|
incoming.wait().handle<card_inserted>([&](card_inserted const& msg) {
|
||||||
incoming.wait()
|
account = msg.account;
|
||||||
.handle<card_inserted>(
|
pin = "";
|
||||||
[&](card_inserted const& msg)
|
interface_hardware.send(display_enter_pin());
|
||||||
{
|
state = &atm::getting_pin;
|
||||||
account=msg.account;
|
});
|
||||||
pin="";
|
}
|
||||||
interface_hardware.send(display_enter_pin());
|
void getting_pin();
|
||||||
state=&atm::getting_pin;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
void getting_pin();
|
|
||||||
public:
|
public:
|
||||||
void run()
|
void run()
|
||||||
{
|
{
|
||||||
state=&atm::waiting_for_card;
|
state = &atm::waiting_for_card;
|
||||||
try
|
try {
|
||||||
{
|
for (;;) {
|
||||||
for(;;)
|
(this->*state)();
|
||||||
{
|
}
|
||||||
(this->*state)();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(messaging::close_queue const&)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
catch (messaging::close_queue const&) {
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,31 +1,20 @@
|
|||||||
void atm::getting_pin()
|
void
|
||||||
|
atm::getting_pin()
|
||||||
{
|
{
|
||||||
incoming.wait()
|
incoming.wait()
|
||||||
.handle<digit_pressed>(
|
.handle<digit_pressed>([&](digit_pressed const& msg) {
|
||||||
[&](digit_pressed const& msg)
|
unsigned const pin_length = 4;
|
||||||
{
|
pin += msg.digit;
|
||||||
unsigned const pin_length=4;
|
if (pin.length() == pin_length) {
|
||||||
pin+=msg.digit;
|
bank.send(verify_pin(account, pin, incoming));
|
||||||
if(pin.length()==pin_length)
|
state = &atm::verifying_pin;
|
||||||
{
|
}
|
||||||
bank.send(verify_pin(account,pin,incoming));
|
})
|
||||||
state=&atm::verifying_pin;
|
.handle<clear_last_pressed>([&](clear_last_pressed const& msg) {
|
||||||
}
|
if (!pin.empty()) {
|
||||||
}
|
pin.resize(pin.length() - 1);
|
||||||
)
|
}
|
||||||
.handle<clear_last_pressed>(
|
})
|
||||||
[&](clear_last_pressed const& msg)
|
.handle<cancel_pressed>(
|
||||||
{
|
[&](cancel_pressed const& msg) { state = &atm::done_processing; });
|
||||||
if(!pin.empty())
|
|
||||||
{
|
|
||||||
pin.resize(pin.length()-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.handle<cancel_pressed>(
|
|
||||||
[&](cancel_pressed const& msg)
|
|
||||||
{
|
|
||||||
state=&atm::done_processing;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,36 @@
|
|||||||
template <class T, class Container = std::deque<T> >
|
template <class T, class Container = std::deque<T>>
|
||||||
class queue {
|
class queue {
|
||||||
public:
|
public:
|
||||||
explicit queue(const Container&);
|
explicit queue(const Container&);
|
||||||
explicit queue(Container&& = Container());
|
explicit queue(Container&& = Container());
|
||||||
queue(queue&& q);
|
queue(queue&& q);
|
||||||
|
|
||||||
template <class Alloc> explicit queue(const Alloc&);
|
template <class Alloc>
|
||||||
template <class Alloc> queue(const Container&, const Alloc&);
|
explicit queue(const Alloc&);
|
||||||
template <class Alloc> queue(Container&&, const Alloc&);
|
template <class Alloc>
|
||||||
template <class Alloc> queue(queue&&, const Alloc&);
|
queue(const Container&, const Alloc&);
|
||||||
|
template <class Alloc>
|
||||||
|
queue(Container&&, const Alloc&);
|
||||||
|
template <class Alloc>
|
||||||
|
queue(queue&&, const Alloc&);
|
||||||
|
|
||||||
queue& operator=(queue&& q);
|
queue& operator=(queue&& q);
|
||||||
void swap(queue&& q);
|
void swap(queue&& q);
|
||||||
|
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
size_type size() const;
|
size_type size() const;
|
||||||
|
|
||||||
T& front();
|
T& front();
|
||||||
const T& front() const;
|
const T& front() const;
|
||||||
T& back();
|
T& back();
|
||||||
const T& back() const;
|
const T& back() const;
|
||||||
|
|
||||||
void push(const T& x);
|
void push(const T& x);
|
||||||
void push(T&& x);
|
void push(T&& x);
|
||||||
void pop();
|
void pop();
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
{}
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_queue
|
class threadsafe_queue {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
threadsafe_queue();
|
threadsafe_queue();
|
||||||
threadsafe_queue(const threadsafe_queue&);
|
threadsafe_queue(const threadsafe_queue&);
|
||||||
threadsafe_queue& operator=(const threadsafe_queue&) = delete;
|
threadsafe_queue& operator=(const threadsafe_queue&) = delete;
|
||||||
|
|
||||||
void push(T new_value);
|
void push(T new_value);
|
||||||
|
|
||||||
bool try_pop(T& value);
|
|
||||||
std::shared_ptr<T> try_pop();
|
|
||||||
|
|
||||||
void wait_and_pop(T& value);
|
bool try_pop(T& value);
|
||||||
std::shared_ptr<T> wait_and_pop();
|
std::shared_ptr<T> try_pop();
|
||||||
|
|
||||||
bool empty() const;
|
void wait_and_pop(T& value);
|
||||||
|
std::shared_ptr<T> wait_and_pop();
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
{}
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,33 +1,32 @@
|
|||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_queue
|
class threadsafe_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
std::mutex mut;
|
std::mutex mut;
|
||||||
std::queue<T> data_queue;
|
std::queue<T> data_queue;
|
||||||
std::condition_variable data_cond;
|
std::condition_variable data_cond;
|
||||||
public:
|
|
||||||
void push(T new_value)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
|
||||||
data_queue.push(new_value);
|
|
||||||
data_cond.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait_and_pop(T& value)
|
public:
|
||||||
{
|
void push(T new_value)
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
{
|
||||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
value=data_queue.front();
|
data_queue.push(new_value);
|
||||||
data_queue.pop();
|
data_cond.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wait_and_pop(T& value)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
|
data_cond.wait(lk, [this] { return !data_queue.empty(); });
|
||||||
|
value = data_queue.front();
|
||||||
|
data_queue.pop();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct data_chunk {
|
||||||
struct data_chunk
|
};
|
||||||
{};
|
|
||||||
|
|
||||||
data_chunk prepare_data();
|
data_chunk prepare_data();
|
||||||
bool more_data_to_prepare();
|
bool more_data_to_prepare();
|
||||||
@@ -36,23 +35,23 @@ bool is_last_chunk(data_chunk);
|
|||||||
|
|
||||||
threadsafe_queue<data_chunk> data_queue;
|
threadsafe_queue<data_chunk> data_queue;
|
||||||
|
|
||||||
void data_preparation_thread()
|
void
|
||||||
|
data_preparation_thread()
|
||||||
{
|
{
|
||||||
while(more_data_to_prepare())
|
while (more_data_to_prepare()) {
|
||||||
{
|
data_chunk const data = prepare_data();
|
||||||
data_chunk const data=prepare_data();
|
data_queue.push(data);
|
||||||
data_queue.push(data);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void data_processing_thread()
|
void
|
||||||
|
data_processing_thread()
|
||||||
{
|
{
|
||||||
while(true)
|
while (true) {
|
||||||
{
|
data_chunk data;
|
||||||
data_chunk data;
|
data_queue.wait_and_pop(data);
|
||||||
data_queue.wait_and_pop(data);
|
process(data);
|
||||||
process(data);
|
if (is_last_chunk(data))
|
||||||
if(is_last_chunk(data))
|
break;
|
||||||
break;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,73 +1,74 @@
|
|||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <queue>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_queue
|
class threadsafe_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
mutable std::mutex mut;
|
mutable std::mutex mut;
|
||||||
std::queue<T> data_queue;
|
std::queue<T> data_queue;
|
||||||
std::condition_variable data_cond;
|
std::condition_variable data_cond;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
threadsafe_queue()
|
threadsafe_queue() {}
|
||||||
{}
|
threadsafe_queue(threadsafe_queue const& other)
|
||||||
threadsafe_queue(threadsafe_queue const& other)
|
{
|
||||||
{
|
std::lock_guard<std::mutex> lk(other.mut);
|
||||||
std::lock_guard<std::mutex> lk(other.mut);
|
data_queue = other.data_queue;
|
||||||
data_queue=other.data_queue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void push(T new_value)
|
void push(T new_value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
data_queue.push(new_value);
|
data_queue.push(new_value);
|
||||||
data_cond.notify_one();
|
data_cond.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_and_pop(T& value)
|
void wait_and_pop(T& value)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
data_cond.wait(lk, [this] { return !data_queue.empty(); });
|
||||||
value=data_queue.front();
|
value = data_queue.front();
|
||||||
data_queue.pop();
|
data_queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<T> wait_and_pop()
|
std::shared_ptr<T> wait_and_pop()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
data_cond.wait(lk, [this] { return !data_queue.empty(); });
|
||||||
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
|
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
|
||||||
data_queue.pop();
|
data_queue.pop();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool try_pop(T& value)
|
bool try_pop(T& value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
if(data_queue.empty)
|
if (data_queue.empty)
|
||||||
return false;
|
return false;
|
||||||
value=data_queue.front();
|
value = data_queue.front();
|
||||||
data_queue.pop();
|
data_queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop()
|
std::shared_ptr<T> try_pop()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
if(data_queue.empty())
|
if (data_queue.empty())
|
||||||
return std::shared_ptr<T>();
|
return std::shared_ptr<T>();
|
||||||
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
|
std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));
|
||||||
data_queue.pop();
|
data_queue.pop();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
return data_queue.empty();
|
return data_queue.empty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
{}
|
main()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,20 @@
|
|||||||
#include <future>
|
#include <future>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
int find_the_answer_to_ltuae()
|
int
|
||||||
|
find_the_answer_to_ltuae()
|
||||||
{
|
{
|
||||||
return 42;
|
return 42;
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_other_stuff()
|
void
|
||||||
{}
|
do_other_stuff()
|
||||||
|
|
||||||
int main()
|
|
||||||
{
|
{
|
||||||
std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
|
}
|
||||||
do_other_stuff();
|
|
||||||
std::cout<<"The answer is "<<the_answer.get()<<std::endl;
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
std::future<int> the_answer = std::async(find_the_answer_to_ltuae);
|
||||||
|
do_other_stuff();
|
||||||
|
std::cout << "The answer is " << the_answer.get() << std::endl;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,30 @@
|
|||||||
#include <string>
|
|
||||||
#include <future>
|
#include <future>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
struct X
|
struct X {
|
||||||
{
|
void foo(int, std::string const&);
|
||||||
void foo(int,std::string const&);
|
std::string bar(std::string const&);
|
||||||
std::string bar(std::string const&);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
X x;
|
X x;
|
||||||
auto f1=std::async(&X::foo,&x,42,"hello");
|
auto f1 = std::async(&X::foo, &x, 42, "hello");
|
||||||
auto f2=std::async(&X::bar,x,"goodbye");
|
auto f2 = std::async(&X::bar, x, "goodbye");
|
||||||
|
|
||||||
struct Y
|
struct Y {
|
||||||
{
|
double operator()(double);
|
||||||
double operator()(double);
|
|
||||||
};
|
};
|
||||||
Y y;
|
Y y;
|
||||||
auto f3=std::async(Y(),3.141);
|
auto f3 = std::async(Y(), 3.141);
|
||||||
auto f4=std::async(std::ref(y),2.718);
|
auto f4 = std::async(std::ref(y), 2.718);
|
||||||
X baz(X&);
|
X baz(X&);
|
||||||
auto f6=std::async(baz,std::ref(x));
|
auto f6 = std::async(baz, std::ref(x));
|
||||||
class move_only
|
class move_only {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
move_only();
|
move_only();
|
||||||
move_only(move_only&&);
|
move_only(move_only&&);
|
||||||
move_only(move_only const&) = delete;
|
move_only(move_only const&) = delete;
|
||||||
move_only& operator=(move_only&&);
|
move_only& operator =(move_only&&);
|
||||||
move_only& operator=(move_only const&) = delete;
|
move_only& operator=(move_only const&) = delete;
|
||||||
void operator()();
|
void operator()();
|
||||||
};
|
};
|
||||||
auto f5=std::async(move_only());
|
auto f5 = std::async(move_only());
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
template<>
|
template <>
|
||||||
class packaged_task<std::string(std::vector<char>*,int)>
|
class packaged_task<std::string(std::vector<char>*, int)> {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
template<typename Callable>
|
template <typename Callable>
|
||||||
explicit packaged_task(Callable&& f);
|
explicit packaged_task(Callable&& f);
|
||||||
std::future<std::string> get_future();
|
std::future<std::string> get_future();
|
||||||
void operator()(std::vector<char>*,int);
|
void operator()(std::vector<char>*, int);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,40 +1,41 @@
|
|||||||
#include <deque>
|
#include <deque>
|
||||||
#include <mutex>
|
|
||||||
#include <future>
|
#include <future>
|
||||||
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
std::mutex m;
|
std::mutex m;
|
||||||
std::deque<std::packaged_task<void()> > tasks;
|
std::deque<std::packaged_task<void()>> tasks;
|
||||||
|
|
||||||
bool gui_shutdown_message_received();
|
bool gui_shutdown_message_received();
|
||||||
void get_and_process_gui_message();
|
void get_and_process_gui_message();
|
||||||
|
|
||||||
void gui_thread()
|
void
|
||||||
|
gui_thread()
|
||||||
{
|
{
|
||||||
while(!gui_shutdown_message_received())
|
while (!gui_shutdown_message_received()) {
|
||||||
|
get_and_process_gui_message();
|
||||||
|
std::packaged_task<void()> task;
|
||||||
{
|
{
|
||||||
get_and_process_gui_message();
|
std::lock_guard<std::mutex> lk(m);
|
||||||
std::packaged_task<void()> task;
|
if (tasks.empty())
|
||||||
{
|
continue;
|
||||||
std::lock_guard<std::mutex> lk(m);
|
task = std::move(tasks.front());
|
||||||
if(tasks.empty())
|
tasks.pop_front();
|
||||||
continue;
|
|
||||||
task=std::move(tasks.front());
|
|
||||||
tasks.pop_front();
|
|
||||||
}
|
|
||||||
task();
|
|
||||||
}
|
}
|
||||||
|
task();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::thread gui_bg_thread(gui_thread);
|
std::thread gui_bg_thread(gui_thread);
|
||||||
|
|
||||||
template<typename Func>
|
template <typename Func>
|
||||||
std::future<void> post_task_for_gui_thread(Func f)
|
std::future<void>
|
||||||
|
post_task_for_gui_thread(Func f)
|
||||||
{
|
{
|
||||||
std::packaged_task<void()> task(f);
|
std::packaged_task<void()> task(f);
|
||||||
std::future<void> res=task.get_future();
|
std::future<void> res = task.get_future();
|
||||||
std::lock_guard<std::mutex> lk(m);
|
std::lock_guard<std::mutex> lk(m);
|
||||||
tasks.push_back(std::move(task));
|
tasks.push_back(std::move(task));
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
class spinlock_mutex
|
class spinlock_mutex {
|
||||||
{
|
std::atomic_flag flag;
|
||||||
std::atomic_flag flag;
|
|
||||||
public:
|
public:
|
||||||
spinlock_mutex():
|
spinlock_mutex() : flag(ATOMIC_FLAG_INIT) {}
|
||||||
flag(ATOMIC_FLAG_INIT)
|
void lock()
|
||||||
{}
|
{
|
||||||
void lock()
|
while (flag.test_and_set(std::memory_order_acquire))
|
||||||
{
|
;
|
||||||
while(flag.test_and_set(std::memory_order_acquire));
|
}
|
||||||
}
|
void unlock() { flag.clear(std::memory_order_release); }
|
||||||
void unlock()
|
|
||||||
{
|
|
||||||
flag.clear(std::memory_order_release);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,39 +1,40 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include <atomic>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <atomic>
|
struct X {
|
||||||
#include <assert.h>
|
int i;
|
||||||
struct X
|
std::string s;
|
||||||
{
|
|
||||||
int i;
|
|
||||||
std::string s;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
std::atomic<X*> p;
|
std::atomic<X*> p;
|
||||||
std::atomic<int> a;
|
std::atomic<int> a;
|
||||||
|
|
||||||
void create_x()
|
void
|
||||||
|
create_x()
|
||||||
{
|
{
|
||||||
X* x=new X;
|
X* x = new X;
|
||||||
x->i=42;
|
x->i = 42;
|
||||||
x->s="hello";
|
x->s = "hello";
|
||||||
a.store(99,std::memory_order_relaxed);
|
a.store(99, std::memory_order_relaxed);
|
||||||
p.store(x,std::memory_order_release);
|
p.store(x, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void use_x()
|
void
|
||||||
|
use_x()
|
||||||
{
|
{
|
||||||
X* x;
|
X* x;
|
||||||
while(!(x=p.load(std::memory_order_consume)))
|
while (!(x = p.load(std::memory_order_consume)))
|
||||||
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||||
assert(x->i==42);
|
assert(x->i == 42);
|
||||||
assert(x->s=="hello");
|
assert(x->s == "hello");
|
||||||
assert(a.load(std::memory_order_relaxed)==99);
|
assert(a.load(std::memory_order_relaxed) == 99);
|
||||||
}
|
}
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
std::thread t1(create_x);
|
std::thread t1(create_x);
|
||||||
std::thread t2(use_x);
|
std::thread t2(use_x);
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,38 +4,38 @@
|
|||||||
std::vector<int> queue_data;
|
std::vector<int> queue_data;
|
||||||
std::atomic<int> count;
|
std::atomic<int> count;
|
||||||
|
|
||||||
void populate_queue()
|
void
|
||||||
|
populate_queue()
|
||||||
{
|
{
|
||||||
unsigned const number_of_items=20;
|
unsigned const number_of_items = 20;
|
||||||
queue_data.clear();
|
queue_data.clear();
|
||||||
for(unsigned i=0;i<number_of_items;++i)
|
for (unsigned i = 0; i < number_of_items; ++i) {
|
||||||
{
|
queue_data.push_back(i);
|
||||||
queue_data.push_back(i);
|
}
|
||||||
}
|
|
||||||
|
count.store(number_of_items, std::memory_order_release);
|
||||||
count.store(number_of_items,std::memory_order_release);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void consume_queue_items()
|
void
|
||||||
|
consume_queue_items()
|
||||||
{
|
{
|
||||||
while(true)
|
while (true) {
|
||||||
{
|
int item_index;
|
||||||
int item_index;
|
if ((item_index = count.fetch_sub(1, std::memory_order_acquire)) <= 0) {
|
||||||
if((item_index=count.fetch_sub(1,std::memory_order_acquire))<=0)
|
wait_for_more_items();
|
||||||
{
|
continue;
|
||||||
wait_for_more_items();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
process(queue_data[item_index-1]);
|
|
||||||
}
|
}
|
||||||
|
process(queue_data[item_index - 1]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
std::thread a(populate_queue);
|
std::thread a(populate_queue);
|
||||||
std::thread b(consume_queue_items);
|
std::thread b(consume_queue_items);
|
||||||
std::thread c(consume_queue_items);
|
std::thread c(consume_queue_items);
|
||||||
a.join();
|
a.join();
|
||||||
b.join();
|
b.join();
|
||||||
c.join();
|
c.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,33 +1,37 @@
|
|||||||
|
#include <assert.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
std::atomic<bool> x,y;
|
std::atomic<bool> x, y;
|
||||||
std::atomic<int> z;
|
std::atomic<int> z;
|
||||||
|
|
||||||
void write_x_then_y()
|
void
|
||||||
|
write_x_then_y()
|
||||||
{
|
{
|
||||||
x.store(true,std::memory_order_relaxed);
|
x.store(true, std::memory_order_relaxed);
|
||||||
std::atomic_thread_fence(std::memory_order_release);
|
std::atomic_thread_fence(std::memory_order_release);
|
||||||
y.store(true,std::memory_order_relaxed);
|
y.store(true, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_y_then_x()
|
void
|
||||||
|
read_y_then_x()
|
||||||
{
|
{
|
||||||
while(!y.load(std::memory_order_relaxed));
|
while (!y.load(std::memory_order_relaxed))
|
||||||
std::atomic_thread_fence(std::memory_order_acquire);
|
;
|
||||||
if(x.load(std::memory_order_relaxed))
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
++z;
|
if (x.load(std::memory_order_relaxed))
|
||||||
|
++z;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
x=false;
|
x = false;
|
||||||
y=false;
|
y = false;
|
||||||
z=0;
|
z = 0;
|
||||||
std::thread a(write_x_then_y);
|
std::thread a(write_x_then_y);
|
||||||
std::thread b(read_y_then_x);
|
std::thread b(read_y_then_x);
|
||||||
a.join();
|
a.join();
|
||||||
b.join();
|
b.join();
|
||||||
assert(z.load()!=0);
|
assert(z.load() != 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,38 @@
|
|||||||
|
#include <assert.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
bool x=false;
|
bool x = false;
|
||||||
std::atomic<bool> y;
|
std::atomic<bool> y;
|
||||||
std::atomic<int> z;
|
std::atomic<int> z;
|
||||||
|
|
||||||
void write_x_then_y()
|
void
|
||||||
|
write_x_then_y()
|
||||||
{
|
{
|
||||||
x=true;
|
x = true;
|
||||||
std::atomic_thread_fence(std::memory_order_release);
|
std::atomic_thread_fence(std::memory_order_release);
|
||||||
y.store(true,std::memory_order_relaxed);
|
y.store(true, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_y_then_x()
|
void
|
||||||
|
read_y_then_x()
|
||||||
{
|
{
|
||||||
while(!y.load(std::memory_order_relaxed));
|
while (!y.load(std::memory_order_relaxed))
|
||||||
std::atomic_thread_fence(std::memory_order_acquire);
|
;
|
||||||
if(x)
|
std::atomic_thread_fence(std::memory_order_acquire);
|
||||||
++z;
|
if (x)
|
||||||
|
++z;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
x=false;
|
x = false;
|
||||||
y=false;
|
y = false;
|
||||||
z=0;
|
z = 0;
|
||||||
std::thread a(write_x_then_y);
|
std::thread a(write_x_then_y);
|
||||||
std::thread b(read_y_then_x);
|
std::thread b(read_y_then_x);
|
||||||
a.join();
|
a.join();
|
||||||
b.join();
|
b.join();
|
||||||
assert(z.load()!=0);
|
assert(z.load() != 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,23 @@
|
|||||||
#include <vector>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <iostream>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
std::vector<int> data;
|
std::vector<int> data;
|
||||||
std::atomic_bool data_ready(false);
|
std::atomic_bool data_ready(false);
|
||||||
|
|
||||||
void reader_thread()
|
void
|
||||||
|
reader_thread()
|
||||||
{
|
{
|
||||||
while(!data_ready.load())
|
while (!data_ready.load()) {
|
||||||
{
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
}
|
||||||
}
|
std::cout << "The answer=" << data[0] << "\n";
|
||||||
std::cout<<"The answer="<<data[0]<<"\n";
|
|
||||||
}
|
}
|
||||||
void writer_thread()
|
void
|
||||||
|
writer_thread()
|
||||||
{
|
{
|
||||||
data.push_back(42);
|
data.push_back(42);
|
||||||
data_ready=true;
|
data_ready = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,20 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
void foo(int a,int b)
|
void
|
||||||
|
foo(int a, int b)
|
||||||
{
|
{
|
||||||
std::cout<<a<<”,”<<b<<std::endl;
|
std::cout << a <<”,”<< b << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_num()
|
int
|
||||||
|
get_num()
|
||||||
{
|
{
|
||||||
static int i=0;
|
static int i = 0;
|
||||||
return ++i;
|
return ++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
foo(get_num(),get_num());
|
foo(get_num(), get_num());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,53 @@
|
|||||||
|
#include <assert.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
std::atomic<bool> x,y;
|
std::atomic<bool> x, y;
|
||||||
std::atomic<int> z;
|
std::atomic<int> z;
|
||||||
|
|
||||||
void write_x()
|
void
|
||||||
|
write_x()
|
||||||
{
|
{
|
||||||
x.store(true,std::memory_order_seq_cst);
|
x.store(true, std::memory_order_seq_cst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_y()
|
void
|
||||||
|
write_y()
|
||||||
{
|
{
|
||||||
y.store(true,std::memory_order_seq_cst);
|
y.store(true, std::memory_order_seq_cst);
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_x_then_y()
|
void
|
||||||
|
read_x_then_y()
|
||||||
{
|
{
|
||||||
while(!x.load(std::memory_order_seq_cst));
|
while (!x.load(std::memory_order_seq_cst))
|
||||||
if(y.load(std::memory_order_seq_cst))
|
;
|
||||||
++z;
|
if (y.load(std::memory_order_seq_cst))
|
||||||
|
++z;
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_y_then_x()
|
void
|
||||||
|
read_y_then_x()
|
||||||
{
|
{
|
||||||
while(!y.load(std::memory_order_seq_cst));
|
while (!y.load(std::memory_order_seq_cst))
|
||||||
if(x.load(std::memory_order_seq_cst))
|
;
|
||||||
++z;
|
if (x.load(std::memory_order_seq_cst))
|
||||||
|
++z;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
x=false;
|
x = false;
|
||||||
y=false;
|
y = false;
|
||||||
z=0;
|
z = 0;
|
||||||
std::thread a(write_x);
|
std::thread a(write_x);
|
||||||
std::thread b(write_y);
|
std::thread b(write_y);
|
||||||
std::thread c(read_x_then_y);
|
std::thread c(read_x_then_y);
|
||||||
std::thread d(read_y_then_x);
|
std::thread d(read_y_then_x);
|
||||||
a.join();
|
a.join();
|
||||||
b.join();
|
b.join();
|
||||||
c.join();
|
c.join();
|
||||||
d.join();
|
d.join();
|
||||||
assert(z.load()!=0);
|
assert(z.load() != 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,35 @@
|
|||||||
|
#include <assert.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
std::atomic<bool> x,y;
|
std::atomic<bool> x, y;
|
||||||
std::atomic<int> z;
|
std::atomic<int> z;
|
||||||
|
|
||||||
void write_x_then_y()
|
void
|
||||||
|
write_x_then_y()
|
||||||
{
|
{
|
||||||
x.store(true,std::memory_order_relaxed);
|
x.store(true, std::memory_order_relaxed);
|
||||||
y.store(true,std::memory_order_relaxed);
|
y.store(true, std::memory_order_relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_y_then_x()
|
void
|
||||||
|
read_y_then_x()
|
||||||
{
|
{
|
||||||
while(!y.load(std::memory_order_relaxed));
|
while (!y.load(std::memory_order_relaxed))
|
||||||
if(x.load(std::memory_order_relaxed))
|
;
|
||||||
++z;
|
if (x.load(std::memory_order_relaxed))
|
||||||
|
++z;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
x=false;
|
x = false;
|
||||||
y=false;
|
y = false;
|
||||||
z=0;
|
z = 0;
|
||||||
std::thread a(write_x_then_y);
|
std::thread a(write_x_then_y);
|
||||||
std::thread b(read_y_then_x);
|
std::thread b(read_y_then_x);
|
||||||
a.join();
|
a.join();
|
||||||
b.join();
|
b.join();
|
||||||
assert(z.load()!=0);
|
assert(z.load() != 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,74 +1,72 @@
|
|||||||
#include <thread>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
std::atomic<int> x(0),y(0),z(0);
|
std::atomic<int> x(0), y(0), z(0);
|
||||||
std::atomic<bool> go(false);
|
std::atomic<bool> go(false);
|
||||||
unsigned const loop_count=10;
|
unsigned const loop_count = 10;
|
||||||
|
|
||||||
struct read_values
|
struct read_values {
|
||||||
{
|
int x, y, z;
|
||||||
int x,y,z;
|
|
||||||
};
|
};
|
||||||
read_values values1[loop_count];
|
read_values values1[loop_count];
|
||||||
read_values values2[loop_count];
|
read_values values2[loop_count];
|
||||||
read_values values3[loop_count];
|
read_values values3[loop_count];
|
||||||
read_values values4[loop_count];
|
read_values values4[loop_count];
|
||||||
read_values values5[loop_count];
|
read_values values5[loop_count];
|
||||||
void increment(std::atomic<int>* var_to_inc,read_values* values)
|
void
|
||||||
|
increment(std::atomic<int>* var_to_inc, read_values* values)
|
||||||
{
|
{
|
||||||
while(!go)
|
while (!go) std::this_thread::yield();
|
||||||
std::this_thread::yield();
|
for (unsigned i = 0; i < loop_count; ++i) {
|
||||||
for(unsigned i=0;i<loop_count;++i)
|
values[i].x = x.load(std::memory_order_relaxed);
|
||||||
{
|
values[i].y = y.load(std::memory_order_relaxed);
|
||||||
values[i].x=x.load(std::memory_order_relaxed);
|
values[i].z = z.load(std::memory_order_relaxed);
|
||||||
values[i].y=y.load(std::memory_order_relaxed);
|
var_to_inc->store(i + 1, std::memory_order_relaxed);
|
||||||
values[i].z=z.load(std::memory_order_relaxed);
|
std::this_thread::yield();
|
||||||
var_to_inc->store(i+1,std::memory_order_relaxed);
|
}
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_vals(read_values* values)
|
void
|
||||||
|
read_vals(read_values* values)
|
||||||
{
|
{
|
||||||
while(!go)
|
while (!go) std::this_thread::yield();
|
||||||
std::this_thread::yield();
|
for (unsigned i = 0; i < loop_count; ++i) {
|
||||||
for(unsigned i=0;i<loop_count;++i)
|
values[i].x = x.load(std::memory_order_relaxed);
|
||||||
{
|
values[i].y = y.load(std::memory_order_relaxed);
|
||||||
values[i].x=x.load(std::memory_order_relaxed);
|
values[i].z = z.load(std::memory_order_relaxed);
|
||||||
values[i].y=y.load(std::memory_order_relaxed);
|
std::this_thread::yield();
|
||||||
values[i].z=z.load(std::memory_order_relaxed);
|
}
|
||||||
std::this_thread::yield();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void print(read_values* v)
|
void
|
||||||
|
print(read_values* v)
|
||||||
{
|
{
|
||||||
for(unsigned i=0;i<loop_count;++i)
|
for (unsigned i = 0; i < loop_count; ++i) {
|
||||||
{
|
if (i)
|
||||||
if(i)
|
std::cout << ",";
|
||||||
std::cout<<",";
|
std::cout << "(" << v[i].x << "," << v[i].y << "," << v[i].z << ")";
|
||||||
std::cout<<"("<<v[i].x<<","<<v[i].y<<","<<v[i].z<<")";
|
}
|
||||||
}
|
std::cout << std::endl;
|
||||||
std::cout<<std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
std::thread t1(increment,&x,values1);
|
std::thread t1(increment, &x, values1);
|
||||||
std::thread t2(increment,&y,values2);
|
std::thread t2(increment, &y, values2);
|
||||||
std::thread t3(increment,&z,values3);
|
std::thread t3(increment, &z, values3);
|
||||||
std::thread t4(read_vals,values4);
|
std::thread t4(read_vals, values4);
|
||||||
std::thread t5(read_vals,values5);
|
std::thread t5(read_vals, values5);
|
||||||
go=true;
|
go = true;
|
||||||
t5.join();
|
t5.join();
|
||||||
t4.join();
|
t4.join();
|
||||||
t3.join();
|
t3.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
t1.join();
|
t1.join();
|
||||||
print(values1);
|
print(values1);
|
||||||
print(values2);
|
print(values2);
|
||||||
print(values3);
|
print(values3);
|
||||||
print(values4);
|
print(values4);
|
||||||
print(values5);
|
print(values5);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,46 +1,53 @@
|
|||||||
|
#include <assert.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
std::atomic<bool> x,y;
|
std::atomic<bool> x, y;
|
||||||
std::atomic<int> z;
|
std::atomic<int> z;
|
||||||
|
|
||||||
void write_x()
|
void
|
||||||
|
write_x()
|
||||||
{
|
{
|
||||||
x.store(true,std::memory_order_release);
|
x.store(true, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void write_y()
|
void
|
||||||
|
write_y()
|
||||||
{
|
{
|
||||||
y.store(true,std::memory_order_release);
|
y.store(true, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_x_then_y()
|
void
|
||||||
|
read_x_then_y()
|
||||||
{
|
{
|
||||||
while(!x.load(std::memory_order_acquire));
|
while (!x.load(std::memory_order_acquire))
|
||||||
if(y.load(std::memory_order_acquire))
|
;
|
||||||
++z;
|
if (y.load(std::memory_order_acquire))
|
||||||
|
++z;
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_y_then_x()
|
void
|
||||||
|
read_y_then_x()
|
||||||
{
|
{
|
||||||
while(!y.load(std::memory_order_acquire));
|
while (!y.load(std::memory_order_acquire))
|
||||||
if(x.load(std::memory_order_acquire))
|
;
|
||||||
++z;
|
if (x.load(std::memory_order_acquire))
|
||||||
|
++z;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
x=false;
|
x = false;
|
||||||
y=false;
|
y = false;
|
||||||
z=0;
|
z = 0;
|
||||||
std::thread a(write_x);
|
std::thread a(write_x);
|
||||||
std::thread b(write_y);
|
std::thread b(write_y);
|
||||||
std::thread c(read_x_then_y);
|
std::thread c(read_x_then_y);
|
||||||
std::thread d(read_y_then_x);
|
std::thread d(read_y_then_x);
|
||||||
a.join();
|
a.join();
|
||||||
b.join();
|
b.join();
|
||||||
c.join();
|
c.join();
|
||||||
d.join();
|
d.join();
|
||||||
assert(z.load()!=0);
|
assert(z.load() != 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,35 @@
|
|||||||
|
#include <assert.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
std::atomic<bool> x,y;
|
std::atomic<bool> x, y;
|
||||||
std::atomic<int> z;
|
std::atomic<int> z;
|
||||||
|
|
||||||
void write_x_then_y()
|
void
|
||||||
|
write_x_then_y()
|
||||||
{
|
{
|
||||||
x.store(true,std::memory_order_relaxed);
|
x.store(true, std::memory_order_relaxed);
|
||||||
y.store(true,std::memory_order_release);
|
y.store(true, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void read_y_then_x()
|
void
|
||||||
|
read_y_then_x()
|
||||||
{
|
{
|
||||||
while(!y.load(std::memory_order_acquire));
|
while (!y.load(std::memory_order_acquire))
|
||||||
if(x.load(std::memory_order_relaxed))
|
;
|
||||||
++z;
|
if (x.load(std::memory_order_relaxed))
|
||||||
|
++z;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
x=false;
|
x = false;
|
||||||
y=false;
|
y = false;
|
||||||
z=0;
|
z = 0;
|
||||||
std::thread a(write_x_then_y);
|
std::thread a(write_x_then_y);
|
||||||
std::thread b(read_y_then_x);
|
std::thread b(read_y_then_x);
|
||||||
a.join();
|
a.join();
|
||||||
b.join();
|
b.join();
|
||||||
assert(z.load()!=0);
|
assert(z.load() != 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,42 +1,48 @@
|
|||||||
|
#include <assert.h>
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
std::atomic<int> data[5];
|
std::atomic<int> data[5];
|
||||||
std::atomic<bool> sync1(false),sync2(false);
|
std::atomic<bool> sync1(false), sync2(false);
|
||||||
|
|
||||||
void thread_1()
|
void
|
||||||
|
thread_1()
|
||||||
{
|
{
|
||||||
data[0].store(42,std::memory_order_relaxed);
|
data[0].store(42, std::memory_order_relaxed);
|
||||||
data[1].store(97,std::memory_order_relaxed);
|
data[1].store(97, std::memory_order_relaxed);
|
||||||
data[2].store(17,std::memory_order_relaxed);
|
data[2].store(17, std::memory_order_relaxed);
|
||||||
data[3].store(-141,std::memory_order_relaxed);
|
data[3].store(-141, std::memory_order_relaxed);
|
||||||
data[4].store(2003,std::memory_order_relaxed);
|
data[4].store(2003, std::memory_order_relaxed);
|
||||||
sync1.store(true,std::memory_order_release);
|
sync1.store(true, std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void thread_2()
|
void
|
||||||
|
thread_2()
|
||||||
{
|
{
|
||||||
while(!sync1.load(std::memory_order_acquire));
|
while (!sync1.load(std::memory_order_acquire))
|
||||||
sync2.store(std::memory_order_release);
|
;
|
||||||
|
sync2.store(std::memory_order_release);
|
||||||
}
|
}
|
||||||
|
|
||||||
void thread_3()
|
void
|
||||||
|
thread_3()
|
||||||
{
|
{
|
||||||
while(!sync2.load(std::memory_order_acquire));
|
while (!sync2.load(std::memory_order_acquire))
|
||||||
assert(data[0].load(std::memory_order_relaxed)==42);
|
;
|
||||||
assert(data[1].load(std::memory_order_relaxed)==97);
|
assert(data[0].load(std::memory_order_relaxed) == 42);
|
||||||
assert(data[2].load(std::memory_order_relaxed)==17);
|
assert(data[1].load(std::memory_order_relaxed) == 97);
|
||||||
assert(data[3].load(std::memory_order_relaxed)==-141);
|
assert(data[2].load(std::memory_order_relaxed) == 17);
|
||||||
assert(data[4].load(std::memory_order_relaxed)==2003);
|
assert(data[3].load(std::memory_order_relaxed) == -141);
|
||||||
|
assert(data[4].load(std::memory_order_relaxed) == 2003);
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
std::thread t1(thread_1);
|
std::thread t1(thread_1);
|
||||||
std::thread t2(thread_2);
|
std::thread t2(thread_2);
|
||||||
std::thread t3(thread_3);
|
std::thread t3(thread_3);
|
||||||
t1.join();
|
t1.join();
|
||||||
t2.join();
|
t2.join();
|
||||||
t3.join();
|
t3.join();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,56 +1,52 @@
|
|||||||
#include <exception>
|
#include <exception>
|
||||||
#include <stack>
|
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <stack>
|
||||||
|
|
||||||
struct empty_stack: std::exception
|
struct empty_stack : std::exception {
|
||||||
{
|
const char* what() const throw() { return "empty stack"; }
|
||||||
const char* what() const throw()
|
|
||||||
{
|
|
||||||
return "empty stack";
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_stack
|
class threadsafe_stack {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
std::stack<T> data;
|
std::stack<T> data;
|
||||||
mutable std::mutex m;
|
mutable std::mutex m;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
threadsafe_stack(){}
|
threadsafe_stack() {}
|
||||||
threadsafe_stack(const threadsafe_stack& other)
|
threadsafe_stack(const threadsafe_stack& other)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(other.m);
|
std::lock_guard<std::mutex> lock(other.m);
|
||||||
data=other.data;
|
data = other.data;
|
||||||
}
|
}
|
||||||
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
|
threadsafe_stack& operator=(const threadsafe_stack&) = delete;
|
||||||
|
|
||||||
void push(T new_value)
|
void push(T new_value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m);
|
std::lock_guard<std::mutex> lock(m);
|
||||||
data.push(std::move(new_value));
|
data.push(std::move(new_value));
|
||||||
}
|
}
|
||||||
std::shared_ptr<T> pop()
|
std::shared_ptr<T> pop()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m);
|
std::lock_guard<std::mutex> lock(m);
|
||||||
if(data.empty()) throw empty_stack();
|
if (data.empty())
|
||||||
std::shared_ptr<T> const res(
|
throw empty_stack();
|
||||||
std::make_shared<T>(std::move(data.top())));
|
std::shared_ptr<T> const res(std::make_shared<T>(std::move(data.top())));
|
||||||
data.pop();
|
data.pop();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
void pop(T& value)
|
void pop(T& value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(m);
|
std::lock_guard<std::mutex> lock(m);
|
||||||
if(data.empty()) throw empty_stack();
|
if (data.empty())
|
||||||
value=std::move(data.top());
|
throw empty_stack();
|
||||||
data.pop();
|
value = std::move(data.top());
|
||||||
}
|
data.pop();
|
||||||
bool empty() const
|
}
|
||||||
{
|
bool empty() const
|
||||||
std::lock_guard<std::mutex> lock(m);
|
{
|
||||||
return data.empty();
|
std::lock_guard<std::mutex> lock(m);
|
||||||
}
|
return data.empty();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +1,41 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_queue
|
class threadsafe_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<node> try_pop_head()
|
std::unique_ptr<node> try_pop_head()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||||
if(head.get()==get_tail())
|
if (head.get() == get_tail()) {
|
||||||
{
|
return std::unique_ptr<node>();
|
||||||
return std::unique_ptr<node>();
|
|
||||||
}
|
|
||||||
return pop_head();
|
|
||||||
}
|
}
|
||||||
|
return pop_head();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<node> try_pop_head(T& value)
|
std::unique_ptr<node> try_pop_head(T& value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||||
if(head.get()==get_tail())
|
if (head.get() == get_tail()) {
|
||||||
{
|
return std::unique_ptr<node>();
|
||||||
return std::unique_ptr<node>();
|
|
||||||
}
|
|
||||||
value=std::move(*head->data);
|
|
||||||
return pop_head();
|
|
||||||
}
|
}
|
||||||
|
value = std::move(*head->data);
|
||||||
|
return pop_head();
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<T> try_pop()
|
std::shared_ptr<T> try_pop()
|
||||||
{
|
{
|
||||||
std::unique_ptr<node> const old_head=try_pop_head();
|
std::unique_ptr<node> const old_head = try_pop_head();
|
||||||
return old_head?old_head->data:std::shared_ptr<T>();
|
return old_head ? old_head->data : std::shared_ptr<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool try_pop(T& value)
|
bool try_pop(T& value)
|
||||||
{
|
{
|
||||||
std::unique_ptr<node> const old_head=try_pop_head(value);
|
std::unique_ptr<node> const old_head = try_pop_head(value);
|
||||||
return old_head;
|
return old_head;
|
||||||
}
|
}
|
||||||
|
|
||||||
void empty()
|
void empty()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||||
return (head==get_tail());
|
return (head == get_tail());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,108 +1,97 @@
|
|||||||
#include <vector>
|
#include <boost/thread/shared_mutex.hpp>
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <boost/thread/shared_mutex.hpp>
|
#include <vector>
|
||||||
|
|
||||||
template<typename Key,typename Value,typename Hash=std::hash<Key> >
|
template <typename Key, typename Value, typename Hash = std::hash<Key>>
|
||||||
class threadsafe_lookup_table
|
class threadsafe_lookup_table {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
class bucket_type
|
class bucket_type {
|
||||||
|
private:
|
||||||
|
typedef std::pair<Key, Value> bucket_value;
|
||||||
|
typedef std::list<bucket_value> bucket_data;
|
||||||
|
typedef typename bucket_data::iterator bucket_iterator;
|
||||||
|
|
||||||
|
bucket_data data;
|
||||||
|
mutable boost::shared_mutex mutex;
|
||||||
|
|
||||||
|
bucket_iterator find_entry_for(Key const& key) const
|
||||||
{
|
{
|
||||||
private:
|
return std::find_if(
|
||||||
typedef std::pair<Key,Value> bucket_value;
|
data.begin(), data.end(), [&](bucket_value const& item) {
|
||||||
typedef std::list<bucket_value> bucket_data;
|
return item.first == key;
|
||||||
typedef typename bucket_data::iterator bucket_iterator;
|
});
|
||||||
|
|
||||||
bucket_data data;
|
|
||||||
mutable boost::shared_mutex mutex;
|
|
||||||
|
|
||||||
bucket_iterator find_entry_for(Key const& key) const
|
|
||||||
{
|
|
||||||
return std::find_if(data.begin(),data.end(),
|
|
||||||
[&](bucket_value const& item)
|
|
||||||
{return item.first==key;});
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
Value value_for(Key const& key,Value const& default_value) const
|
|
||||||
{
|
|
||||||
boost::shared_lock<boost::shared_mutex> lock(mutex);
|
|
||||||
bucket_iterator const found_entry=find_entry_for(key);
|
|
||||||
return (found_entry==data.end())?
|
|
||||||
default_value : found_entry->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void add_or_update_mapping(Key const& key,Value const& value)
|
|
||||||
{
|
|
||||||
std::unique_lock<boost::shared_mutex> lock(mutex);
|
|
||||||
bucket_iterator const found_entry=find_entry_for(key);
|
|
||||||
if(found_entry==data.end())
|
|
||||||
{
|
|
||||||
data.push_back(bucket_value(key,value));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
found_entry->second=value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_mapping(Key const& key)
|
|
||||||
{
|
|
||||||
std::unique_lock<boost::shared_mutex> lock(mutex);
|
|
||||||
bucket_iterator const found_entry=find_entry_for(key);
|
|
||||||
if(found_entry!=data.end())
|
|
||||||
{
|
|
||||||
data.erase(found_entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<bucket_type> > buckets;
|
|
||||||
Hash hasher;
|
|
||||||
|
|
||||||
bucket_type& get_bucket(Key const& key) const
|
|
||||||
{
|
|
||||||
std::size_t const bucket_index=hasher(key)%buckets.size();
|
|
||||||
return *buckets[bucket_index];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef Key key_type;
|
Value value_for(Key const& key, Value const& default_value) const
|
||||||
typedef Value mapped_type;
|
|
||||||
typedef Hash hash_type;
|
|
||||||
|
|
||||||
threadsafe_lookup_table(
|
|
||||||
unsigned num_buckets=19, Hash const& hasher_=Hash()):
|
|
||||||
buckets(num_buckets),hasher(hasher_)
|
|
||||||
{
|
{
|
||||||
for(unsigned i=0;i<num_buckets;++i)
|
boost::shared_lock<boost::shared_mutex> lock(mutex);
|
||||||
{
|
bucket_iterator const found_entry = find_entry_for(key);
|
||||||
buckets[i].reset(new bucket_type);
|
return (found_entry == data.end()) ? default_value : found_entry->second;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
threadsafe_lookup_table(threadsafe_lookup_table const& other)=delete;
|
void add_or_update_mapping(Key const& key, Value const& value)
|
||||||
threadsafe_lookup_table& operator=(
|
|
||||||
threadsafe_lookup_table const& other)=delete;
|
|
||||||
|
|
||||||
Value value_for(Key const& key,
|
|
||||||
Value const& default_value=Value()) const
|
|
||||||
{
|
{
|
||||||
return get_bucket(key).value_for(key,default_value);
|
std::unique_lock<boost::shared_mutex> lock(mutex);
|
||||||
|
bucket_iterator const found_entry = find_entry_for(key);
|
||||||
|
if (found_entry == data.end()) {
|
||||||
|
data.push_back(bucket_value(key, value));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
found_entry->second = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void add_or_update_mapping(Key const& key,Value const& value)
|
|
||||||
{
|
|
||||||
get_bucket(key).add_or_update_mapping(key,value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove_mapping(Key const& key)
|
void remove_mapping(Key const& key)
|
||||||
{
|
{
|
||||||
get_bucket(key).remove_mapping(key);
|
std::unique_lock<boost::shared_mutex> lock(mutex);
|
||||||
|
bucket_iterator const found_entry = find_entry_for(key);
|
||||||
|
if (found_entry != data.end()) {
|
||||||
|
data.erase(found_entry);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<bucket_type>> buckets;
|
||||||
|
Hash hasher;
|
||||||
|
|
||||||
|
bucket_type& get_bucket(Key const& key) const
|
||||||
|
{
|
||||||
|
std::size_t const bucket_index = hasher(key) % buckets.size();
|
||||||
|
return *buckets[bucket_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef Key key_type;
|
||||||
|
typedef Value mapped_type;
|
||||||
|
typedef Hash hash_type;
|
||||||
|
|
||||||
|
threadsafe_lookup_table(unsigned num_buckets = 19,
|
||||||
|
Hash const& hasher_ = Hash())
|
||||||
|
: buckets(num_buckets), hasher(hasher_)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < num_buckets; ++i) {
|
||||||
|
buckets[i].reset(new bucket_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
threadsafe_lookup_table(threadsafe_lookup_table const& other) = delete;
|
||||||
|
threadsafe_lookup_table& operator=(threadsafe_lookup_table const& other) =
|
||||||
|
delete;
|
||||||
|
|
||||||
|
Value value_for(Key const& key, Value const& default_value = Value()) const
|
||||||
|
{
|
||||||
|
return get_bucket(key).value_for(key, default_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void add_or_update_mapping(Key const& key, Value const& value)
|
||||||
|
{
|
||||||
|
get_bucket(key).add_or_update_mapping(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_mapping(Key const& key) { get_bucket(key).remove_mapping(key); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
std::map<Key,Value> threadsafe_lookup_table::get_map() const
|
std::map<Key, Value>
|
||||||
|
threadsafe_lookup_table::get_map() const
|
||||||
{
|
{
|
||||||
std::vector<std::unique_lock<boost::shared_mutex> > locks;
|
std::vector<std::unique_lock<boost::shared_mutex>> locks;
|
||||||
for(unsigned i=0;i<buckets.size();++i)
|
for (unsigned i = 0; i < buckets.size(); ++i) {
|
||||||
{
|
locks.push_back(std::unique_lock<boost::shared_mutex>(buckets[i].mutex));
|
||||||
locks.push_back(
|
}
|
||||||
std::unique_lock<boost::shared_mutex>(buckets[i].mutex));
|
std::map<Key, Value> res;
|
||||||
|
for (unsigned i = 0; i < buckets.size(); ++i) {
|
||||||
|
for (bucket_iterator it = buckets[i].data.begin();
|
||||||
|
it != buckets[i].data.end();
|
||||||
|
++it) {
|
||||||
|
res.insert(*it);
|
||||||
}
|
}
|
||||||
std::map<Key,Value> res;
|
}
|
||||||
for(unsigned i=0;i<buckets.size();++i)
|
return res;
|
||||||
{
|
|
||||||
for(bucket_iterator it=buckets[i].data.begin();
|
|
||||||
it!=buckets[i].data.end();
|
|
||||||
++it)
|
|
||||||
{
|
|
||||||
res.insert(*it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,101 +1,87 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_list
|
class threadsafe_list {
|
||||||
{
|
struct node {
|
||||||
struct node
|
std::mutex m;
|
||||||
{
|
std::shared_ptr<T> data;
|
||||||
std::mutex m;
|
std::unique_ptr<node> next;
|
||||||
std::shared_ptr<T> data;
|
|
||||||
std::unique_ptr<node> next;
|
|
||||||
|
|
||||||
node():
|
node() : next() {}
|
||||||
next()
|
|
||||||
{}
|
node(T const& value) : data(std::make_shared<T>(value)) {}
|
||||||
|
};
|
||||||
node(T const& value):
|
|
||||||
data(std::make_shared<T>(value))
|
node head;
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
node head;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
threadsafe_list()
|
threadsafe_list() {}
|
||||||
{}
|
|
||||||
|
|
||||||
~threadsafe_list()
|
~threadsafe_list()
|
||||||
{
|
{
|
||||||
remove_if([](T const&){return true;});
|
remove_if([](T const&) { return true; });
|
||||||
}
|
}
|
||||||
|
|
||||||
threadsafe_list(threadsafe_list const& other)=delete;
|
threadsafe_list(threadsafe_list const& other) = delete;
|
||||||
threadsafe_list& operator=(threadsafe_list const& other)=delete;
|
threadsafe_list& operator=(threadsafe_list const& other) = delete;
|
||||||
|
|
||||||
void push_front(T const& value)
|
|
||||||
{
|
|
||||||
std::unique_ptr<node> new_node(new node(value));
|
|
||||||
std::lock_guard<std::mutex> lk(head.m);
|
|
||||||
new_node->next=std::move(head.next);
|
|
||||||
head.next=std::move(new_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Function>
|
void push_front(T const& value)
|
||||||
void for_each(Function f)
|
{
|
||||||
{
|
std::unique_ptr<node> new_node(new node(value));
|
||||||
node* current=&head;
|
std::lock_guard<std::mutex> lk(head.m);
|
||||||
std::unique_lock<std::mutex> lk(head.m);
|
new_node->next = std::move(head.next);
|
||||||
while(node* const next=current->next.get())
|
head.next = std::move(new_node);
|
||||||
{
|
}
|
||||||
std::unique_lock<std::mutex> next_lk(next->m);
|
|
||||||
lk.unlock();
|
|
||||||
f(*next->data);
|
|
||||||
current=next;
|
|
||||||
lk=std::move(next_lk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Predicate>
|
template <typename Function>
|
||||||
std::shared_ptr<T> find_first_if(Predicate p)
|
void for_each(Function f)
|
||||||
{
|
{
|
||||||
node* current=&head;
|
node* current = &head;
|
||||||
std::unique_lock<std::mutex> lk(head.m);
|
std::unique_lock<std::mutex> lk(head.m);
|
||||||
while(node* const next=current->next.get())
|
while (node* const next = current->next.get()) {
|
||||||
{
|
std::unique_lock<std::mutex> next_lk(next->m);
|
||||||
std::unique_lock<std::mutex> next_lk(next->m);
|
lk.unlock();
|
||||||
lk.unlock();
|
f(*next->data);
|
||||||
if(p(*next->data))
|
current = next;
|
||||||
{
|
lk = std::move(next_lk);
|
||||||
return next->data;
|
|
||||||
}
|
|
||||||
current=next;
|
|
||||||
lk=std::move(next_lk);
|
|
||||||
}
|
|
||||||
return std::shared_ptr<T>();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Predicate>
|
template <typename Predicate>
|
||||||
void remove_if(Predicate p)
|
std::shared_ptr<T> find_first_if(Predicate p)
|
||||||
{
|
{
|
||||||
node* current=&head;
|
node* current = &head;
|
||||||
std::unique_lock<std::mutex> lk(head.m);
|
std::unique_lock<std::mutex> lk(head.m);
|
||||||
while(node* const next=current->next.get())
|
while (node* const next = current->next.get()) {
|
||||||
{
|
std::unique_lock<std::mutex> next_lk(next->m);
|
||||||
std::unique_lock<std::mutex> next_lk(next->m);
|
lk.unlock();
|
||||||
if(p(*next->data))
|
if (p(*next->data)) {
|
||||||
{
|
return next->data;
|
||||||
std::unique_ptr<node> old_next=std::move(current->next);
|
}
|
||||||
current->next=std::move(next->next);
|
current = next;
|
||||||
next_lk.unlock();
|
lk = std::move(next_lk);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lk.unlock();
|
|
||||||
current=next;
|
|
||||||
lk=std::move(next_lk);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return std::shared_ptr<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Predicate>
|
||||||
|
void remove_if(Predicate p)
|
||||||
|
{
|
||||||
|
node* current = &head;
|
||||||
|
std::unique_lock<std::mutex> lk(head.m);
|
||||||
|
while (node* const next = current->next.get()) {
|
||||||
|
std::unique_lock<std::mutex> next_lk(next->m);
|
||||||
|
if (p(*next->data)) {
|
||||||
|
std::unique_ptr<node> old_next = std::move(current->next);
|
||||||
|
current->next = std::move(next->next);
|
||||||
|
next_lk.unlock();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
lk.unlock();
|
||||||
|
current = next;
|
||||||
|
lk = std::move(next_lk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,72 +1,70 @@
|
|||||||
#include <queue>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_queue
|
class threadsafe_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
mutable std::mutex mut;
|
mutable std::mutex mut;
|
||||||
std::queue<T> data_queue;
|
std::queue<T> data_queue;
|
||||||
std::condition_variable data_cond;
|
std::condition_variable data_cond;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
threadsafe_queue()
|
threadsafe_queue() {}
|
||||||
{}
|
|
||||||
|
|
||||||
void push(T new_value)
|
void push(T new_value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
data_queue.push(std::move(new_value));
|
data_queue.push(std::move(new_value));
|
||||||
data_cond.notify_one();
|
data_cond.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_and_pop(T& value)
|
void wait_and_pop(T& value)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
data_cond.wait(lk, [this] { return !data_queue.empty(); });
|
||||||
value=std::move(data_queue.front());
|
value = std::move(data_queue.front());
|
||||||
data_queue.pop();
|
data_queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<T> wait_and_pop()
|
std::shared_ptr<T> wait_and_pop()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
data_cond.wait(lk, [this] { return !data_queue.empty(); });
|
||||||
std::shared_ptr<T> res(
|
std::shared_ptr<T> res(std::make_shared<T>(std::move(data_queue.front())));
|
||||||
std::make_shared<T>(std::move(data_queue.front())));
|
data_queue.pop();
|
||||||
data_queue.pop();
|
return res;
|
||||||
return res;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool try_pop(T& value)
|
bool try_pop(T& value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
if(data_queue.empty())
|
if (data_queue.empty())
|
||||||
return false;
|
return false;
|
||||||
value=std::move(data_queue.front());
|
value = std::move(data_queue.front());
|
||||||
data_queue.pop();
|
data_queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop()
|
std::shared_ptr<T> try_pop()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
if(data_queue.empty())
|
if (data_queue.empty())
|
||||||
return std::shared_ptr<T>();
|
return std::shared_ptr<T>();
|
||||||
std::shared_ptr<T> res(
|
std::shared_ptr<T> res(std::make_shared<T>(std::move(data_queue.front())));
|
||||||
std::make_shared<T>(std::move(data_queue.front())));
|
data_queue.pop();
|
||||||
data_queue.pop();
|
return res;
|
||||||
return res;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
return data_queue.empty();
|
return data_queue.empty();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int main()
|
int
|
||||||
|
main()
|
||||||
{
|
{
|
||||||
threadsafe_queue<int> rq;
|
threadsafe_queue<int> rq;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,65 @@
|
|||||||
#include <queue>
|
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_queue
|
class threadsafe_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
mutable std::mutex mut;
|
mutable std::mutex mut;
|
||||||
std::queue<std::shared_ptr<T> > data_queue;
|
std::queue<std::shared_ptr<T>> data_queue;
|
||||||
std::condition_variable data_cond;
|
std::condition_variable data_cond;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
threadsafe_queue()
|
threadsafe_queue() {}
|
||||||
{}
|
|
||||||
|
|
||||||
void wait_and_pop(T& value)
|
void wait_and_pop(T& value)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
data_cond.wait(lk, [this] { return !data_queue.empty(); });
|
||||||
value=std::move(*data_queue.front());
|
value = std::move(*data_queue.front());
|
||||||
data_queue.pop();
|
data_queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool try_pop(T& value)
|
bool try_pop(T& value)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
if(data_queue.empty())
|
if (data_queue.empty())
|
||||||
return false;
|
return false;
|
||||||
value=std::move(*data_queue.front());
|
value = std::move(*data_queue.front());
|
||||||
data_queue.pop();
|
data_queue.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<T> wait_and_pop()
|
std::shared_ptr<T> wait_and_pop()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lk(mut);
|
std::unique_lock<std::mutex> lk(mut);
|
||||||
data_cond.wait(lk,[this]{return !data_queue.empty();});
|
data_cond.wait(lk, [this] { return !data_queue.empty(); });
|
||||||
std::shared_ptr<T> res=data_queue.front();
|
std::shared_ptr<T> res = data_queue.front();
|
||||||
data_queue.pop();
|
data_queue.pop();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop()
|
std::shared_ptr<T> try_pop()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
if(data_queue.empty())
|
if (data_queue.empty())
|
||||||
return std::shared_ptr<T>();
|
return std::shared_ptr<T>();
|
||||||
std::shared_ptr<T> res=data_queue.front();
|
std::shared_ptr<T> res = data_queue.front();
|
||||||
data_queue.pop();
|
data_queue.pop();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty() const
|
bool empty() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
return data_queue.empty();
|
return data_queue.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(T new_value)
|
|
||||||
{
|
|
||||||
std::shared_ptr<T> data(
|
|
||||||
std::make_shared<T>(std::move(new_value)));
|
|
||||||
std::lock_guard<std::mutex> lk(mut);
|
|
||||||
data_queue.push(data);
|
|
||||||
data_cond.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
void push(T new_value)
|
||||||
|
{
|
||||||
|
std::shared_ptr<T> data(std::make_shared<T>(std::move(new_value)));
|
||||||
|
std::lock_guard<std::mutex> lk(mut);
|
||||||
|
data_queue.push(data);
|
||||||
|
data_cond.notify_one();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,56 +1,45 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class queue
|
class queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node
|
struct node {
|
||||||
{
|
T data;
|
||||||
T data;
|
std::unique_ptr<node> next;
|
||||||
std::unique_ptr<node> next;
|
|
||||||
|
node(T data_) : data(std::move(data_)) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<node> head;
|
||||||
|
node* tail;
|
||||||
|
|
||||||
node(T data_):
|
|
||||||
data(std::move(data_))
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unique_ptr<node> head;
|
|
||||||
node* tail;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
queue():
|
queue() : tail(nullptr) {}
|
||||||
tail(nullptr)
|
|
||||||
{}
|
|
||||||
|
|
||||||
queue(const queue& other)=delete;
|
queue(const queue& other) = delete;
|
||||||
queue& operator=(const queue& other)=delete;
|
queue& operator=(const queue& other) = delete;
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop()
|
std::shared_ptr<T> try_pop()
|
||||||
{
|
{
|
||||||
if(!head)
|
if (!head) {
|
||||||
{
|
return std::shared_ptr<T>();
|
||||||
return std::shared_ptr<T>();
|
|
||||||
}
|
|
||||||
std::shared_ptr<T> const res(
|
|
||||||
std::make_shared<T>(std::move(head->data)));
|
|
||||||
std::unique_ptr<node> const old_head=std::move(head);
|
|
||||||
head=std::move(old_head->next);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<T> const res(std::make_shared<T>(std::move(head->data)));
|
||||||
void push(T new_value)
|
std::unique_ptr<node> const old_head = std::move(head);
|
||||||
{
|
head = std::move(old_head->next);
|
||||||
std::unique_ptr<node> p(new node(std::move(new_value)));
|
return res;
|
||||||
node* const new_tail=p.get();
|
}
|
||||||
if(tail)
|
|
||||||
{
|
void push(T new_value)
|
||||||
tail->next=std::move(p);
|
{
|
||||||
}
|
std::unique_ptr<node> p(new node(std::move(new_value)));
|
||||||
else
|
node* const new_tail = p.get();
|
||||||
{
|
if (tail) {
|
||||||
head=std::move(p);
|
tail->next = std::move(p);
|
||||||
}
|
|
||||||
tail=new_tail;
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
head = std::move(p);
|
||||||
|
}
|
||||||
|
tail = new_tail;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,46 +1,39 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class queue
|
class queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node
|
struct node {
|
||||||
{
|
std::shared_ptr<T> data;
|
||||||
std::shared_ptr<T> data;
|
std::unique_ptr<node> next;
|
||||||
std::unique_ptr<node> next;
|
};
|
||||||
};
|
|
||||||
|
std::unique_ptr<node> head;
|
||||||
std::unique_ptr<node> head;
|
node* tail;
|
||||||
node* tail;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
queue():
|
queue() : head(new node), tail(head.get()) {}
|
||||||
head(new node),tail(head.get())
|
|
||||||
{}
|
|
||||||
|
|
||||||
queue(const queue& other)=delete;
|
queue(const queue& other) = delete;
|
||||||
queue& operator=(const queue& other)=delete;
|
queue& operator=(const queue& other) = delete;
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop()
|
std::shared_ptr<T> try_pop()
|
||||||
{
|
{
|
||||||
if(head.get()==tail)
|
if (head.get() == tail) {
|
||||||
{
|
return std::shared_ptr<T>();
|
||||||
return std::shared_ptr<T>();
|
|
||||||
}
|
|
||||||
std::shared_ptr<T> const res(head->data);
|
|
||||||
std::unique_ptr<node> const old_head=std::move(head);
|
|
||||||
head=std::move(old_head->next);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void push(T new_value)
|
|
||||||
{
|
|
||||||
std::shared_ptr<T> new_data(
|
|
||||||
std::make_shared<T>(std::move(new_value)));
|
|
||||||
std::unique_ptr<node> p(new node);
|
|
||||||
tail->data=new_data;
|
|
||||||
node* const new_tail=p.get();
|
|
||||||
tail->next=std::move(p);
|
|
||||||
tail=new_tail;
|
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<T> const res(head->data);
|
||||||
|
std::unique_ptr<node> const old_head = std::move(head);
|
||||||
|
head = std::move(old_head->next);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void push(T new_value)
|
||||||
|
{
|
||||||
|
std::shared_ptr<T> new_data(std::make_shared<T>(std::move(new_value)));
|
||||||
|
std::unique_ptr<node> p(new node);
|
||||||
|
tail->data = new_data;
|
||||||
|
node* const new_tail = p.get();
|
||||||
|
tail->next = std::move(p);
|
||||||
|
tail = new_tail;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,64 +1,56 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_queue
|
class threadsafe_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node
|
struct node {
|
||||||
{
|
std::shared_ptr<T> data;
|
||||||
std::shared_ptr<T> data;
|
std::unique_ptr<node> next;
|
||||||
std::unique_ptr<node> next;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
std::mutex head_mutex;
|
|
||||||
std::unique_ptr<node> head;
|
|
||||||
std::mutex tail_mutex;
|
|
||||||
node* tail;
|
|
||||||
|
|
||||||
node* get_tail()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
|
||||||
return tail;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<node> pop_head()
|
std::mutex head_mutex;
|
||||||
{
|
std::unique_ptr<node> head;
|
||||||
std::lock_guard<std::mutex> head_lock(head_mutex);
|
std::mutex tail_mutex;
|
||||||
if(head.get()==get_tail())
|
node* tail;
|
||||||
{
|
|
||||||
return nullptr;
|
node* get_tail()
|
||||||
}
|
{
|
||||||
std::unique_ptr<node> const old_head=std::move(head);
|
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||||
head=std::move(old_head->next);
|
return tail;
|
||||||
return old_head;
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<node> pop_head()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> head_lock(head_mutex);
|
||||||
|
if (head.get() == get_tail()) {
|
||||||
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
std::unique_ptr<node> const old_head = std::move(head);
|
||||||
|
head = std::move(old_head->next);
|
||||||
|
return old_head;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
threadsafe_queue():
|
threadsafe_queue() : head(new node), tail(head.get()) {}
|
||||||
head(new node),tail(head.get())
|
|
||||||
{}
|
|
||||||
|
|
||||||
threadsafe_queue(const threadsafe_queue& other)=delete;
|
threadsafe_queue(const threadsafe_queue& other) = delete;
|
||||||
threadsafe_queue& operator=(const threadsafe_queue& other)=delete;
|
threadsafe_queue& operator=(const threadsafe_queue& other) = delete;
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop()
|
std::shared_ptr<T> try_pop()
|
||||||
{
|
{
|
||||||
std::unique_ptr<node> old_head=pop_head();
|
std::unique_ptr<node> old_head = pop_head();
|
||||||
return old_head?old_head->data:std::shared_ptr<T>();
|
return old_head ? old_head->data : std::shared_ptr<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
void push(T new_value)
|
void push(T new_value)
|
||||||
{
|
{
|
||||||
std::shared_ptr<T> new_data(
|
std::shared_ptr<T> new_data(std::make_shared<T>(std::move(new_value)));
|
||||||
std::make_shared<T>(std::move(new_value)));
|
std::unique_ptr<node> p(new node);
|
||||||
std::unique_ptr<node> p(new node);
|
node* const new_tail = p.get();
|
||||||
node* const new_tail=p.get();
|
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
tail->data = new_data;
|
||||||
tail->data=new_data;
|
tail->next = std::move(p);
|
||||||
tail->next=std::move(p);
|
tail = new_tail;
|
||||||
tail=new_tail;
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,26 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_queue
|
class threadsafe_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node
|
struct node {
|
||||||
{
|
std::shared_ptr<T> data;
|
||||||
std::shared_ptr<T> data;
|
std::unique_ptr<node> next;
|
||||||
std::unique_ptr<node> next;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
std::mutex head_mutex;
|
|
||||||
std::unique_ptr<node> head;
|
|
||||||
std::mutex tail_mutex;
|
|
||||||
node* tail;
|
|
||||||
std::condition_variable data_cond;
|
|
||||||
public:
|
|
||||||
threadsafe_queue():
|
|
||||||
head(new node),tail(head.get())
|
|
||||||
{}
|
|
||||||
threadsafe_queue(const threadsafe_queue& other)=delete;
|
|
||||||
threadsafe_queue& operator=(const threadsafe_queue& other)=delete;
|
|
||||||
|
|
||||||
std::shared_ptr<T> try_pop();
|
std::mutex head_mutex;
|
||||||
bool try_pop(T& value);
|
std::unique_ptr<node> head;
|
||||||
std::shared_ptr<T> wait_and_pop();
|
std::mutex tail_mutex;
|
||||||
void wait_and_pop(T& value);
|
node* tail;
|
||||||
void push(T new_value);
|
std::condition_variable data_cond;
|
||||||
void empty();
|
|
||||||
|
public:
|
||||||
|
threadsafe_queue() : head(new node), tail(head.get()) {}
|
||||||
|
threadsafe_queue(const threadsafe_queue& other) = delete;
|
||||||
|
threadsafe_queue& operator=(const threadsafe_queue& other) = delete;
|
||||||
|
|
||||||
|
std::shared_ptr<T> try_pop();
|
||||||
|
bool try_pop(T& value);
|
||||||
|
std::shared_ptr<T> wait_and_pop();
|
||||||
|
void wait_and_pop(T& value);
|
||||||
|
void push(T new_value);
|
||||||
|
void empty();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
void threadsafe_queue<T>::push(T new_value)
|
void
|
||||||
|
threadsafe_queue<T>::push(T new_value)
|
||||||
{
|
{
|
||||||
std::shared_ptr<T> new_data(
|
std::shared_ptr<T> new_data(std::make_shared<T>(std::move(new_value)));
|
||||||
std::make_shared<T>(std::move(new_value)));
|
std::unique_ptr<node> p(new node);
|
||||||
std::unique_ptr<node> p(new node);
|
{
|
||||||
{
|
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
tail->data = new_data;
|
||||||
tail->data=new_data;
|
node* const new_tail = p.get();
|
||||||
node* const new_tail=p.get();
|
tail->next = std::move(p);
|
||||||
tail->next=std::move(p);
|
tail = new_tail;
|
||||||
tail=new_tail;
|
}
|
||||||
}
|
data_cond.notify_one();
|
||||||
data_cond.notify_one();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,48 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
class threadsafe_queue
|
class threadsafe_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
node* get_tail()
|
node* get_tail()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
std::lock_guard<std::mutex> tail_lock(tail_mutex);
|
||||||
return tail;
|
return tail;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<node> pop_head()
|
std::unique_ptr<node> pop_head()
|
||||||
{
|
{
|
||||||
std::unique_ptr<node> const old_head=std::move(head);
|
std::unique_ptr<node> const old_head = std::move(head);
|
||||||
head=std::move(old_head->next);
|
head = std::move(old_head->next);
|
||||||
return old_head;
|
return old_head;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_lock<std::mutex> wait_for_data()
|
std::unique_lock<std::mutex> wait_for_data()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> head_lock(head_mutex);
|
std::unique_lock<std::mutex> head_lock(head_mutex);
|
||||||
data_cond.wait(head_lock,[&]{return head!=get_tail();});
|
data_cond.wait(head_lock, [&] { return head != get_tail(); });
|
||||||
return std::move(head_lock);
|
return std::move(head_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<node> wait_pop_head()
|
std::unique_ptr<node> wait_pop_head()
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> head_lock(wait_for_data());
|
std::unique_lock<std::mutex> head_lock(wait_for_data());
|
||||||
return pop_head();
|
return pop_head();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<node> wait_pop_head(T& value)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> head_lock(wait_for_data());
|
||||||
|
value = std::move(*head->data);
|
||||||
|
return pop_head();
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<node> wait_pop_head(T& value)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> head_lock(wait_for_data());
|
|
||||||
value=std::move(*head->data);
|
|
||||||
return pop_head();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<T> wait_and_pop()
|
std::shared_ptr<T> wait_and_pop()
|
||||||
{
|
{
|
||||||
std::unique_ptr<node> const old_head=wait_pop_head();
|
std::unique_ptr<node> const old_head = wait_pop_head();
|
||||||
return old_head->data;
|
return old_head->data;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_and_pop(T& value)
|
void wait_and_pop(T& value)
|
||||||
{
|
{
|
||||||
std::unique_ptr<node> const old_head=wait_pop_head(value);
|
std::unique_ptr<node> const old_head = wait_pop_head(value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
class spinlock_mutex
|
class spinlock_mutex {
|
||||||
{
|
std::atomic_flag flag;
|
||||||
std::atomic_flag flag;
|
|
||||||
public:
|
public:
|
||||||
spinlock_mutex():
|
spinlock_mutex() : flag(ATOMIC_FLAG_INIT) {}
|
||||||
flag(ATOMIC_FLAG_INIT)
|
void lock()
|
||||||
{}
|
{
|
||||||
void lock()
|
while (flag.test_and_set(std::memory_order_acquire))
|
||||||
{
|
;
|
||||||
while(flag.test_and_set(std::memory_order_acquire));
|
}
|
||||||
}
|
void unlock() { flag.clear(std::memory_order_release); }
|
||||||
void unlock()
|
|
||||||
{
|
|
||||||
flag.clear(std::memory_order_release);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,38 +1,37 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class lock_free_stack
|
class lock_free_stack {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node;
|
struct node;
|
||||||
struct counted_node_ptr
|
struct counted_node_ptr {
|
||||||
|
int external_count;
|
||||||
|
node* ptr;
|
||||||
|
};
|
||||||
|
struct node {
|
||||||
|
std::shared_ptr<T> data;
|
||||||
|
std::atomic<int> internal_count;
|
||||||
|
counted_node_ptr next;
|
||||||
|
node(T const& data_) : data(std::make_shared<T>(data_)), internal_count(0)
|
||||||
{
|
{
|
||||||
int external_count;
|
}
|
||||||
node* ptr;
|
};
|
||||||
};
|
std::atomic<counted_node_ptr> head;
|
||||||
struct node
|
|
||||||
{
|
|
||||||
std::shared_ptr<T> data;
|
|
||||||
std::atomic<int> internal_count;
|
|
||||||
counted_node_ptr next;
|
|
||||||
node(T const& data_):
|
|
||||||
data(std::make_shared<T>(data_)),
|
|
||||||
internal_count(0)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
std::atomic<counted_node_ptr> head;
|
|
||||||
public:
|
public:
|
||||||
~lock_free_stack()
|
~lock_free_stack()
|
||||||
{
|
{
|
||||||
while(pop());
|
while (pop())
|
||||||
}
|
;
|
||||||
void push(T const& data)
|
}
|
||||||
{
|
void push(T const& data)
|
||||||
counted_node_ptr new_node;
|
{
|
||||||
new_node.ptr=new node(data);
|
counted_node_ptr new_node;
|
||||||
new_node.external_count=1;
|
new_node.ptr = new node(data);
|
||||||
new_node.ptr->next=head.load();
|
new_node.external_count = 1;
|
||||||
while(!head.compare_exchange_weak(new_node.ptr->next,new_node));
|
new_node.ptr->next = head.load();
|
||||||
}
|
while (!head.compare_exchange_weak(new_node.ptr->next, new_node))
|
||||||
|
;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,46 +1,38 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
class lock_free_stack
|
class lock_free_stack {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
void increase_head_count(counted_node_ptr& old_counter)
|
void increase_head_count(counted_node_ptr& old_counter)
|
||||||
{
|
{
|
||||||
counted_node_ptr new_counter;
|
counted_node_ptr new_counter;
|
||||||
do
|
do {
|
||||||
{
|
new_counter = old_counter;
|
||||||
new_counter=old_counter;
|
++new_counter.external_count;
|
||||||
++new_counter.external_count;
|
} while (!head.compare_exchange_strong(old_counter, new_counter));
|
||||||
}
|
old_counter.external_count = new_counter.external_count;
|
||||||
while(!head.compare_exchange_strong(old_counter,new_counter));
|
}
|
||||||
old_counter.external_count=new_counter.external_count;
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
std::shared_ptr<T> pop()
|
std::shared_ptr<T> pop()
|
||||||
{
|
{
|
||||||
counted_node_ptr old_head=head.load();
|
counted_node_ptr old_head = head.load();
|
||||||
for(;;)
|
for (;;) {
|
||||||
{
|
increase_head_count(old_head);
|
||||||
increase_head_count(old_head);
|
node* const ptr = old_head.ptr;
|
||||||
node* const ptr=old_head.ptr;
|
if (!ptr) {
|
||||||
if(!ptr)
|
return std::shared_ptr<T>();
|
||||||
{
|
}
|
||||||
return std::shared_ptr<T>();
|
if (head.compare_exchange_strong(old_head, ptr->next)) {
|
||||||
}
|
std::shared_ptr<T> res;
|
||||||
if(head.compare_exchange_strong(old_head,ptr->next))
|
res.swap(ptr->data);
|
||||||
{
|
int const count_increase = old_head.external_count - 2;
|
||||||
std::shared_ptr<T> res;
|
if (ptr->internal_count.fetch_add(count_increase) == -count_increase) {
|
||||||
res.swap(ptr->data);
|
delete ptr;
|
||||||
int const count_increase=old_head.external_count-2;
|
|
||||||
if(ptr->internal_count.fetch_add(count_increase)==
|
|
||||||
-count_increase)
|
|
||||||
{
|
|
||||||
delete ptr;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
else if(ptr->internal_count.fetch_sub(1)==1)
|
|
||||||
{
|
|
||||||
delete ptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if (ptr->internal_count.fetch_sub(1) == 1) {
|
||||||
|
delete ptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,88 +1,78 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class lock_free_stack
|
class lock_free_stack {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node;
|
struct node;
|
||||||
struct counted_node_ptr
|
struct counted_node_ptr {
|
||||||
|
int external_count;
|
||||||
|
node* ptr;
|
||||||
|
};
|
||||||
|
struct node {
|
||||||
|
std::shared_ptr<T> data;
|
||||||
|
std::atomic<int> internal_count;
|
||||||
|
counted_node_ptr next;
|
||||||
|
node(T const& data_) : data(std::make_shared<T>(data_)), internal_count(0)
|
||||||
{
|
{
|
||||||
int external_count;
|
|
||||||
node* ptr;
|
|
||||||
};
|
|
||||||
struct node
|
|
||||||
{
|
|
||||||
std::shared_ptr<T> data;
|
|
||||||
std::atomic<int> internal_count;
|
|
||||||
counted_node_ptr next;
|
|
||||||
node(T const& data_):
|
|
||||||
data(std::make_shared<T>(data_)),
|
|
||||||
internal_count(0)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
std::atomic<counted_node_ptr> head;
|
|
||||||
void increase_head_count(counted_node_ptr& old_counter)
|
|
||||||
{
|
|
||||||
counted_node_ptr new_counter;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
new_counter=old_counter;
|
|
||||||
++new_counter.external_count;
|
|
||||||
}
|
|
||||||
while(!head.compare_exchange_strong(
|
|
||||||
old_counter,new_counter,
|
|
||||||
std::memory_order_acquire,
|
|
||||||
std::memory_order_relaxed));
|
|
||||||
old_counter.external_count=new_counter.external_count;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
std::atomic<counted_node_ptr> head;
|
||||||
|
void increase_head_count(counted_node_ptr& old_counter)
|
||||||
|
{
|
||||||
|
counted_node_ptr new_counter;
|
||||||
|
do {
|
||||||
|
new_counter = old_counter;
|
||||||
|
++new_counter.external_count;
|
||||||
|
} while (!head.compare_exchange_strong(old_counter,
|
||||||
|
new_counter,
|
||||||
|
std::memory_order_acquire,
|
||||||
|
std::memory_order_relaxed));
|
||||||
|
old_counter.external_count = new_counter.external_count;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~lock_free_stack()
|
~lock_free_stack()
|
||||||
{
|
{
|
||||||
while(pop());
|
while (pop())
|
||||||
}
|
;
|
||||||
void push(T const& data)
|
}
|
||||||
{
|
void push(T const& data)
|
||||||
counted_node_ptr new_node;
|
{
|
||||||
new_node.ptr=new node(data);
|
counted_node_ptr new_node;
|
||||||
new_node.external_count=1;
|
new_node.ptr = new node(data);
|
||||||
new_node.ptr->next=head.load(std::memory_order_relaxed)
|
new_node.external_count = 1;
|
||||||
while(!head.compare_exchange_weak(
|
new_node.ptr->next = head.load(std::memory_order_relaxed) while (
|
||||||
new_node.ptr->next,new_node,
|
!head.compare_exchange_weak(new_node.ptr->next,
|
||||||
std::memory_order_release,
|
new_node,
|
||||||
std::memory_order_relaxed));
|
std::memory_order_release,
|
||||||
}
|
std::memory_order_relaxed));
|
||||||
std::shared_ptr<T> pop()
|
}
|
||||||
{
|
std::shared_ptr<T> pop()
|
||||||
counted_node_ptr old_head=
|
{
|
||||||
head.load(std::memory_order_relaxed);
|
counted_node_ptr old_head = head.load(std::memory_order_relaxed);
|
||||||
for(;;)
|
for (;;) {
|
||||||
{
|
increase_head_count(old_head);
|
||||||
increase_head_count(old_head);
|
node* const ptr = old_head.ptr;
|
||||||
node* const ptr=old_head.ptr;
|
if (!ptr) {
|
||||||
if(!ptr)
|
return std::shared_ptr<T>();
|
||||||
{
|
}
|
||||||
return std::shared_ptr<T>();
|
if (head.compare_exchange_strong(
|
||||||
}
|
old_head, ptr->next, std::memory_order_relaxed)) {
|
||||||
if(head.compare_exchange_strong(
|
std::shared_ptr<T> res;
|
||||||
old_head,ptr->next,std::memory_order_relaxed))
|
res.swap(ptr->data);
|
||||||
{
|
int const count_increase = old_head.external_count - 2;
|
||||||
std::shared_ptr<T> res;
|
if (ptr->internal_count.fetch_add(
|
||||||
res.swap(ptr->data);
|
count_increase, std::memory_order_release) == -count_increase) {
|
||||||
int const count_increase=old_head.external_count-2;
|
delete ptr;
|
||||||
if(ptr->internal_count.fetch_add(
|
|
||||||
count_increase,std::memory_order_release)==-count_increase)
|
|
||||||
{
|
|
||||||
delete ptr;
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
else if(ptr->internal_count.fetch_add(
|
|
||||||
-1,std::memory_order_relaxed)==1)
|
|
||||||
{
|
|
||||||
ptr->internal_count.load(std::memory_order_acquire);
|
|
||||||
delete ptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if (ptr->internal_count.fetch_add(-1, std::memory_order_relaxed) ==
|
||||||
|
1) {
|
||||||
|
ptr->internal_count.load(std::memory_order_acquire);
|
||||||
|
delete ptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,62 +1,54 @@
|
|||||||
#include <memory>
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class lock_free_queue
|
class lock_free_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node
|
struct node {
|
||||||
{
|
std::shared_ptr<T> data;
|
||||||
std::shared_ptr<T> data;
|
node* next;
|
||||||
node* next;
|
node() : next(nullptr) {}
|
||||||
node():
|
};
|
||||||
next(nullptr)
|
std::atomic<node*> head;
|
||||||
{}
|
std::atomic<node*> tail;
|
||||||
};
|
node* pop_head()
|
||||||
std::atomic<node*> head;
|
{
|
||||||
std::atomic<node*> tail;
|
node* const old_head = head.load();
|
||||||
node* pop_head()
|
if (old_head == tail.load()) {
|
||||||
{
|
return nullptr;
|
||||||
node* const old_head=head.load();
|
|
||||||
if(old_head==tail.load())
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
head.store(old_head->next);
|
|
||||||
return old_head;
|
|
||||||
}
|
}
|
||||||
|
head.store(old_head->next);
|
||||||
|
return old_head;
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
lock_free_queue():
|
lock_free_queue() : head(new node), tail(head.load()) {}
|
||||||
head(new node),tail(head.load())
|
lock_free_queue(const lock_free_queue& other) = delete;
|
||||||
{}
|
lock_free_queue& operator=(const lock_free_queue& other) = delete;
|
||||||
lock_free_queue(const lock_free_queue& other)=delete;
|
~lock_free_queue()
|
||||||
lock_free_queue& operator=(const lock_free_queue& other)=delete;
|
{
|
||||||
~lock_free_queue()
|
while (node* const old_head = head.load()) {
|
||||||
{
|
head.store(old_head->next);
|
||||||
while(node* const old_head=head.load())
|
delete old_head;
|
||||||
{
|
|
||||||
head.store(old_head->next);
|
|
||||||
delete old_head;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
std::shared_ptr<T> pop()
|
}
|
||||||
{
|
std::shared_ptr<T> pop()
|
||||||
node* old_head=pop_head();
|
{
|
||||||
if(!old_head)
|
node* old_head = pop_head();
|
||||||
{
|
if (!old_head) {
|
||||||
return std::shared_ptr<T>();
|
return std::shared_ptr<T>();
|
||||||
}
|
|
||||||
std::shared_ptr<T> const res(old_head->data);
|
|
||||||
delete old_head;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
void push(T new_value)
|
|
||||||
{
|
|
||||||
std::shared_ptr<T> new_data(std::make_shared<T>(new_value));
|
|
||||||
node* p=new node;
|
|
||||||
node* const old_tail=tail.load();
|
|
||||||
old_tail->data.swap(new_data);
|
|
||||||
old_tail->next=p;
|
|
||||||
tail.store(p);
|
|
||||||
}
|
}
|
||||||
|
std::shared_ptr<T> const res(old_head->data);
|
||||||
|
delete old_head;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
void push(T new_value)
|
||||||
|
{
|
||||||
|
std::shared_ptr<T> new_data(std::make_shared<T>(new_value));
|
||||||
|
node* p = new node;
|
||||||
|
node* const old_tail = tail.load();
|
||||||
|
old_tail->data.swap(new_data);
|
||||||
|
old_tail->next = p;
|
||||||
|
tail.store(p);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
void push(T new_value)
|
void
|
||||||
|
push(T new_value)
|
||||||
{
|
{
|
||||||
std::unique_ptr<T> new_data(new T(new_value));
|
std::unique_ptr<T> new_data(new T(new_value));
|
||||||
counted_node_ptr new_next;
|
counted_node_ptr new_next;
|
||||||
new_next.ptr=new node;
|
new_next.ptr = new node;
|
||||||
new_next.external_count=1;
|
new_next.external_count = 1;
|
||||||
for(;;)
|
for (;;) {
|
||||||
{
|
node* const old_tail = tail.load();
|
||||||
node* const old_tail=tail.load();
|
T* old_data = nullptr;
|
||||||
T* old_data=nullptr;
|
if (old_tail->data.compare_exchange_strong(old_data, new_data.get())) {
|
||||||
if(old_tail->data.compare_exchange_strong(
|
old_tail->next = new_next;
|
||||||
old_data,new_data.get()))
|
tail.store(new_next.ptr);
|
||||||
{
|
new_data.release();
|
||||||
old_tail->next=new_next;
|
break;
|
||||||
tail.store(new_next.ptr);
|
|
||||||
new_data.release();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,54 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class lock_free_queue
|
class lock_free_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node;
|
struct node;
|
||||||
struct counted_node_ptr
|
struct counted_node_ptr {
|
||||||
|
int external_count;
|
||||||
|
node* ptr;
|
||||||
|
};
|
||||||
|
std::atomic<counted_node_ptr> head;
|
||||||
|
std::atomic<counted_node_ptr> tail;
|
||||||
|
struct node_counter {
|
||||||
|
unsigned internal_count : 30;
|
||||||
|
unsigned external_counters : 2;
|
||||||
|
};
|
||||||
|
struct node {
|
||||||
|
std::atomic<T*> data;
|
||||||
|
std::atomic<node_counter> count;
|
||||||
|
counted_node_ptr next;
|
||||||
|
node()
|
||||||
{
|
{
|
||||||
int external_count;
|
node_counter new_count;
|
||||||
node* ptr;
|
new_count.internal_count = 0;
|
||||||
};
|
new_count.external_counters = 2;
|
||||||
std::atomic<counted_node_ptr> head;
|
count.store(new_count);
|
||||||
std::atomic<counted_node_ptr> tail;
|
next.ptr = nullptr;
|
||||||
struct node_counter
|
next.external_count = 0;
|
||||||
{
|
|
||||||
unsigned internal_count:30;
|
|
||||||
unsigned external_counters:2;
|
|
||||||
};
|
|
||||||
struct node
|
|
||||||
{
|
|
||||||
std::atomic<T*> data;
|
|
||||||
std::atomic<node_counter> count;
|
|
||||||
counted_node_ptr next;
|
|
||||||
node()
|
|
||||||
{
|
|
||||||
node_counter new_count;
|
|
||||||
new_count.internal_count=0;
|
|
||||||
new_count.external_counters=2;
|
|
||||||
count.store(new_count);
|
|
||||||
next.ptr=nullptr;
|
|
||||||
next.external_count=0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
public:
|
|
||||||
void push(T new_value)
|
|
||||||
{
|
|
||||||
std::unique_ptr<T> new_data(new T(new_value));
|
|
||||||
counted_node_ptr new_next;
|
|
||||||
new_next.ptr=new node;
|
|
||||||
new_next.external_count=1;
|
|
||||||
counted_node_ptr old_tail=tail.load();
|
|
||||||
for(;;)
|
|
||||||
{
|
|
||||||
increase_external_count(tail,old_tail);
|
|
||||||
T* old_data=nullptr;
|
|
||||||
if(old_tail.ptr->data.compare_exchange_strong(
|
|
||||||
old_data,new_data.get()))
|
|
||||||
{
|
|
||||||
old_tail.ptr->next=new_next;
|
|
||||||
old_tail=tail.exchange(new_next);
|
|
||||||
free_external_counter(old_tail);
|
|
||||||
new_data.release();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
old_tail.ptr->release_ref();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
void push(T new_value)
|
||||||
|
{
|
||||||
|
std::unique_ptr<T> new_data(new T(new_value));
|
||||||
|
counted_node_ptr new_next;
|
||||||
|
new_next.ptr = new node;
|
||||||
|
new_next.external_count = 1;
|
||||||
|
counted_node_ptr old_tail = tail.load();
|
||||||
|
for (;;) {
|
||||||
|
increase_external_count(tail, old_tail);
|
||||||
|
T* old_data = nullptr;
|
||||||
|
if (old_tail.ptr->data.compare_exchange_strong(old_data,
|
||||||
|
new_data.get())) {
|
||||||
|
old_tail.ptr->next = new_next;
|
||||||
|
old_tail = tail.exchange(new_next);
|
||||||
|
free_external_counter(old_tail);
|
||||||
|
new_data.release();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
old_tail.ptr->release_ref();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,31 +1,27 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
class lock_free_queue
|
class lock_free_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node
|
struct node {
|
||||||
{
|
void release_ref();
|
||||||
void release_ref();
|
};
|
||||||
};
|
|
||||||
public:
|
public:
|
||||||
std::unique_ptr<T> pop()
|
std::unique_ptr<T> pop()
|
||||||
{
|
{
|
||||||
counted_node_ptr old_head=head.load(std::memory_order_relaxed);
|
counted_node_ptr old_head = head.load(std::memory_order_relaxed);
|
||||||
for(;;)
|
for (;;) {
|
||||||
{
|
increase_external_count(head, old_head);
|
||||||
increase_external_count(head,old_head);
|
node* const ptr = old_head.ptr;
|
||||||
node* const ptr=old_head.ptr;
|
if (ptr == tail.load().ptr) {
|
||||||
if(ptr==tail.load().ptr)
|
ptr->release_ref();
|
||||||
{
|
return std::unique_ptr<T>();
|
||||||
ptr->release_ref();
|
}
|
||||||
return std::unique_ptr<T>();
|
if (head.compare_exchange_strong(old_head, ptr->next)) {
|
||||||
}
|
T* const res = ptr->data.exchange(nullptr);
|
||||||
if(head.compare_exchange_strong(old_head,ptr->next))
|
free_external_counter(old_head);
|
||||||
{
|
return std::unique_ptr<T>(res);
|
||||||
T* const res=ptr->data.exchange(nullptr);
|
}
|
||||||
free_external_counter(old_head);
|
ptr->release_ref();
|
||||||
return std::unique_ptr<T>(res);
|
|
||||||
}
|
|
||||||
ptr->release_ref();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,27 +1,21 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
class lock_free_queue
|
class lock_free_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node
|
struct node {
|
||||||
|
void release_ref()
|
||||||
{
|
{
|
||||||
void release_ref()
|
node_counter old_counter = count.load(std::memory_order_relaxed);
|
||||||
{
|
node_counter new_counter;
|
||||||
node_counter old_counter=
|
do {
|
||||||
count.load(std::memory_order_relaxed);
|
new_counter = old_counter;
|
||||||
node_counter new_counter;
|
--new_counter.internal_count;
|
||||||
do
|
} while (!count.compare_exchange_strong(old_counter,
|
||||||
{
|
new_counter,
|
||||||
new_counter=old_counter;
|
std::memory_order_acquire,
|
||||||
--new_counter.internal_count;
|
std::memory_order_relaxed));
|
||||||
}
|
if (!new_counter.internal_count && !new_counter.external_counters) {
|
||||||
while(!count.compare_exchange_strong(
|
delete this;
|
||||||
old_counter,new_counter,
|
}
|
||||||
std::memory_order_acquire,std::memory_order_relaxed));
|
}
|
||||||
if(!new_counter.internal_count &&
|
};
|
||||||
!new_counter.external_counters)
|
|
||||||
{
|
|
||||||
delete this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +1,17 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
class lock_free_queue
|
class lock_free_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
static void increase_external_count(
|
static void increase_external_count(std::atomic<counted_node_ptr>& counter,
|
||||||
std::atomic<counted_node_ptr>& counter,
|
counted_node_ptr& old_counter)
|
||||||
counted_node_ptr& old_counter)
|
{
|
||||||
{
|
counted_node_ptr new_counter;
|
||||||
counted_node_ptr new_counter;
|
do {
|
||||||
do
|
new_counter = old_counter;
|
||||||
{
|
++new_counter.external_count;
|
||||||
new_counter=old_counter;
|
} while (!counter.compare_exchange_strong(old_counter,
|
||||||
++new_counter.external_count;
|
new_counter,
|
||||||
}
|
std::memory_order_acquire,
|
||||||
while(!counter.compare_exchange_strong(
|
std::memory_order_relaxed));
|
||||||
old_counter,new_counter,
|
old_counter.external_count = new_counter.external_count;
|
||||||
std::memory_order_acquire,std::memory_order_relaxed));
|
}
|
||||||
old_counter.external_count=new_counter.external_count;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,27 +1,22 @@
|
|||||||
template<typename T>
|
template <typename T>
|
||||||
class lock_free_queue
|
class lock_free_queue {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
static void free_external_counter(counted_node_ptr &old_node_ptr)
|
static void free_external_counter(counted_node_ptr& old_node_ptr)
|
||||||
{
|
{
|
||||||
node* const ptr=old_node_ptr.ptr;
|
node* const ptr = old_node_ptr.ptr;
|
||||||
int const count_increase=old_node_ptr.external_count-2;
|
int const count_increase = old_node_ptr.external_count - 2;
|
||||||
node_counter old_counter=
|
node_counter old_counter = ptr->count.load(std::memory_order_relaxed);
|
||||||
ptr->count.load(std::memory_order_relaxed);
|
node_counter new_counter;
|
||||||
node_counter new_counter;
|
do {
|
||||||
do
|
new_counter = old_counter;
|
||||||
{
|
--new_counter.external_counters;
|
||||||
new_counter=old_counter;
|
new_counter.internal_count += count_increase;
|
||||||
--new_counter.external_counters;
|
} while (!ptr->count.compare_exchange_strong(old_counter,
|
||||||
new_counter.internal_count+=count_increase;
|
new_counter,
|
||||||
}
|
std::memory_order_acquire,
|
||||||
while(!ptr->count.compare_exchange_strong(
|
std::memory_order_relaxed));
|
||||||
old_counter,new_counter,
|
if (!new_counter.internal_count && !new_counter.external_counters) {
|
||||||
std::memory_order_acquire,std::memory_order_relaxed));
|
delete ptr;
|
||||||
if(!new_counter.internal_count &&
|
|
||||||
!new_counter.external_counters)
|
|
||||||
{
|
|
||||||
delete ptr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,23 +1,21 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
class lock_free_stack
|
class lock_free_stack {
|
||||||
{
|
|
||||||
private:
|
private:
|
||||||
struct node
|
struct node {
|
||||||
{
|
T data;
|
||||||
T data;
|
node* next;
|
||||||
node* next;
|
node(T const& data_) : data(data_) {}
|
||||||
node(T const& data_):
|
};
|
||||||
data(data_)
|
std::atomic<node*> head;
|
||||||
{}
|
|
||||||
};
|
|
||||||
std::atomic<node*> head;
|
|
||||||
public:
|
public:
|
||||||
void push(T const& data)
|
void push(T const& data)
|
||||||
{
|
{
|
||||||
node* const new_node=new node(data);
|
node* const new_node = new node(data);
|
||||||
new_node->next=head.load();
|
new_node->next = head.load();
|
||||||
while(!head.compare_exchange_weak(new_node->next,new_node));
|
while (!head.compare_exchange_weak(new_node->next, new_node))
|
||||||
}
|
;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user