├── Makefile ├── paper ├── Makefile ├── astm.bib └── astm.tex ├── astm.sln ├── astm_new.hpp ├── concurrency_tests.cpp ├── astm.vcxproj.filters ├── astm_config.hpp ├── .gitattributes ├── .gitignore ├── astm.vcxproj ├── binary_tree.cpp ├── unit_tests.cpp └── astm.hpp /Makefile: -------------------------------------------------------------------------------- 1 | CXX=hpxcxx 2 | 3 | CXXFLAGS+=-std=c++1y -I. -g 4 | ADDITIONAL_SOURCES= 5 | PROGRAMS=binary_tree unit_tests concurrency_tests 6 | DIRECTORIES=build 7 | 8 | all: directories $(PROGRAMS) 9 | 10 | .PHONY: directories 11 | directories: $(DIRECTORIES)/ 12 | 13 | $(DIRECTORIES)/: 14 | mkdir -p $@ 15 | 16 | % : %.cpp 17 | $(CXX) -DASTM_HPX $(ADDITIONAL_SOURCES) $< --exe=$@ -o build/$@ $(CXXFLAGS) 18 | 19 | clean: 20 | rm -rf $(DIRECTORIES) 21 | 22 | -------------------------------------------------------------------------------- /paper/Makefile: -------------------------------------------------------------------------------- 1 | PAPER = astm 2 | PDFTEX = pdflatex 3 | PSTEX = latex 4 | 5 | default: pdf 6 | 7 | pdf: 8 | -$(PSTEX) --interaction=batchmode $(PAPER) 9 | #bibtex $(PAPER) 10 | -$(PSTEX) --interaction=batchmode $(PAPER) 11 | $(PDFTEX) $(PAPER) 12 | 13 | dvi: 14 | $(PSTEX) $(PAPER) 15 | #bibtex $(PAPER) 16 | $(PSTEX) $(PAPER) 17 | $(PSTEX) $(PAPER) 18 | 19 | ps: dvi 20 | dvips -tletter -o $(PAPER).ps $(PAPER).dvi 21 | 22 | clean: 23 | /bin/rm -f *.dvi *.log *.aux *.toc *.bbl *.blg *.out 24 | /bin/rm -f $(PAPER).ps 25 | -------------------------------------------------------------------------------- /astm.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.21005.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "astm", "astm.vcxproj", "{FC765958-F575-4708-B667-9EF952D89862}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release|Win32 = Release|Win32 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {FC765958-F575-4708-B667-9EF952D89862}.Debug|Win32.ActiveCfg = Debug|Win32 15 | {FC765958-F575-4708-B667-9EF952D89862}.Debug|Win32.Build.0 = Debug|Win32 16 | {FC765958-F575-4708-B667-9EF952D89862}.Release|Win32.ActiveCfg = Release|Win32 17 | {FC765958-F575-4708-B667-9EF952D89862}.Release|Win32.Build.0 = Release|Win32 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /astm_new.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2014 Bryce Adelstein-Lelbach 3 | // Copyright (c) 2014 Steve Brandt 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | #if !defined(ASTM_65EB3381_65AE_4527_A4E5_A87D5014A33E) 10 | #define ASTM_65EB3381_65AE_4527_A4E5_A87D5014A33E 11 | 12 | #include "astm.hpp" 13 | 14 | namespace astm 15 | { 16 | // Asynchronous object allocation 17 | /* 18 | template 19 | ASTM_FUTURE 20 | new_(shared_var& var, T* ptr, Args&&... args) 21 | { 22 | return var.queue.then( 23 | [=] (ASTM_FUTURE f) 24 | { 25 | //ptr = new T(std::forward(args)...); 26 | } 27 | ); 28 | } 29 | */ 30 | } 31 | 32 | #endif 33 | 34 | -------------------------------------------------------------------------------- /concurrency_tests.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2014 Bryce Adelstein-Lelbach 3 | // Copyright (c) 2014 Steve Brandt 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | #include 10 | 11 | #include "astm.hpp" 12 | 13 | #ifdef ASTM_HPX 14 | #include 15 | #endif 16 | 17 | using namespace astm; 18 | 19 | void cubeA(shared_var& A) 20 | { 21 | transaction t; 22 | do { 23 | auto A_ = A.get_local(t); 24 | 25 | A_ = A_ * A_; 26 | } while (!t.commit_transaction()); 27 | } 28 | 29 | int main() 30 | { 31 | { 32 | shared_var A(1); 33 | 34 | std::vector > F; 35 | 36 | for (unsigned i = 0; i < 64; ++i) 37 | F.push_back(ASTM_ASYNC(std::bind(cubeA, std::ref(A)))); 38 | 39 | for (ASTM_FUTURE& f : F) 40 | f.get(); 41 | } 42 | 43 | return ASTM_REPORT; 44 | } 45 | -------------------------------------------------------------------------------- /astm.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | 23 | 24 | Header Files 25 | 26 | 27 | Header Files 28 | 29 | 30 | -------------------------------------------------------------------------------- /astm_config.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2014 Bryce Adelstein-Lelbach 3 | // Copyright (c) 2014 Steve Brandt 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | #if !defined(ASTM_DBA88345_57B8_4CC8_A574_D5F007250E95) 10 | #define ASTM_DBA88345_57B8_4CC8_A574_D5F007250E95 11 | 12 | #if defined(ASTM_HPX) // Use hpx:: 13 | #include 14 | #include 15 | 16 | #define ASTM_MUTEX hpx::util::spinlock 17 | #define ASTM_LOCK hpx::util::spinlock::scoped_lock 18 | #define ASTM_FUTURE hpx::future 19 | #define ASTM_FUNCTION HPX_STD_FUNCTION 20 | #define ASTM_ASYNC hpx::async 21 | #define ASTM_MAKE_READY_FUTURE hpx::make_ready_future 22 | 23 | #include 24 | 25 | #define ASTM_TEST HPX_TEST 26 | #define ASTM_REPORT hpx::util::report_errors() 27 | #else // Use std:: 28 | #include 29 | #include 30 | 31 | #define ASTM_MUTEX std::mutex 32 | #define ASTM_LOCK std::unique_lock 33 | #define ASTM_FUTURE std::future 34 | #define ASTM_FUNCTION std::function 35 | #define ASTM_ASYNC std::async 36 | 37 | // Note: this is a hack, no make_ready_future in std:: 38 | #define ASTM_MAKE_READY_FUTURE std::async([](){}) 39 | 40 | #include 41 | 42 | #define ASTM_TEST assert 43 | #define ASTM_REPORT 0 44 | #endif 45 | 46 | #endif // ASTM_DBA88345_57B8_4CC8_A574_D5F007250E95 47 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 19 | !packages/*/build/ 20 | 21 | # MSTest test Results 22 | [Tt]est[Rr]esult*/ 23 | [Bb]uild[Ll]og.* 24 | 25 | *_i.c 26 | *_p.c 27 | *.ilk 28 | *.meta 29 | *.obj 30 | *.pch 31 | *.pdb 32 | *.pgc 33 | *.pgd 34 | *.rsp 35 | *.sbr 36 | *.tlb 37 | *.tli 38 | *.tlh 39 | *.tmp 40 | *.tmp_proj 41 | *.log 42 | *.vspscc 43 | *.vssscc 44 | .builds 45 | *.pidb 46 | *.log 47 | *.scc 48 | 49 | # Visual C++ cache files 50 | ipch/ 51 | *.aps 52 | *.ncb 53 | *.opensdf 54 | *.sdf 55 | *.cachefile 56 | 57 | # Visual Studio profiler 58 | *.psess 59 | *.vsp 60 | *.vspx 61 | 62 | # Guidance Automation Toolkit 63 | *.gpState 64 | 65 | # ReSharper is a .NET coding add-in 66 | _ReSharper*/ 67 | *.[Rr]e[Ss]harper 68 | 69 | # TeamCity is a build add-in 70 | _TeamCity* 71 | 72 | # DotCover is a Code Coverage Tool 73 | *.dotCover 74 | 75 | # NCrunch 76 | *.ncrunch* 77 | .*crunch*.local.xml 78 | 79 | # Installshield output folder 80 | [Ee]xpress/ 81 | 82 | # DocProject is a documentation generator add-in 83 | DocProject/buildhelp/ 84 | DocProject/Help/*.HxT 85 | DocProject/Help/*.HxC 86 | DocProject/Help/*.hhc 87 | DocProject/Help/*.hhk 88 | DocProject/Help/*.hhp 89 | DocProject/Help/Html2 90 | DocProject/Help/html 91 | 92 | # Click-Once directory 93 | publish/ 94 | 95 | # Publish Web Output 96 | *.Publish.xml 97 | 98 | # NuGet Packages Directory 99 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 100 | #packages/ 101 | 102 | # Windows Azure Build Output 103 | csx 104 | *.build.csdef 105 | 106 | # Windows Store app package directory 107 | AppPackages/ 108 | 109 | # Others 110 | sql/ 111 | *.Cache 112 | ClientBin/ 113 | [Ss]tyle[Cc]op.* 114 | ~$* 115 | *~ 116 | *.dbmdl 117 | *.[Pp]ublish.xml 118 | *.pfx 119 | *.publishsettings 120 | 121 | # RIA/Silverlight projects 122 | Generated_Code/ 123 | 124 | # Backup & report files from converting an old project file to a newer 125 | # Visual Studio version. Backup files are not needed, because we have git ;-) 126 | _UpgradeReport_Files/ 127 | Backup*/ 128 | UpgradeLog*.XML 129 | UpgradeLog*.htm 130 | 131 | # SQL Server files 132 | App_Data/*.mdf 133 | App_Data/*.ldf 134 | 135 | 136 | #LightSwitch generated files 137 | GeneratedArtifacts/ 138 | _Pvt_Extensions/ 139 | ModelManifest.xml 140 | 141 | # ========================= 142 | # Windows detritus 143 | # ========================= 144 | 145 | # Windows image file caches 146 | Thumbs.db 147 | ehthumbs.db 148 | 149 | # Folder config file 150 | Desktop.ini 151 | 152 | # Recycle Bin used on file shares 153 | $RECYCLE.BIN/ 154 | 155 | # Mac desktop service store files 156 | .DS_Store 157 | -------------------------------------------------------------------------------- /astm.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | 14 | {FC765958-F575-4708-B667-9EF952D89862} 15 | Win32Proj 16 | 17 | 18 | 19 | Application 20 | true 21 | v120 22 | 23 | 24 | Application 25 | false 26 | v120 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | true 40 | 41 | 42 | true 43 | 44 | 45 | 46 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 47 | MultiThreadedDebugDLL 48 | Level3 49 | ProgramDatabase 50 | Disabled 51 | 52 | 53 | MachineX86 54 | true 55 | Console 56 | 57 | 58 | 59 | 60 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 61 | MultiThreadedDLL 62 | Level3 63 | ProgramDatabase 64 | 65 | 66 | MachineX86 67 | true 68 | Console 69 | true 70 | true 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /binary_tree.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2014 Bryce Adelstein-Lelbach 3 | // Copyright (c) 2014 Steve Brandt 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | #include 10 | 11 | #include 12 | 13 | #include "astm.hpp" 14 | 15 | #include "astm_new.hpp" 16 | 17 | #ifdef ASTM_HPX 18 | #include 19 | #endif 20 | 21 | using namespace astm; 22 | 23 | template 24 | struct binary_tree 25 | { 26 | struct node 27 | { 28 | shared_var key; 29 | Value value; 30 | shared_var left; 31 | shared_var right; 32 | 33 | node() 34 | : key(), value(), left(), right() 35 | {} 36 | 37 | node(Key k, Value const& v, node* l, node* r) 38 | : key(k), value(v), left(l), right(r) 39 | {} 40 | 41 | node(Key k, Value const& v, shared_var l, shared_var r) 42 | : key(k), value(v), left(l), right(r) 43 | {} 44 | 45 | node(Key k, Value const& v) 46 | : key(k), value(v), left(), right() 47 | {} 48 | 49 | /* 50 | bool operator==(node const& rhs) const 51 | { 52 | return key == rhs.key; 53 | } 54 | */ 55 | }; 56 | 57 | binary_tree() 58 | : root() 59 | {} 60 | 61 | ~binary_tree() 62 | { 63 | // destroy(root); 64 | } 65 | 66 | // Initiates transaction. 67 | void insert(Key key, Value const& value) 68 | { 69 | transaction t; 70 | do { 71 | auto root_ = root.get_local(t); 72 | 73 | if (root_.get() != 0) 74 | insert(key, value, root, t); 75 | else 76 | root_ = new node(key, value); 77 | } while (!t.commit_transaction()); 78 | } 79 | 80 | // Initiates transaction. 81 | node* search(Key key) 82 | { 83 | node* n; 84 | 85 | transaction t; 86 | do { 87 | n = search(key, root, t); 88 | } while (!t.commit_transaction()); 89 | 90 | return n; 91 | } 92 | 93 | private: 94 | /* 95 | // Initiates transaction. 96 | void destroy(node* leaf) 97 | { 98 | transaction t; 99 | do { 100 | destroy(leaf, t); 101 | } while (!t.commit_transaction()); 102 | } 103 | 104 | void destroy(node* leaf, transaction& t) 105 | { 106 | if (leaf != 0) 107 | { 108 | destroy(leaf->left, t); 109 | destroy(leaf->right, t); 110 | delete leaf; 111 | } 112 | } 113 | */ 114 | 115 | void insert(Key key, Value const& value, shared_var& leaf, transaction& t) 116 | { 117 | auto leaf_ = leaf.get_local(t); 118 | 119 | if (key < leaf_.get()->key.get_local(t).get()) 120 | { 121 | if (leaf_.get()->left.get_local(t).get() != 0) 122 | insert(key, value, leaf_.get()->left, t); 123 | else 124 | // FIXME: Unnecessary deallocation here. 125 | leaf_.get()->left.get_local(t) = new node(key, value); 126 | } 127 | 128 | else if (key >= leaf_.get()->key.get_local(t).get()) 129 | { 130 | if (leaf_.get()->right.get_local(t).get() != 0) 131 | insert(key, value, leaf_.get()->right, t); 132 | else 133 | // Allocation required here. 134 | leaf_.get()->right.get_local(t) = new node(key, value); 135 | } 136 | } 137 | 138 | node* search(Key key, shared_var& leaf, transaction& t) 139 | { 140 | auto leaf_ = leaf.get_local(t); 141 | 142 | if (leaf_.get() != 0) 143 | { 144 | if (key == leaf_.get()->key.get_local(t).get()) 145 | return leaf_.get(); 146 | if (key < leaf_.get()->key.get_local(t).get()) 147 | return search(key, leaf_.get()->left, t); 148 | else 149 | return search(key, leaf_.get()->right, t); 150 | } 151 | else 152 | { 153 | return 0; 154 | } 155 | } 156 | 157 | shared_var root; 158 | }; 159 | 160 | int main() 161 | { 162 | { 163 | binary_tree tree; 164 | 165 | tree.insert(1, "hello"); 166 | 167 | auto n = tree.search(1); 168 | 169 | std::cout << n->value << "\n"; 170 | } 171 | 172 | return 0; 173 | } 174 | 175 | -------------------------------------------------------------------------------- /unit_tests.cpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2014 Bryce Adelstein-Lelbach 3 | // Copyright (c) 2014 Steve Brandt 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | #include 10 | 11 | #include "astm.hpp" 12 | 13 | #ifdef ASTM_HPX 14 | #include 15 | #endif 16 | 17 | using namespace astm; 18 | 19 | int main() 20 | { 21 | { // Read A, Write A 22 | shared_var A(1); 23 | 24 | ASTM_TEST(A.read() == 1); 25 | 26 | transaction t; 27 | do { 28 | auto A_ = A.get_local(t); 29 | 30 | ASTM_TEST(A_ == 1); 31 | A_ = 2; 32 | } while (!t.commit_transaction()); 33 | 34 | ASTM_TEST(A.read() == 2); 35 | } 36 | 37 | { // Write A, Read A 38 | shared_var A(1); 39 | 40 | ASTM_TEST(A.read() == 1); 41 | 42 | transaction t; 43 | do { 44 | auto A_ = A.get_local(t); 45 | 46 | A_ = 2; 47 | ASTM_TEST(A_ == 2); 48 | } while (!t.commit_transaction()); 49 | 50 | ASTM_TEST(A.read() == 2); 51 | } 52 | 53 | { // Write A, Write A 54 | shared_var A(1); 55 | 56 | ASTM_TEST(A.read() == 1); 57 | 58 | transaction t; 59 | do { 60 | auto A_ = A.get_local(t); 61 | 62 | ASTM_TEST(A_ == 1); 63 | ASTM_TEST(A_ == 1); 64 | } while (!t.commit_transaction()); 65 | 66 | ASTM_TEST(A.read() == 1); 67 | } 68 | 69 | { // Write A, Write A 70 | shared_var A(1); 71 | 72 | ASTM_TEST(A.read() == 1); 73 | 74 | transaction t; 75 | do { 76 | auto A_ = A.get_local(t); 77 | 78 | A_ = 2; 79 | A_ = 2; 80 | } while (!t.commit_transaction()); 81 | 82 | ASTM_TEST(A.read() == 2); 83 | } 84 | 85 | { // Read A, Write A, Read A 86 | shared_var A(1); 87 | 88 | ASTM_TEST(A.read() == 1); 89 | 90 | transaction t; 91 | do { 92 | auto A_ = A.get_local(t); 93 | 94 | ASTM_TEST(A_ == 1); 95 | A_ = 2; 96 | ASTM_TEST(A_ == 2); 97 | } while (!t.commit_transaction()); 98 | 99 | ASTM_TEST(A.read() == 2); 100 | } 101 | 102 | { // Write A, Read A, Write A 103 | shared_var A(1); 104 | 105 | ASTM_TEST(A.read() == 1); 106 | 107 | transaction t; 108 | do { 109 | auto A_ = A.get_local(t); 110 | 111 | A_ = 2; 112 | ASTM_TEST(A_ == 2); 113 | A_ = 2; 114 | } while (!t.commit_transaction()); 115 | 116 | ASTM_TEST(A.read() == 2); 117 | } 118 | 119 | { // Basic arithmetic with local_vars. 120 | shared_var A(4); 121 | shared_var B(1); 122 | 123 | // atomic { A = A*A - B; } 124 | transaction t; 125 | do { 126 | auto A_ = A.get_local(t); 127 | auto B_ = B.get_local(t); 128 | 129 | A_ = A_*A_ - B_; 130 | } while (!t.commit_transaction()); 131 | 132 | ASTM_TEST(A.read() == 15); 133 | ASTM_TEST(B.read() == 1); 134 | } 135 | 136 | { // Read A to future. 137 | shared_var A(4); 138 | shared_var B(1); 139 | 140 | // future IO; 141 | // atomic { 142 | // A = A*A; 143 | // IO = async { std::cout << A << "\n"; } 144 | // A = A - B; 145 | // } 146 | transaction t; 147 | transaction_future IO(t); 148 | 149 | do { 150 | auto A_ = A.get_local(t); 151 | auto B_ = B.get_local(t); 152 | 153 | A_ = A_*A_; 154 | 155 | IO.then( 156 | [local_A = int(A_)] (transaction*) { ASTM_TEST(local_A == 16); } 157 | ); 158 | 159 | A_ = A_ - B_; 160 | } while (!t.commit_transaction()); 161 | 162 | ASTM_TEST(A.read() == 15); 163 | ASTM_TEST(B.read() == 1); 164 | 165 | IO.get(); 166 | } 167 | 168 | { 169 | shared_var A(4); 170 | 171 | // atomic { A = A*A; } 172 | bool fail = true; 173 | int attempt_count = 0; 174 | transaction t; 175 | do { 176 | ++attempt_count; 177 | 178 | auto A_ = A.get_local(t); 179 | 180 | int tmp = A_*A_; 181 | 182 | if (fail) 183 | { 184 | A.write(3); 185 | fail = false; 186 | } 187 | 188 | A_ = tmp; 189 | } while (!t.commit_transaction()); 190 | 191 | ASTM_TEST(A.read() == 9); 192 | ASTM_TEST(attempt_count == 2); 193 | } 194 | 195 | return ASTM_REPORT; 196 | } 197 | -------------------------------------------------------------------------------- /paper/astm.bib: -------------------------------------------------------------------------------- 1 | 2 | @techreport{cxx11_standard, 3 | author = {{The C++ Standards Committee}}, 4 | title = {{ISO/IEC 14882:2011, Standard for Programming Language C++}}, 5 | year = 2011, 6 | url = {http://www.open-std.org/jtc1/sc22/wg21}, 7 | note = {http://www.open-std.org/jtc1/sc22/wg21}, 8 | } 9 | 10 | @misc{boostcpplibraries, 11 | title = {{Boost: a collection of free peer-reviewed portable C++ source libraries}}, 12 | year = 2013, 13 | url = {http://www.boost.org/}, 14 | note = {http://www.boost.org/}, 15 | } 16 | 17 | % Futures 18 | @inproceedings{future1, 19 | author = {Henry C. Baker AND Carl Hewitt}, 20 | title = {The incremental garbage collection of processes}, 21 | booktitle = {SIGART Bull.}, 22 | issue = {64}, 23 | month = {August}, 24 | year = {1977}, 25 | issn = {0163-5719}, 26 | pages = {55--59}, 27 | numpages = {5}, 28 | url = {http://doi.acm.org/10.1145/872736.806932}, 29 | doi = {http://doi.acm.org/10.1145/872736.806932}, 30 | acmid = {806932}, 31 | publisher = {ACM}, 32 | address = {New York, NY, USA}, 33 | keywords = {Eager evaluation, Garbage collection, Lazy evaluation, Multiprocessing systems, Processor scheduling}, 34 | } 35 | 36 | @inproceedings{future2, 37 | author = {Daniel P. Friedman AND David S. Wise}, 38 | title = {CONS Should Not Evaluate its Arguments}, 39 | booktitle = {ICALP}, 40 | year = {1976}, 41 | pages = {257-284}, 42 | bibsource = {DBLP, http://dblp.uni-trier.de} 43 | } 44 | 45 | % Multilisp 46 | @article{Halstead:1985:MLC:4472.4478, 47 | author = {Halstead,Jr., Robert H.}, 48 | title = {{MULTILISP}: A Language for Concurrent Symbolic Computation}, 49 | journal = {ACM Trans. Program. Lang. Syst.}, 50 | volume = {7}, 51 | issue = {4}, 52 | month = {October}, 53 | year = {1985}, 54 | issn = {0164-0925}, 55 | pages = {501--538}, 56 | numpages = {38}, 57 | url = {http://doi.acm.org/10.1145/4472.4478}, 58 | doi = {http://doi.acm.org/10.1145/4472.4478}, 59 | acmid = {4478}, 60 | publisher = {ACM}, 61 | address = {New York, NY, USA}, 62 | } 63 | 64 | % static dataflow 65 | @inproceedings{Dennis74, 66 | author = {Jack B. Dennis}, 67 | title = {First version of a data flow procedure language}, 68 | booktitle = {Symposium on Programming}, 69 | year = {1974}, 70 | pages = {362-376}, 71 | bibsource = {DBLP, http://dblp.uni-trier.de} 72 | } 73 | 74 | @inproceedings{DennisM98, 75 | author = {Jack B. Dennis and 76 | David Misunas}, 77 | title = {A Preliminary Architecture for a Basic Data-Flow Processor}, 78 | booktitle = {25 Years ISCA: Retrospectives and Reprints}, 79 | year = {1998}, 80 | pages = {125-131}, 81 | ee = {http://doi.acm.org/10.1145/285930.286058}, 82 | bibsource = {DBLP, http://dblp.uni-trier.de} 83 | } 84 | 85 | % dynamic dataflow 86 | @InCollection{dyn_dataflow, 87 | author = "{Arvind} and Nikhil, R.", 88 | editor = "J. W. de Bakker and A. J. Nijman and P. C. Treleaven", 89 | title = "Executing a Program on the {MIT} Tagged-Token Dataflow 90 | Architecture", 91 | booktitle = "PARLE '87, Parallel Architectures and Languages 92 | Europe, Volume 2: Parallel Languages", 93 | publisher = "Springer-Verlag", 94 | address = "Berlin, DE", 95 | year = "1987", 96 | keywords = "id functional", 97 | note = "Lecture Notes in Computer Science 259.", 98 | } 99 | 100 | @article{mutex, 101 | Address = {New York, NY, USA}, 102 | Author = {Courtois, P. J. and Heymans, F. and Parnas, D. L.}, 103 | Issn = {0001-0782}, 104 | Journal = {Commun. ACM}, 105 | Number = {10}, 106 | Pages = {667--668}, 107 | Publisher = {ACM}, 108 | Title = {Concurrent control with ``readers'' and ``writers''}, 109 | Volume = {14}, 110 | Year = {1971}} 111 | 112 | @proceedings{icpp09, 113 | author = {H. Kaiser and M. Brodowicz and T. Sterling}, 114 | title = "{ParallelX: An Advanced Parallel Execution Model for Scaling-Impaired Applications}", 115 | booktitle = {Proceedings of the 38th International Conference on Parallel Processing}, 116 | series = {ICPP '09}, 117 | year = {2009}, 118 | location = {Vienna, Austria}, 119 | pages = {394--401} 120 | } 121 | 122 | @article{PMBS, 123 | author = {A. Tabbal and M. Anderson and M. Brodowicz and H. Kaiser and T. Sterling}, 124 | title = "{Preliminary Design Examination of the ParalleX System from a Software and Hardware Perspective}", 125 | journal = {ACM SIGMETRICS Performance Evaluation Review}, 126 | volume = {38}, 127 | number = {4}, 128 | year = {2011}, 129 | pages = {81--87} 130 | } 131 | 132 | @inproceedings{Cilk++, 133 | author = {Leiserson, Charles E.}, 134 | title = {The Cilk++ Concurrency Platform}, 135 | booktitle = {Proceedings of the 46th Annual Design Automation Conference}, 136 | series = {DAC '09}, 137 | year = {2009}, 138 | isbn = {978-1-60558-497-3}, 139 | location = {San Francisco, California}, 140 | pages = {522--527}, 141 | numpages = {6}, 142 | url = {http://doi.acm.org/10.1145/1629911.1630048}, 143 | doi = {10.1145/1629911.1630048}, 144 | acmid = {1630048}, 145 | publisher = {ACM}, 146 | address = {New York, NY, USA}, 147 | keywords = {Amdahl's Law, dag model, hyperobject, multicore programming, multithreading, parallel programming, parallelism, race detection, reducer, span, speedup, work}, 148 | } 149 | 150 | @inproceedings{QthreadsScheduling, 151 | author = {Olivier, Stephen L. and Porterfield, Allan K. and Wheeler, Kyle B. and Prins, Jan F.}, 152 | title = {Scheduling Task Parallelism on Multi-socket Multicore Systems}, 153 | booktitle = {Proceedings of the 1st International Workshop on Runtime and Operating Systems for Supercomputers}, 154 | series = {ROSS '11}, 155 | year = {2011}, 156 | isbn = {978-1-4503-0761-1}, 157 | location = {Tucson, Arizona}, 158 | pages = {49--56}, 159 | numpages = {8}, 160 | url = {http://doi.acm.org/10.1145/1988796.1988804}, 161 | doi = {10.1145/1988796.1988804}, 162 | acmid = {1988804}, 163 | publisher = {ACM}, 164 | address = {New York, NY, USA}, 165 | } 166 | 167 | @INPROCEEDINGS{Qthreads, 168 | author={Wheeler, K.B. and Murphy, R.C. and Thain, D.}, 169 | booktitle={Parallel and Distributed Processing, 2008. IPDPS 2008. IEEE International Symposium on}, 170 | title={Qthreads: {An API for programming with millions of lightweight threads}}, 171 | year={2008}, 172 | month={April}, 173 | pages={1-8}, 174 | keywords={Unix;application program interfaces;multi-threading;resource allocation;API;HPCCG benchmark;Qthreads;Unix implementation;application program interfaces;large scale hardware supported multithreading;lightweight threads;qthread API;qthread abstraction;resource management;Computer architecture;Costs;Hardware;Laboratories;Large-scale systems;Multithreading;Parallel processing;Programming profession;Resource management;Yarn}, 175 | doi={10.1109/IPDPS.2008.4536359}, 176 | ISSN={1530-2075},} 177 | 178 | @inproceedings{michael1996, 179 | title={Simple, fast, and practical non-blocking and blocking concurrent queue algorithms}, 180 | author={Michael, Maged M and Scott, Michael L}, 181 | booktitle={Proceedings of the fifteenth annual ACM symposium on Principles of distributed computing}, 182 | booktitle = {Proceedings of the Fifteenth Annual ACM Symposium on Principles 183 | of Distributed Computing, Philadelphia, Pennsylvania, USA, 184 | May 23-26, 1996}, 185 | isbn = {0-89791-800-2}, 186 | url={http://www.research.ibm.com/people/m/michael/podc-1996.pdf}, 187 | pages={267--275}, 188 | year={1996}, 189 | organization={ACM} 190 | } 191 | 192 | @book{treiber1986, 193 | title={Systems Programming: Coping with Parallelism}, 194 | author={International Business Machines Corporation. Research Division and Treiber, R.K.}, 195 | series={Research Report RJ}, 196 | url={http://domino.research.ibm.com/library/cyberdig.nsf/0/58319a2ed2b1078985257003004617ef?OpenDocument}, 197 | pages = {17--28}, 198 | year={1986}, 199 | publisher={International Business Machines Incorporated, Thomas J. Watson Research Center} 200 | } 201 | 202 | @book{herlihy2008, 203 | author = {Herlihy, Maurice and Shavit, Nir}, 204 | title = {The Art of Multiprocessor Programming}, 205 | year = {2008}, 206 | isbn = {0123705916, 9780123705914}, 207 | url={http://dl.acm.org/citation.cfm?id=1734069}, 208 | publisher = {Morgan Kaufmann Publishers Inc.}, 209 | address = {San Francisco, CA, USA}, 210 | } 211 | 212 | @misc{inteltbb, 213 | howpublished = "\url{http://www.threadingbuildingblocks.org/}", 214 | title = {{Intel\textregistered \ Thread Building Blocks 4.2}} 215 | } 216 | 217 | @misc{PAPI, 218 | howpublished = "\url{http://icl.cs.utk.edu/papi/}", 219 | title = {{PAPI}: {Performance Application Programming Interface}} 220 | } 221 | 222 | @misc{Intel-manual, 223 | howpublished = "http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-optimization-manual.html", 224 | title = {Intel\textregistered \ 64 and {IA-32 Architectures Optimization Reference Manual}s} 225 | } 226 | 227 | @misc{Phi-manual, 228 | howpublished = "http://software.intel.com/en-us/articles/optimization-and-performance-tuning-for-intel-xeon-phi-coprocessors-part-2-understanding" 229 | 230 | @inproceedings{APEX, 231 | author = {Huck, Kevin and Shende, Sameer and Malony, Allen and Kaiser, Hartmut and Porterfield, Allan and Fowler, Rob and Brightwell, Ron}, 232 | title = {{An Early Prototype of an Autonomic Performance Environment for Exascale}}, 233 | booktitle = {Proceedings of the 3rd International Workshop on Runtime and Operating Systems for Supercomputers}, 234 | series = {ROSS '13}, 235 | year = {2013}, 236 | isbn = {978-1-4503-2146-4}, 237 | location = {Eugene, Oregon}, 238 | pages = {8:1--8:8}, 239 | articleno = {8}, 240 | numpages = {8}, 241 | url = {http://doi.acm.org/10.1145/2491661.2481434}, 242 | doi = {10.1145/2491661.2481434}, 243 | acmid = {2481434}, 244 | publisher = {ACM}, 245 | address = {New York, NY, USA}, 246 | } 247 | 248 | @misc{pgas_link, 249 | author = {{PGAS}}, 250 | title = {{PGAS - Partitioned Global Address Space}}, 251 | year = 2013, 252 | url = {http://www.pgas.org}, 253 | note = {http://www.pgas.org}, 254 | } 255 | 256 | % X10 257 | @article{Charles:2005:XOA:1103845.1094852, 258 | author = {Charles, Philippe and Grothoff, Christian and Saraswat, Vijay and Donawa, 259 | Christopher and Kielstra, Allan and Ebcioglu, Kemal and von Praun, 260 | Christoph and Sarkar, Vivek}, 261 | title = {{X10}: An object-oriented approach to non-uniform cluster computing}, 262 | journal = {SIGPLAN Not.}, 263 | volume = {40}, 264 | issue = {10}, 265 | month = {October}, 266 | year = {2005}, 267 | issn = {0362-1340}, 268 | pages = {519--538}, 269 | numpages = {20}, 270 | url = {http://doi.acm.org/10.1145/1103845.1094852}, 271 | doi = {http://doi.acm.org/10.1145/1103845.1094852}, 272 | acmid = {1094852}, 273 | publisher = {ACM}, 274 | address = {New York, NY, USA}, 275 | keywords = {Java, X10, atomic blocks, clocks, data distribution, multithreading, 276 | non-uniform cluster computing (NUCC), partitioned global address space (PGAS), 277 | places, productivity, scalability}, 278 | } 279 | 280 | % chapel 281 | @article{Chamberlain07parallelprogrammability, 282 | author = {Bradford L. Chamberlain and David Callahan and Hans P. Zima}, 283 | title = {Parallel programmability and the {Chapel} language}, 284 | journal = {International Journal of High Performance Computing Applications}, 285 | year = {2007}, 286 | volume = {21}, 287 | pages = {291--312} 288 | } 289 | 290 | % UPC 291 | @techreport{UPCSpec, 292 | author = {{UPC Consortium}}, 293 | citeulike-article-id = {8184513}, 294 | citeulike-linkout-0 = {http://www.gwu.edu/\~{}upc/publications/LBNL-59208.pdf}, 295 | institution = {Lawrence Berkeley National Lab}, 296 | keywords = {pgas, upc}, 297 | number = {LBNL-59208}, 298 | posted-at = {2010-11-03 13:06:17}, 299 | priority = {2}, 300 | title = {{UPC Language Specifications, v1.2}}, 301 | type = {Tech Report}, 302 | url = {http://www.gwu.edu/~upc/publications/LBNL-59208.pdf}, 303 | year = {2005} 304 | } 305 | 306 | % Active messages 307 | @inproceedings{Wall:1982:MAA:582153.582157, 308 | author = {Wall, David W.}, 309 | title = {Messages as active agents}, 310 | booktitle = {Proceedings of the 9th ACM SIGPLAN-SIGACT symposium on Principles of programming languages}, 311 | series = {POPL '82}, 312 | year = {1982}, 313 | isbn = {0-89791-065-6}, 314 | location = {Albuquerque, New Mexico}, 315 | pages = {34--39}, 316 | numpages = {6}, 317 | url = {http://doi.acm.org/10.1145/582153.582157}, 318 | doi = {http://doi.acm.org/10.1145/582153.582157}, 319 | acmid = {582157}, 320 | publisher = {ACM}, 321 | address = {New York, NY, USA}, 322 | } 323 | -------------------------------------------------------------------------------- /astm.hpp: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////////////////////////////////////// 2 | // Copyright (c) 2014 Bryce Adelstein-Lelbach 3 | // Copyright (c) 2014 Steve Brandt 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | #if !defined(ASTM_DBA88345_57B8_4CC8_A574_D5F007250E94) 10 | #define ASTM_DBA88345_57B8_4CC8_A574_D5F007250E94 11 | 12 | #include "astm_config.hpp" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace astm 21 | { 22 | 23 | // transaction should be in a shared_ptr; local_ objects should hold references 24 | // each async {} block needs to hold a clone()'d copy of any shared_var_base variables it uses. 25 | 26 | // inside of an async {} block, they can only be read 27 | 28 | // IO.then([] { /* write my 100GB of plaintext doubles to file */ }); 29 | 30 | // if (/* some atomic condition */) then /* start some async thing to make a bitcoin */ 31 | 32 | struct shared_var_base 33 | { 34 | virtual ~shared_var_base() {} 35 | 36 | virtual shared_var_base* clone() const = 0; 37 | 38 | virtual void write(shared_var_base const&) = 0; 39 | 40 | virtual ASTM_LOCK lock() const = 0; 41 | 42 | virtual bool operator==(shared_var_base const&) const = 0; 43 | }; 44 | 45 | struct transaction; 46 | 47 | struct transaction_future 48 | { 49 | typedef ASTM_FUTURE future_type; 50 | 51 | private: 52 | transaction* trans_; 53 | future_type fut_; 54 | 55 | public: 56 | transaction_future(transaction* trans) 57 | : trans_(trans) 58 | , fut_(ASTM_MAKE_READY_FUTURE()) 59 | {} 60 | 61 | transaction_future(transaction& trans) 62 | : trans_(&trans) 63 | , fut_(ASTM_MAKE_READY_FUTURE()) 64 | {} 65 | 66 | template 67 | void then(F f); 68 | 69 | void get() 70 | { 71 | fut_.get(); 72 | } 73 | }; 74 | 75 | 76 | template 77 | struct shared_var : shared_var_base 78 | { 79 | typedef ASTM_FUTURE future_type; 80 | 81 | struct local_var 82 | { 83 | private: 84 | transaction* trans_; 85 | shared_var_base* var_; 86 | 87 | public: 88 | local_var(transaction* trans, shared_var_base* var) 89 | : trans_(trans) 90 | , var_(var) 91 | {} 92 | 93 | T get() const; 94 | 95 | operator T const& () const; 96 | 97 | local_var& operator=(shared_var_base const& rhs); 98 | 99 | local_var& operator=(T const& rhs); 100 | 101 | template 102 | void then(F f); 103 | }; 104 | 105 | private: 106 | T data_; 107 | mutable ASTM_MUTEX mtx_; 108 | 109 | public: 110 | future_type queue; 111 | 112 | shared_var() : data_(), mtx_(), queue(ASTM_MAKE_READY_FUTURE()) {} 113 | 114 | shared_var(T const& t) : data_(t), mtx_(), queue(ASTM_MAKE_READY_FUTURE()) {} 115 | 116 | shared_var(T&& t) : data_(t), mtx_(), queue(ASTM_MAKE_READY_FUTURE()) {} 117 | 118 | shared_var(shared_var const& rhs) : data_(rhs.data_), mtx_(), queue(ASTM_MAKE_READY_FUTURE()) {} 119 | 120 | ~shared_var() { } 121 | 122 | // Locks. 123 | shared_var_base* clone() const 124 | { 125 | auto l = lock(); 126 | return new shared_var(data_); 127 | } 128 | 129 | // Doesn't lock. 130 | T const& read() const 131 | { 132 | return data_; 133 | } 134 | 135 | // Doesn't lock. 136 | void write(T const& rhs) 137 | { 138 | data_ = rhs; 139 | } 140 | 141 | // Doesn't lock. 142 | void write(shared_var_base const& rhs) 143 | { 144 | data_ = dynamic_cast(&rhs)->read(); 145 | } 146 | 147 | ASTM_LOCK lock() const 148 | { 149 | return ASTM_LOCK(mtx_); 150 | } 151 | 152 | bool operator==(shared_var_base const& rhs) const 153 | { 154 | return data_ == dynamic_cast(&rhs)->read(); 155 | } 156 | 157 | local_var get_local(transaction& trans) 158 | { 159 | return local_var(&trans, this); 160 | } 161 | }; 162 | 163 | struct transaction 164 | { 165 | std::list< 166 | std::pair< 167 | // The shared variable we're reading from 168 | shared_var_base* 169 | // The value we read from the variable 170 | , std::shared_ptr 171 | > 172 | > read_list; 173 | 174 | std::set< 175 | shared_var_base* // The shared variable we're writing to 176 | > write_set; 177 | 178 | std::list< 179 | std::pair< 180 | ASTM_FUTURE* // The future we're writing to 181 | // (if NULL, fire-and-forget semantics) 182 | , ASTM_FUNCTION // The async action to execute 183 | > 184 | > async_list; 185 | 186 | std::map< 187 | shared_var_base* // The shared variable we're reading from 188 | , std::shared_ptr // Current value of the variable 189 | > variables; 190 | 191 | void clear() 192 | { 193 | read_list.clear(); 194 | write_set.clear(); 195 | async_list.clear(); 196 | variables.clear(); 197 | } 198 | 199 | // This should possibly be called RAII-style from the destructor. 200 | bool commit_transaction() 201 | { 202 | // Algorithm: 203 | // 204 | // 1.) Obtain exclusive access to all the variables. 205 | // 2.) Compare our recorded reads against the current values (and fail if needed). 206 | // 3.) Perform writes, reading from our internal map. 207 | // 4.) Perform async operations. 208 | // 5.) Release exclusive access. 209 | 210 | // 1.) Obtain exclusive access to all the variables. 211 | std::list locks; 212 | 213 | // Variable map is sorted, so order of locking is sorted. 214 | for ( std::pair > const& var 215 | : variables) 216 | { 217 | assert(var.first != NULL); 218 | 219 | locks.push_back((*var.first).lock()); 220 | } 221 | 222 | // 2.) Compare our recorded reads against the current values (and fail if needed). 223 | for ( std::pair > const& var 224 | : read_list) 225 | { 226 | assert(var.first != NULL); 227 | 228 | if (!((*var.first) == (*var.second))) // Perform read operation. 229 | { 230 | clear(); 231 | 232 | // Transaction fails; exclusive access is released by RAII. 233 | // Note that this leaves any futures used in a default-constructed 234 | // state - perhaps we should put an exception (transaction_failed) 235 | // into them instead. 236 | return false; 237 | } 238 | } 239 | 240 | // 3.) Perform writes, reading from our internal map. 241 | for (shared_var_base* var : write_set) 242 | { 243 | assert(var != NULL); 244 | 245 | auto it = variables.find(var); 246 | 247 | assert(it != variables.end()); 248 | 249 | (*var).write((*(*it).second)); // Perform write operation. 250 | } 251 | 252 | // 4.) Perform async operations. 253 | for ( std::pair*, ASTM_FUNCTION >& op 254 | : async_list) 255 | { 256 | // If the future is void, use fire-and-forget semantics. 257 | if (op.first == NULL) 258 | // For HPX version, just use hpx::apply. 259 | ASTM_ASYNC(op.second, this); 260 | else 261 | (*op.first) = (*op.first).then(std::bind(op.second, this)); 262 | } 263 | 264 | // 5.) Release exclusive access. 265 | return true; 266 | } 267 | 268 | shared_var_base const& read(shared_var_base* var) 269 | { 270 | // We have two cases here: 271 | // * The variable has not been read or written in this transaction, and is 272 | // not present in the internal state. 273 | // * The variable has been read or written previously in this transaction, 274 | // and is present in the internal state. 275 | 276 | assert(var != NULL); 277 | 278 | // We will attempt an insertion with a placeholder value, to avoid a 279 | // speculative read and performing the map lookup twice (find then insert) 280 | // for first-time entries. 281 | std::pair > entry(var, 0); 282 | 283 | // This auto will be a std::pair. 284 | auto result = variables.insert(entry); 285 | 286 | if (result.second == true) 287 | { 288 | // FIXME FIXME FIXME: Read list needs a /copy/ of the entry, not a 289 | // shared_ptr reference to it. 290 | 291 | // Insertion succeeded; this is the first read of the variable. 292 | (*result.first).second.reset((*var).clone()); // Perform read. 293 | 294 | // Record the read operation. 295 | read_list.push_back(*result.first); 296 | 297 | return (*(*result.first).second); 298 | } 299 | else 300 | // Insertion failed; the variable is in the internal state. 301 | return (*(*result.first).second); 302 | } 303 | 304 | void write(shared_var_base* var, shared_var_base const& value) 305 | { 306 | // We have two cases here: 307 | // * The variable has not been read or written in this transaction, and is 308 | // not present in the internal state. 309 | // * The variable has been read or written previously in this transaction, 310 | // and is present in the internal state. 311 | 312 | assert(var != NULL); 313 | 314 | // We will attempt an insertion, to avoid performing the map lookup twice 315 | // (find then insert) for first-time entries. 316 | std::pair > 317 | entry(var, std::shared_ptr(value.clone())); 318 | 319 | // This auto will be a std::pair. 320 | auto result = variables.insert(entry); 321 | 322 | if (result.second == true) 323 | { 324 | // Insertion succeeded; this is the first read of the variable. 325 | 326 | // Make sure the variable is in the write set. 327 | write_set.insert(var); 328 | } 329 | else 330 | { 331 | // FIXME FIXME FIXME: Use write here to avoid the second allocation/ 332 | // creation of a new shared pointer. 333 | 334 | // Insertion failed; the variable is in the internal state. 335 | (*result.first).second = entry.second; // Perform INTERNAL write. 336 | 337 | // Make sure the variable is in the write set. 338 | write_set.insert(var); 339 | } 340 | } 341 | 342 | // If fut is NULL, then fire-and-forget semantics are used. 343 | void then(ASTM_FUTURE* fut, ASTM_FUNCTION F) 344 | { 345 | std::pair*, ASTM_FUNCTION > entry(fut, F); 346 | async_list.push_back(entry); 347 | } 348 | }; 349 | 350 | template 351 | shared_var::local_var::operator T const& () const 352 | { 353 | return dynamic_cast(&trans_->read(var_))->read(); 354 | } 355 | 356 | template 357 | T shared_var::local_var::get() const 358 | { 359 | return dynamic_cast(&trans_->read(var_))->read(); 360 | } 361 | 362 | template 363 | typename shared_var::local_var& 364 | shared_var::local_var::operator=(shared_var_base const& rhs) 365 | { 366 | trans_->write(var_, rhs); 367 | return *this; 368 | } 369 | 370 | template 371 | typename shared_var::local_var& 372 | shared_var::local_var::operator=(T const& rhs) 373 | { 374 | shared_var tmp(rhs); 375 | trans_->write(var_, tmp); 376 | return *this; 377 | } 378 | 379 | template 380 | template 381 | void shared_var::local_var::then(F f) 382 | { 383 | assert(trans_); 384 | trans_->then(&dynamic_cast(&trans_)->queue, f); 385 | } 386 | 387 | template 388 | void transaction_future::then(F f) 389 | { 390 | assert(trans_); 391 | trans_->then(&fut_, f); 392 | } 393 | 394 | } // namespace astm 395 | 396 | #endif // ASTM_DBA88345_57B8_4CC8_A574_D5F007250E94 397 | 398 | -------------------------------------------------------------------------------- /paper/astm.tex: -------------------------------------------------------------------------------- 1 | \documentclass[conference]{IEEEtran} 2 | 3 | \usepackage{xcolor} 4 | \definecolor{darkred}{rgb}{0.5,0,0} 5 | \definecolor{darkgreen}{rgb}{0,0.5,0} 6 | \definecolor{darkblue}{rgb}{0,0,0.5} 7 | 8 | \usepackage{graphicx} 9 | \usepackage{amsmath} 10 | \usepackage{amssymb} 11 | \usepackage{xspace} 12 | \usepackage{upgreek} 13 | \usepackage{multirow} 14 | \usepackage{array} 15 | 16 | \usepackage{caption} 17 | \usepackage[ampersand]{easylist} 18 | 19 | \usepackage{fixme} 20 | \fxusetheme{color} 21 | \fxsetup{ 22 | status=draft, 23 | author=, 24 | layout=inline, % also try footnote or pdfnote 25 | theme=color 26 | } 27 | 28 | \definecolor{fxnote}{rgb}{0.8000,0.0000,0.0000} 29 | 30 | \usepackage[bordercolor=white,backgroundcolor=gray!30,linecolor=black,colorinlistoftodos]{todonotes} 31 | \newcommand{\rework}[1]{% 32 | \todo[color=yellow,inline]{{#1}}% 33 | } 34 | \newcommand{\I}[1]{\textit{#1}} 35 | \newcommand{\B}[1]{\textbf{#1}} 36 | \newcommand{\T}[1]{\texttt{#1}} 37 | \newcommand{\F}[1]{\B{\textcolor{red}{FIXME: #1}}} 38 | 39 | \usepackage{listings} 40 | \lstloadlanguages{C++,Pascal} 41 | 42 | % Settings for the lstlistings environment 43 | \lstset{ 44 | language=C++, % choose the language of the code 45 | basicstyle=\footnotesize\ttfamily, % the size of the fonts that are used for the 46 | % code 47 | numbers=none, % where to put the line-numbers 48 | numberstyle=\tiny, % the size of the fonts that are used for the 49 | % line-numbers 50 | stepnumber=1, % the step between two line-numbers. If it's 51 | % 1 each line will be numbered 52 | numbersep=5pt, % how far the line-numbers are from the code 53 | %backgroundcolor=\color{gray}, % choose the background color. You must add 54 | % \usepackage{color} 55 | showspaces=false, % show spaces adding particular underscores 56 | showstringspaces=false, % underline spaces within strings 57 | showtabs=false, % show tabs within strings adding particular 58 | % underscores 59 | keywordstyle=\bfseries\color{darkblue}, % color of the keywords 60 | commentstyle=\color{darkgreen}, % color of the comments 61 | stringstyle=\color{darkred}, % color of strings 62 | captionpos=b, % sets the caption-position to top 63 | tabsize=2, % sets default tabsize to 2 spaces 64 | frame=tb, % adds a frame around the code 65 | breaklines=true, % sets automatic line breaking 66 | breakatwhitespace=false, % sets if automatic breaks should only happen 67 | % at whitespace 68 | escapechar=\%, % toggles between regular LaTeX and listing 69 | belowskip=0.3cm, % vspace after listing 70 | morecomment=[s][\bfseries\color{darkblue}]{struct}{\ }, 71 | morecomment=[s][\bfseries\color{darkblue}]{class}{\ }, 72 | morecomment=[s][\bfseries\color{darkblue}]{public:}{\ }, 73 | morecomment=[s][\bfseries\color{darkblue}]{public}{\ }, 74 | morecomment=[s][\bfseries\color{darkblue}]{protected:}{\ }, 75 | morecomment=[s][\bfseries\color{darkblue}]{private:}{\ }, 76 | morecomment=[s][\bfseries\color{black}]{operator+}{\ }, 77 | xleftmargin=0.1cm, 78 | %xrightmargin=0.1cm, 79 | } 80 | 81 | \newcommand{\mus}{$\upmu$s\xspace} 82 | 83 | \newcommand{\code}[1]{\texttt{{{#1}}}} 84 | 85 | \begin{document} 86 | 87 | \title{The Atomic Future of Software Transactional Memory} 88 | 89 | \author{ 90 | \IEEEauthorblockN{Bryce Adelstein-Lelbach\IEEEauthorrefmark{1}, Steve Brandt\IEEEauthorrefmark{1}} 91 | \IEEEauthorblockA{\small \IEEEauthorrefmark{1}Center for Computation and Technology, Louisiana State University} 92 | \IEEEauthorblockA{\scriptsize \tt blelbach@cct.lsu.edu, sbrandt@cct.lsu.edu} 93 | } 94 | 95 | \maketitle 96 | 97 | 98 | \begin{abstract} 99 | In recent years, Software Transactional Memory has become an increasing popular solution for managing concurrency. STM has a number of advantages over traditional approaches (such as locking), but it also has the disadvantage of disallowing side-effecting code inside atomic blocks. This restriction limits the usefulness of STM in software that frequently interacts with hardware. This paper describes a model for an STM system which supports the asynchronous invocation of side-effecting code from within a transaction, and the use of a special kind of future, an ``atomic future'' if you will, to represent the result of an STM calculation. We present the details of the system and examine two use cases for our design. 100 | \end{abstract} 101 | 102 | 103 | 104 | \IEEEpeerreviewmaketitle 105 | % no \IEEEPARstart 106 | %This demo file is intended to serve as a ``starter file'' for IEEE conference papers produced under \LaTeX\ using IEEEtran.cls version 1.7 and later. 107 | % You must have at least 2 lines in the paragraph with the drop letter 108 | 109 | 110 | \section{Introduction} 111 | 112 | % Introduction (1 page) 113 | % * What is the problem? 114 | % * Explain the basic idea of STM 115 | % * Talk about applications of STM 116 | % * Concurrent data structures for "system programming" 117 | % * OS/kernel/runtime systems 118 | % * Ex: video game frameworks, RCU trees in Linux 119 | % * The problem: STM is limited to side effect free code 120 | % * Literature review: what people have tried 121 | % * Overview of our approach 122 | 123 | Software Transaction Memory is a powerful alternative to traditonal lock-based programming. STM offers both performance benefits and stronger safety gurantees when compared to lock-based concurrency. Modern hardware has reached the physical limitations of CPU clock speeds, a watershed event that has shifted the concerns of performance-driven applications. Communication, not processing power, is now the limiting factor in many parallel applications. STM is optimistic and speculative, which is an ideal fit for modern platforms where cycles are cheap and communication is expensive. Locking can be prone to priority inversion, while STM is not. Additionally, transactional models offer stronger safety gurantees than lock-based programming. When programming with locks, application programmers must remember to acquire and release exclusive access for each relevant shared object every time they write a critical section. Programmers must be able to identify operations that will access the same shared data concurrently. Programmers who fail to properly protect critical sections will introduce subtle race conditions which are hard to detect. Because locks are pessimistic and non-speculative, programmers who are too cautious will introduce unnecessary critical sections, impeding application performance. Those who are not cautious enough may fail to protect overlapping operations. Lock-based programming requires the adoption of an application-wide protocol for obtaining locks, to prevent deadlock and livelock (the lock ordering problem). Transactions are easy to program with as they intercept and manage data access for the programmer, preventing the possibility of deadlocks/livelocks due to user error. Because of the optimistic nature of transactions, the performance penalty for overuse is less severe. 124 | 125 | Transactional systems place restrictions on the types of operations which can be performed within a transaction. STM systems can only function properly if all mutating operations within transactions can be intercepted by the STM system. This is necessary because an STM system must be able to roll-back the effects of a failed transaction. If a transaction does not commit, it should have no effect on data outside of the internal state of the transaction. Typically, STM implementations are capable of intercepting mutating operations that operate on first-class entities of the application programming language (entities that can be stored in variables and copied). Reads and writes to variables which represent ordinary application data can be handled in a straightforward fashion as long as the there is a well-defined copy operation for the object represented by the variable. A copy operation allows the STM system to locally duplicate the data within a transaction, modify it, and then copy it back upon successful commit. 126 | 127 | However, this restriction can prevent the use of STM in applications where copying shared data is not desirable. Software which reads from hardware typically encounter these issue. Such software frequently contains data structures which directly represent the state of hardware: e.g. hardware queues, network sockets, device buffers, file streams. All of these objects have \textbf{mutating read operations} which makes them impossible to copy without affecting them in an irreversible fashion. However, it may be possible to buffer an application specific update operation. For example, in the case of a stream object representing a file, it is not feasible to define a copy operator, but we can buffer an operation which will write to the stream. 128 | 129 | The side-effecting restrictions of STM also makes it difficult to use when writing reusable software. Generic algorithms (implemented with templates or polymorphism) must either pass these restrictions on to their client code, or they must assume that operations on user-provided types have side-effects. For example, it may be feasible for a generic container to require side-effect free comparison operator but allow side-effecting constructors which are executed at the end of a successful commit. The insertion algorithm for the container would execute in the transaction, but the side-effecting constructor would be executed during a successful commit. 130 | 131 | Additionally, certain classes of applications may wish to avoid the speculative execution of certain routines within a transaction. Some code may have side-effects that would not prevent its usage within an transactions, but those side effects could stress system resources. Memory allocation is the best example of this; clearly, allocating memory has side effects (possibly even at the kernel level, if the allocator needs to request additional pages). For small allocations, it is feasible to allow them to occur within transactions, provided that reference counting or memory management is used to clean up the allocation in the event of transaction failure. However, for larger memory structures it may be preferable to defer their allocation until the transaction commits successfully. 132 | 133 | For these classes of software, we propose a feature which allows developers to express side-effecting actions which will be asynchronously executed upon the successful commit of a transaction. We will call these actions \textbf{escaped functions}. Escaped functions are written written within a transaction, and may reference the scope where they are written. Their environment is captured in a closure, and execution of these functions is deferred until the commit succeeds. Our system features a mechanism for expressing the data dependencies of escaped functions, to ensure that overlapping operations are executed safely. We present an implementation of this mechanism using C++ futures, built on top of the HPX parallel runtime system. 134 | 135 | \subsection{HPX} 136 | 137 | ASTM is implemented using HPX, a general purpose parallel runtime system for parallel applications of any scale. 138 | HPX exposes a homogeneous programming model which unifies the execution of remote 139 | and local operations. The runtime system has been developed for conventional 140 | architectures. Currently supported are SMP nodes, large Non Uniform Memory Access 141 | (NUMA) machines and heterogeneous systems such as clusters equipped with Xeon Phi 142 | accelarators. Strict adherence to Standard C++~\cite{cxx11_standard} and the 143 | utilization of the Boost C++ Libraries~\cite{boostcpplibraries} makes HPX both 144 | portable and highly optimized. 145 | The source code is published under the Boost Software 146 | License~\fxnote{cite} making it accessible to everyone as Open Source Software. 147 | It is modular, feature-complete and designed for 148 | best possible performance. HPX's design focuses on overcoming conventional 149 | limitations such as (implicit and explicit) global barriers, poor latency hiding, 150 | static-only resource allocation, and lack of support for medium- to fine-grain 151 | parallelism. 152 | 153 | \section{Technical Approach} 154 | 155 | The implementation of our system is written in C++, and uses an object-oriented model. Our system consists of three basic classes. \textbf{Transaction managers} 156 | represent a single transaction. A transaction object provides storage for transaction-local data, handles reads and writes from non-local values and manages commit attempts. The variables which are accessible in transactions are represented by 157 | \textbf{transaction variables} objects. These objects are containers for a single object of a user-provided type. Transaction variables expose transaction-aware interfaces for accessing the underlying object they represent. 158 | Finally, \textbf{transaction futures} represent escaped functions which have been invoked asynchronously from inside a transaction. 159 | 160 | \subsection{Transaction Manager} 161 | 162 | The transaction manager class is both a representation of a single transaction, and an implementation of the core services of ASTM. ASTM has no global state. Each transaction manager can be thought of as an independent \textbf{STM runtime}. Transaction managers only interact with each other when they both acquire mutual access to the same transaction variable. This interaction occurs via an underlying \textbf{concurrency runtime}, such as HPX or the C++ standard concurrency library. By using this design, we completely encapsulate the state of each transaction from all other transactions. If the internal state of one transaction instance becomes corrupted, no other transaction will be affected. In our system, a transaction block is formed in native C++ using a do-while loop and a transaction object: 163 | 164 | \begin{lstlisting} 165 | transaction t 166 | do { 167 | // Transaction block. 168 | } while (!t.commit()); 169 | \end{lstlisting} 170 | 171 | The transaction manager has two roles in ASTM: 172 | 173 | \begin{itemize} 174 | \item \textbf{Provides transaction-local storage}: during transaction attempts, the 175 | shared variables which are accessed by the transaction need to be recorded. The 176 | initial value of read-variables needs to be stored to check for transaction 177 | failure during the commit, and updates to write-variables need to be buffered. 178 | Additionally, any continuations launched within the transaction need to be 179 | buffered so that they can be launched on a successful commit. 180 | \item \textbf{Implement the four basic operations which occur in transactions}: 181 | \textbf{read} (copy the value of a transaction variable into local storage), \textbf{write} (buffer a write to a transaction variable), \textbf{async} (buffer an escaped function), and 182 | \textbf{commit} (attempt to commit the transaction; if successful, copy local writes to transaction variables and asynchronously evoke escaped functions). These methods are intended to be used directly by application code. Instead, transaction variables and transaction futures provide a high-level interface which is built upon these basic operations. 183 | \end{itemize} 184 | 185 | The transaction manager uses four data structures to implement transaction-local 186 | storage: the \textbf{variable map} which contains the local state of transaction variables and three 187 | structures which store the information needed to process a commit attempt (the \textbf{read list}, \textbf{write set} and \textbf{continuation list}). 188 | 189 | During a transaction, all reads to transaction variables are cached and all writes are buffered, so the transaction manager must maintain a local copy of all transaction variables accessed during its' transaction. These local copies are stored in an ordered map which is indexed by the memory address 190 | of the original transaction variable associated with each local copy. The ordering of the map is important because it ensures a uniform locking order will be used by all transaction managers. Upon insertion into 191 | the map, transaction variables are deep-copied (or potentially moved in the case of a write) \fxnote{Actually implement move-on-write}. 192 | 193 | Whenever a transaction variable is read, the value which is read is added to a read list. These values will be compared to the transaction variables that they were read from during a commit attempt. The elements 194 | of the read list have the same key-value structure of the variable map. Likewise, write operations will add entries to a write set to keep track 195 | of which entries in the variable map will need to be written externally upon 196 | commit. 197 | 198 | The read operation requests access to a local copy of a transaction 199 | variable. If the variable is not present in the variable map, a 200 | thread-safe read of the actual variable value will occur. This value will be 201 | added to the variable map and the read list. If the variable has been written 202 | prior to the read in the transaction, no external read is performed. Instead, 203 | the buffered value from the write will be read from the variable map 204 | and the map entry will be added to the read list. 205 | 206 | The write operation is mostly symmetric to the read operation. A local write to 207 | a variable that has not been previously accessed will create a new entry in the 208 | variable map, but no external read will be made. A write operation will add the 209 | variable's memory reference to the write set. 210 | 211 | The async operation asynchronously executes a function. This operation will 212 | buffer a continuation, which will be launched asynchronously upon the 213 | successful commit of the transaction. The async operation takes two 214 | arguments; a bound function to be invoked, and a reference to the future 215 | object which the operation will wait on (if this reference is null, the function will not have any dependencies). The future object can be 216 | thought of as a queue. When a transaction commits, escaped functions are 217 | attached to their futures as continuations. When the action associated with the 218 | future becomes ready, the continuation will begin immediately. 219 | 220 | The commit operation attempts to complete the transaction. First, the elements of the variables map are iterated, and exclusive access to the transaction variable is obtained. Then, the read list is iterated and each entry of the list is compared to the value of the corresponding transaction variable. If one of these comparisons fails, the transaction will fail. In the case of failure, the internal state is purged and the commit routine returns immediately. If no discrepancies are found, the commit succeeds. The transaction manager will update every transaction variable in the write set with its local value. Next, the continuations which have been buffered by the async operation are enqueued. Finally, exclusive access will be released and the internal state of the transaction manager will be cleared. 221 | 222 | \subsection{Transaction Variables} 223 | 224 | Transaction variables can be thought of as "wrapped" instance of of their underlying type (e.g. an "is-a" relationship with the underlying type). Transaction variables have the following functions in our system: 225 | 226 | \begin{itemize} 227 | \item Safely type-erases objects that are used in transactions. Transaction variables is 228 | part of a type erasure system which enables a type-agnostic implementation of 229 | the transaction manager. 230 | \item Provides an interface to the user for accessing and updating variables 231 | inside and outside of transactions. 232 | \item Enforces the invarants regarding variable access in our system. 233 | \end{itemize} 234 | 235 | Transaction variables expose most of their functionality through \textbf{local variable} objects. A local variable is a proxy object that holds a memory reference to a transaction variable and a memory reference to a transaction. Local variables are representations of the value of a transaction variable within a given transaction. These local variables use a smart pointer interface. Dereferencing a local variable will produce an object that can be assigned to; such an assignment is implemented using the write operation of the transaction manager. The structure dereference operator can be used to invoke non-mutating methods of the underlying type, and the indirection operator can be used to produce a constant reference to an object of the underlying type. Both of these operations are implemented using the read operation of the transaction manager. A syntax table for local variables is provided below; the syntax and semantics of local variables is based on C++ \lstinline$std::shared_ptr<>$. 236 | 237 | \begin{table}[htbp] 238 | \center 239 | \begin{tabular}{|l|m{0.4\linewidth}|} 240 | \hline 241 | \textbf{Syntax} & \textbf{Effect} \\ 242 | \hline 243 | \lstinline$auto A_ = A.local(t)$ & Create a local variable \lstinline$A_$ which represents the state of the transaction variable \lstinline$A$ (with underlying type \lstinline$X$) in transaction \lstinline$t$ \\ 244 | \hline 245 | \lstinline$A_->f()$ & Invoke the \lstinline$const$ member function \lstinline$f$ of the underlying object represented by \lstinline$A_$ \\ 246 | \hline 247 | \lstinline$*A_ = b$ & Perform a write operation (using transaction manager \lstinline$t$) assigning \lstinline$b$ to \lstinline$A_$ \\ 248 | \hline 249 | \lstinline$X x = *A_$ & Perform a read operation (using transaction manager \lstinline$t$) on \lstinline$A_$ \\ 250 | \hline 251 | \end{tabular} 252 | \end{table} 253 | 254 | \subsection{Transaction Futures} 255 | 256 | A transaction future representing the result of a computation which has been "escaped" from the transaction. They are created with the async operation. Transaction futures can be thought of as a representation of a value that may not be known yet because its computation has not completed execution. 257 | Transaction futures are used to integrate side-effecting and computationally expensive code into transactions. A function invoked via the async operation will have the following properties: 258 | 259 | \begin{itemize} 260 | \item Execution of the function will only occur if the transaction commits successfully. 261 | \item Execution will only occur after all other effects of the transaction have been written back to shared storage. 262 | \item Local variables that are passed to or referenced by the function will be copied when the function is passed to the async operation; e.g. the function will execute in a closure obtained from within the transaction. 263 | \item The function will not execute inside of a transaction. 264 | \end{itemize} 265 | 266 | Escaped functions have no restrictions on what operations they may contain, as long as they do not violate the invariants regarding transaction variable access (e.g. transaction variables may only be accessed concurrently from within a transaction). Escaped functions may contain operations that have side-effects or operations which are considered too time consuming to be executed within a critical section. 267 | 268 | However, the async operation introduces potential concurrency issues which must be addressed. If two transactions which are running concurrently each launch an escaped function which accesses the same non-transaction, non-thread-safe variables (e.g. instances of regular C++ data structures not synchronized with ASTM), it is clear that race conditions may arise. This is problematic because escaped functions are most useful if you can seperate your data into two categories: objects accessed only through transactions (represented by transaction variables) and objects that are accessed only through escaped functions. 269 | 270 | To solve this problem, we take advantage of the composability of futures. C++ futures allow the construction of dependency chains. Instead of waiting for the value of a future to become ready, application code can pass a callback function to the future which will be invoked when the data is ready. The operation that registers such a callback is called \lstinline$then()$, and it returns another future (representing the execution of the callback), which enables chaining. Another operation, \lstinline$when_all()$, allow the composition of multiple futures into one future, which can then have a callback attached to it in the same fashion. 271 | 272 | The async operation in ASTM makes use of the \lstinline$then()$ method to launch escaped functions. Application code provides a future as an argument to the transaction managers async operation. If this future is empty, then the escaped function does not have any dependencies, and after the transaction commits successfully it will be scheduled without constraints (in this case, the empty future argument passed to the async operation will be used to hold the return value of \lstinline$hpx::async$). If the future is not null, then the escaped function will be attached to it as a callback (via the \lstinline$then()$ method). In either case, the async operation will create a new future during a successful commit; this new future will be assigned to the argument passed to async within the transaction (the operation is similar to the effect of compound assignment operators such as \lstinline$+=$). 273 | 274 | This functionality allows escaped functions to access shared data safely. As mentioned, applications using escaped functions will divide their data into two categories: objects accessed only from within transactions, and objects accessed only from escaped functions. The shared objects accessed through escaped functions are represented by future objects in the application codes. When an escaped function needs to access one of these objects, it will be attached to the object as a callback function. As long as all shared data accessed by escaped functions is passed by futures, concurrency will be guranteed. 275 | 276 | \section{Applications/Results} 277 | 278 | \subsection{Binary Tree with Side-Effecting Constructors} 279 | 280 | Generic, concurrent containers are often used as the building blocks of large-scale parallel applications. We present an example implementation of an unbalanced binary tree implemented using ASTM. This binary tree supports the use of values which are have side-effecting constructors. The following requirements are placed on the user-supplied types \lstinline$Key$ and \lstinline$Value$: 281 | 282 | \begin{itemize} 283 | \item \lstinline$Key$ must be copyable, assignmable, comparable and sortable; these operations must be side-effect free. 284 | \item \lstinline$Value$ must be copyable. 285 | \end{itemize} 286 | 287 | Each tree node consists of four data members: the key, the value and two child pointers (left and right). The key and the child pointers are stored in transaction variables (\lstinline$astm::variable<>$). The value is stored in a transaction future (\lstinline$astm::future<>$). 288 | 289 | The listing below presents the code for the node class: 290 | 291 | \begin{lstlisting} 292 | template 293 | struct node 294 | { 295 | astm::variable key; 296 | astm::future > value; 297 | astm::variable left; 298 | astm::variable right; 299 | 300 | node() = default; 301 | 302 | node(Key k, Value const& v, astm::transaction& t) 303 | : key(k) 304 | { 305 | astm::new_(value, t, v); 306 | } 307 | }; 308 | \end{lstlisting} 309 | 310 | During construction of a non-empty node, \lstinline$astm::new_<>$ is used to allocate the value of the node. This routine creates an escaped function which will perform the actual allocation and object construction when the transaction \lstinline$t$ commits successfully. If the transaction fails, the tree is not modified and no allocation is performed; e.g. the tree gurantees that instances of \lstinline$Value$ will never be created speculatively. 311 | 312 | Below is a simplified version of the \lstinline$astm::new_<>$ routine, which demonstrates how escaped functions are launched in a transaction. 313 | 314 | \begin{lstlisting} 315 | template 316 | astm::future > 317 | new_(astm::future& fut, 318 | astm::transaction& t, Args&&... args 319 | ) 320 | { 321 | return t.async(fut, 322 | [=] (astm::future > f) 323 | { 324 | return new T(std::forward(args)...); 325 | } 326 | ); 327 | } 328 | \end{lstlisting} 329 | 330 | The \lstinline$async$ operation of the transaction manager (\lstinline$astm::transaction$) takes two arguments, as described in the preceding section. The \lstinline$fut$ argument of \lstinline$astm::new_<>$ is either an empty future (indicating that the operation has no dependencies) or a non-empty future (indicating that the allocation must be attached to the future and wait on it). The rest of the arguments are passed to the constructor of the underlying type. 331 | 332 | In the case of the binary tree, we call \lstinline$astm::new_<>$ when a new tree node is being constructed, so there are no dependencies (the future representing the value is empty). If we were to define a copy operation for a tree node, we would be calling \lstinline$astm::new_<>$ with the future of the LHS of the assignment, and the value of the RHS of the assignment. In this case, the escaped allocation would be attached as a continuation to the LHS of the assignment, waiting for any preceeding operations on it to complete before overwriting the value of the LHS with the value of the RHS. 333 | 334 | The following code implements insertion for our container: 335 | 336 | \begin{lstlisting} 337 | void insert(Key key, Value const& value, 338 | astm::variable& leaf, 339 | astm::transaction& t 340 | ) 341 | { 342 | auto leaf_ = leaf.local(t); 343 | auto leaf_key_ = leaf_->key.local(t); 344 | 345 | if (key < *leaf_key_) 346 | { 347 | auto leaf_left_ = leaf_->left.local(t); 348 | 349 | if (leaf_left_ != nullptr) 350 | insert(key, value, leaf_->left, t); 351 | else 352 | leaf_left_ = new node(key, value, t); 353 | } 354 | 355 | else if (key >= *leaf_key_) 356 | { 357 | auto leaf_right_ = leaf_->right.local(t); 358 | 359 | if (leaf_right_ != nullptr) 360 | insert(key, value, leaf_->right, t); 361 | else 362 | leaf_->right = new node(key, value, t); 363 | } 364 | } 365 | \end{lstlisting} 366 | 367 | The algorithm is fairly similar to what the serial implementation built with \lstinline$std::shared_ptr<>$ might look like. First, a local variable to the \lstinline$leaf$ transaction variable (a parameter) is obtained. Then we obtain the local variable for the key of the leaf- a read operation occurs here when we access the leaf through the structure derefence operator (\lstinline$leaf_->key.local(t)$). Next, we compare the key being inserted (which is a regular C++ variable) to the the leaf key. The comparison expression (\lstinline$key < *leaf_key_$) will invoke a read operation on the leaf key. 368 | 369 | The rests of the comparisons in the routine are similar in nature. First a local variable referring to a data member of a particular node instance is obtained, which invokes a read operation on the node. Then the data member itself is read when it is dereferenced in the comparison expression. 370 | 371 | Once the location for the new node is found through recursion of the tree, the insertion is performed by allocating a new tree node and assigning it to the local variable which references the correct child pointer. As we have mentioned, the tree node will not allocate its associated value when it is created; the creation of the value is deferred until the transaction commits successfully. The construction of the tree node is therefore cheap - only the key and two empty nodes (the child pointers) need to be constructed. If the transaction fails to commit, the tree node which was speculatively constructed during the transaction will be destroyed properly, because the commit algorithm will clear the state of the transaction which will release the last reference count to the tree node. 372 | 373 | Finally, we have the top-level interface for the insert algorithm, which starts the transaction: 374 | 375 | \begin{lstlisting} 376 | void insert(Key key, Value const& value, 377 | astm::variable& root 378 | ) 379 | { 380 | astm::transaction t; 381 | do { 382 | auto root_ = root.local(t); 383 | 384 | if (*root_ != 0) 385 | insert(key, value, root, t); 386 | else 387 | root_ = new node(key, value, t); 388 | } while (!t.commit()); 389 | } 390 | \end{lstlisting} 391 | 392 | As we can see from this example, the syntax of ASTM abstracts programmers away from performing explicit reads and writes through the use of smart pointer syntax and semantics. However, it is easy to identify where these operations will occur in the code; structure dereference (->) and indirection (*) will cause read operations and assignment will cause write operations. 393 | 394 | \section{Future Work/Conclusions} 395 | 396 | % use section* for acknowledgement 397 | \section*{Acknowledgment} 398 | 399 | %\bibliographystyle{IEEEtran} 400 | %\bibliography{astm} 401 | 402 | % that's all folks 403 | \end{document} 404 | 405 | 406 | 407 | \end{document} 408 | --------------------------------------------------------------------------------