Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/windirstat/llfio.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/attic
diff options
context:
space:
mode:
authorNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spam@nowhere>2016-03-21 02:41:51 +0300
committerNiall Douglas (s [underscore] sourceforge {at} nedprod [dot] com) <spam@nowhere>2016-03-21 02:41:51 +0300
commit758a934ab266ed660daa54b72e4606b78e374071 (patch)
tree6f2fe1c5d2b8331f9319549bc6f0c3390168eb6b /attic
AFIO v2: Relocate all the AFIO v2 files in fs_probe into the root hierarchy. AFIO v2 is now the master branch!
Diffstat (limited to 'attic')
-rw-r--r--attic/.travis.yml92
-rw-r--r--attic/Readme.md201
-rw-r--r--attic/appveyor.yml41
-rw-r--r--attic/build/Jamfile.v262
-rw-r--r--attic/build/afio.vcxproj114
-rw-r--r--attic/build/afio_standalone.vcxproj102
-rwxr-xr-xattic/clang-reformat.sh17
-rw-r--r--attic/detail/SpookyV2.cpp350
-rw-r--r--attic/detail/SpookyV2.h302
-rw-r--r--attic/doc/Jamfile.v274
-rw-r--r--attic/doc/Readme.txt63
-rw-r--r--attic/doc/acknowledgments.qbk65
-rw-r--r--attic/doc/advanced_topics.qbk106
-rw-r--r--attic/doc/afio.qbk238
-rw-r--r--attic/doc/benchmarks.xlsxbin0 -> 8707 bytes
-rw-r--r--attic/doc/compiling.qbk1196
-rw-r--r--attic/doc/copyright_block.qbk7
-rw-r--r--attic/doc/design_rationale.qbk462
-rw-r--r--attic/doc/disqus_comments.html42
-rw-r--r--attic/doc/disqus_identifiers/async_close.html4
-rw-r--r--attic/doc/disqus_identifiers/async_dir_2_absolute.html4
-rw-r--r--attic/doc/disqus_identifiers/async_dir_3_relative.html4
-rw-r--r--attic/doc/disqus_identifiers/async_enumerate_6_glob_first.html4
-rw-r--r--attic/doc/disqus_identifiers/async_enumerate_6_maxitems_first.html4
-rw-r--r--attic/doc/disqus_identifiers/async_enumerate_6_metadata_first.html4
-rw-r--r--attic/doc/disqus_identifiers/async_extents.html4
-rw-r--r--attic/doc/disqus_identifiers/async_file_2_absolute.html4
-rw-r--r--attic/doc/disqus_identifiers/async_file_3_relative.html4
-rw-r--r--attic/doc/disqus_identifiers/async_op_flags.html4
-rw-r--r--attic/doc/disqus_identifiers/async_read_3_length_deducing.html4
-rw-r--r--attic/doc/disqus_identifiers/async_read_4_length_specifying.html4
-rw-r--r--attic/doc/disqus_identifiers/async_rmdir_2_absolute.html4
-rw-r--r--attic/doc/disqus_identifiers/async_rmdir_3_relative.html4
-rw-r--r--attic/doc/disqus_identifiers/async_rmfile_2_absolute.html4
-rw-r--r--attic/doc/disqus_identifiers/async_rmfile_3_relative.html4
-rw-r--r--attic/doc/disqus_identifiers/async_rmsymlink_2_absolute.html4
-rw-r--r--attic/doc/disqus_identifiers/async_rmsymlink_3_relative.html4
-rw-r--r--attic/doc/disqus_identifiers/async_statfs.html4
-rw-r--r--attic/doc/disqus_identifiers/async_symlink_3_absolute.html4
-rw-r--r--attic/doc/disqus_identifiers/async_symlink_4_relative.html4
-rw-r--r--attic/doc/disqus_identifiers/async_sync.html4
-rw-r--r--attic/doc/disqus_identifiers/async_truncate.html4
-rw-r--r--attic/doc/disqus_identifiers/async_write_3_length_deducing.html4
-rw-r--r--attic/doc/disqus_identifiers/async_write_4_length_specifying.html4
-rw-r--r--attic/doc/disqus_identifiers/async_zero.html4
-rw-r--r--attic/doc/disqus_identifiers/atomic_logging.html4
-rw-r--r--attic/doc/disqus_identifiers/atomic_relink.html4
-rw-r--r--attic/doc/disqus_identifiers/boost_afio_validate_inputs.html4
-rw-r--r--attic/doc/disqus_identifiers/close_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/close_1_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/close_2_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/compilation.html4
-rw-r--r--attic/doc/disqus_identifiers/complete_async_op_2_errored.html4
-rw-r--r--attic/doc/disqus_identifiers/complete_async_op_3_normal.html4
-rw-r--r--attic/doc/disqus_identifiers/current_dispatcher.html4
-rw-r--r--attic/doc/disqus_identifiers/current_dispatcher_guard.html4
-rw-r--r--attic/doc/disqus_identifiers/depends.html4
-rw-r--r--attic/doc/disqus_identifiers/design_rationale.html4
-rw-r--r--attic/doc/disqus_identifiers/dir_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/dir_2_absolute_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/dir_3_absolute_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/dir_3_relative_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/dir_4_relative_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/directory_entry.html4
-rw-r--r--attic/doc/disqus_identifiers/directory_entry_hash.html4
-rw-r--r--attic/doc/disqus_identifiers/direntry.html4
-rw-r--r--attic/doc/disqus_identifiers/dispatcher.html4
-rw-r--r--attic/doc/disqus_identifiers/enqueued_task.html4
-rw-r--r--attic/doc/disqus_identifiers/enqueued_task_r___.html4
-rw-r--r--attic/doc/disqus_identifiers/enqueued_task_void___.html4
-rw-r--r--attic/doc/disqus_identifiers/enumerate_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/enumerate_6_glob_first_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/enumerate_6_max_items_first_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/enumerate_6_metadata_first_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/enumerate_7_glob_first_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/enumerate_7_max_items_first_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/enumerate_7_metadata_first_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/enumerate_req.html4
-rw-r--r--attic/doc/disqus_identifiers/extents_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/extents_1_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/extents_2_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/file_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/file_2_absolute_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/file_3_absolute_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/file_3_relative_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/file_4_relative_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/file_buffer_default_size.html4
-rw-r--r--attic/doc/disqus_identifiers/file_concat.html4
-rw-r--r--attic/doc/disqus_identifiers/file_flags.html4
-rw-r--r--attic/doc/disqus_identifiers/filesystem_races.html4
-rw-r--r--attic/doc/disqus_identifiers/filter.html4
-rw-r--r--attic/doc/disqus_identifiers/from_hex_string.html4
-rw-r--r--attic/doc/disqus_identifiers/fs_metadata_flags.html4
-rw-r--r--attic/doc/disqus_identifiers/future.html4
-rw-r--r--attic/doc/disqus_identifiers/future_void_.html4
-rw-r--r--attic/doc/disqus_identifiers/handle.html4
-rw-r--r--attic/doc/disqus_identifiers/handle_mapped_file.html4
-rw-r--r--attic/doc/disqus_identifiers/hello_world.html4
-rw-r--r--attic/doc/disqus_identifiers/introduction.html4
-rw-r--r--attic/doc/disqus_identifiers/io_req.html4
-rw-r--r--attic/doc/disqus_identifiers/io_req_constt_.html4
-rw-r--r--attic/doc/disqus_identifiers/io_req_constvoid_.html4
-rw-r--r--attic/doc/disqus_identifiers/io_req_void_.html4
-rw-r--r--attic/doc/disqus_identifiers/is_future.html4
-rw-r--r--attic/doc/disqus_identifiers/is_future_-future_-t-_.html4
-rw-r--r--attic/doc/disqus_identifiers/link.html4
-rw-r--r--attic/doc/disqus_identifiers/lock_req.html4
-rw-r--r--attic/doc/disqus_identifiers/make_dispatcher.html4
-rw-r--r--attic/doc/disqus_identifiers/make_io_req_3_length_deducing.html4
-rw-r--r--attic/doc/disqus_identifiers/make_io_req_4_length_specifying.html4
-rw-r--r--attic/doc/disqus_identifiers/metadata_flags.html4
-rw-r--r--attic/doc/disqus_identifiers/normalise_path.html4
-rw-r--r--attic/doc/disqus_identifiers/open_states.html4
-rw-r--r--attic/doc/disqus_identifiers/overview.html4
-rw-r--r--attic/doc/disqus_identifiers/page_sizes.html4
-rw-r--r--attic/doc/disqus_identifiers/path.html4
-rw-r--r--attic/doc/disqus_identifiers/path_direct.html4
-rw-r--r--attic/doc/disqus_identifiers/path_hash.html4
-rw-r--r--attic/doc/disqus_identifiers/path_make_absolute.html4
-rw-r--r--attic/doc/disqus_identifiers/path_req.html4
-rw-r--r--attic/doc/disqus_identifiers/path_req_absolute.html4
-rw-r--r--attic/doc/disqus_identifiers/path_req_relative.html4
-rw-r--r--attic/doc/disqus_identifiers/process_threadpool.html4
-rw-r--r--attic/doc/disqus_identifiers/random_fill.html4
-rw-r--r--attic/doc/disqus_identifiers/random_string.html4
-rw-r--r--attic/doc/disqus_identifiers/read_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/read_3_length_deducing_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/read_4_length_deducing_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/read_4_length_specifying_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/read_5_length_specifying_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmdir_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/rmdir_2_absolute_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmdir_3_absolute_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmdir_3_relative_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmdir_4_relative_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmfile_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/rmfile_2_absolute_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmfile_3_absolute_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmfile_3_relative_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmfile_4_relative_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmsymlink_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/rmsymlink_2_absolute_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmsymlink_3_absolute_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmsymlink_3_relative_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/rmsymlink_4_relative_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/so_what.html4
-rw-r--r--attic/doc/disqus_identifiers/stat_t.html4
-rw-r--r--attic/doc/disqus_identifiers/statfs_2_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/statfs_2_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/statfs_3_nonthrowing.html4
-rw-r--r--attic/doc/disqus_identifiers/statfs_t.html4
-rw-r--r--attic/doc/disqus_identifiers/statfs_t_f_flags_t.html4
-rw-r--r--attic/doc/disqus_identifiers/std_thread_pool.html4
-rw-r--r--attic/doc/disqus_identifiers/std_thread_pool_worker.html4
-rw-r--r--attic/doc/disqus_identifiers/symlink_2_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/symlink_3_absolute_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/symlink_4_absolute_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/symlink_4_relative_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/symlink_5_relative_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/sync_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/sync_1_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/sync_2_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/target.html4
-rw-r--r--attic/doc/disqus_identifiers/thread_source.html4
-rw-r--r--attic/doc/disqus_identifiers/to_asio_buffers_1_asio_const_buffer.html4
-rw-r--r--attic/doc/disqus_identifiers/to_asio_buffers_1_asio_mutable_buffer.html4
-rw-r--r--attic/doc/disqus_identifiers/to_asio_buffers_1_c_arrays.html4
-rw-r--r--attic/doc/disqus_identifiers/to_asio_buffers_1_const_c_arrays.html4
-rw-r--r--attic/doc/disqus_identifiers/to_asio_buffers_1_const_trivial_and_container_types.html4
-rw-r--r--attic/doc/disqus_identifiers/to_asio_buffers_1_trivial_and_container_types.html4
-rw-r--r--attic/doc/disqus_identifiers/to_asio_buffers_2_buffer.html4
-rw-r--r--attic/doc/disqus_identifiers/to_asio_buffers_2_buffer_of_t.html4
-rw-r--r--attic/doc/disqus_identifiers/to_asio_buffers_2_const_buffer_of_t.html4
-rw-r--r--attic/doc/disqus_identifiers/to_hex_string.html4
-rw-r--r--attic/doc/disqus_identifiers/truncate_2_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/truncate_2_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/truncate_3_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/type.html4
-rw-r--r--attic/doc/disqus_identifiers/unlink.html4
-rw-r--r--attic/doc/disqus_identifiers/utils_page_allocator.html4
-rw-r--r--attic/doc/disqus_identifiers/utils_page_allocator_-void-_.html4
-rw-r--r--attic/doc/disqus_identifiers/utils_page_allocator_rebind.html4
-rw-r--r--attic/doc/disqus_identifiers/utils_secded_ecc.html4
-rw-r--r--attic/doc/disqus_identifiers/verify_status.html4
-rw-r--r--attic/doc/disqus_identifiers/write_1_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/write_3_length_deducing_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/write_4_length_deducing_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/write_4_length_specifying_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/write_5_length_specifying_non_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/zero_2_batch.html4
-rw-r--r--attic/doc/disqus_identifiers/zero_2_throwing.html4
-rw-r--r--attic/doc/disqus_identifiers/zero_3_non_throwing.html4
-rw-r--r--attic/doc/doxy/Doxyfile248
-rw-r--r--attic/doc/doxy/doxygen_enhance.py33
-rw-r--r--attic/doc/doxy/doxygen_input/doxygen_footer.html22
-rw-r--r--attic/doc/doxy/doxygen_input/doxygen_header.html24
-rw-r--r--attic/doc/doxy/doxygen_input/groups/groups.hpp48
-rw-r--r--attic/doc/doxy/doxygen_input/pages/doxygen_mainpage.hpp26
-rw-r--r--attic/doc/doxy/make_documentation.bat17
-rw-r--r--attic/doc/doxygen_xml2qbk.patch113
-rw-r--r--attic/doc/generated/class_current_dispatcher_guard.qbk81
-rw-r--r--attic/doc/generated/class_directory_entry.qbk405
-rw-r--r--attic/doc/generated/class_dispatcher.qbk341
-rw-r--r--attic/doc/generated/class_enqueued_task.qbk37
-rw-r--r--attic/doc/generated/class_enqueued_task_3_01_r_07_08_4.qbk144
-rw-r--r--attic/doc/generated/class_enqueued_task_3_01void_07_08_4.qbk136
-rw-r--r--attic/doc/generated/class_future.qbk114
-rw-r--r--attic/doc/generated/class_future_3_01void_01_4.qbk336
-rw-r--r--attic/doc/generated/class_handle.qbk276
-rw-r--r--attic/doc/generated/class_path.qbk387
-rw-r--r--attic/doc/generated/class_std_thread_pool.qbk105
-rw-r--r--attic/doc/generated/class_std_thread_pool_1_1worker.qbk55
-rw-r--r--attic/doc/generated/class_thread_source.qbk70
-rw-r--r--attic/doc/generated/class_utils_1_1page_allocator.qbk141
-rw-r--r--attic/doc/generated/class_utils_1_1page_allocator_3_01void_01_4.qbk30
-rw-r--r--attic/doc/generated/class_utils_1_1secded_ecc.qbk175
-rw-r--r--attic/doc/generated/group_async_file_io_dispatcher.qbk109
-rw-r--r--attic/doc/generated/group_async_io_handle__ops.qbk237
-rw-r--r--attic/doc/generated/group_async_op_flags.qbk38
-rw-r--r--attic/doc/generated/group_close.qbk142
-rw-r--r--attic/doc/generated/group_dir.qbk298
-rw-r--r--attic/doc/generated/group_dispatcher__barrier.qbk11
-rw-r--r--attic/doc/generated/group_dispatcher__call.qbk11
-rw-r--r--attic/doc/generated/group_dispatcher__completion.qbk11
-rw-r--r--attic/doc/generated/group_dispatcher__depends.qbk43
-rw-r--r--attic/doc/generated/group_dispatcher__enumerate.qbk11
-rw-r--r--attic/doc/generated/group_dispatcher__extents.qbk11
-rw-r--r--attic/doc/generated/group_dispatcher__filedirops.qbk11
-rw-r--r--attic/doc/generated/group_dispatcher__filter.qbk11
-rw-r--r--attic/doc/generated/group_dispatcher__misc.qbk68
-rw-r--r--attic/doc/generated/group_dispatcher__statfs.qbk11
-rw-r--r--attic/doc/generated/group_enumerate.qbk481
-rw-r--r--attic/doc/generated/group_extents.qbk172
-rw-r--r--attic/doc/generated/group_file.qbk301
-rw-r--r--attic/doc/generated/group_file_flags.qbk84
-rw-r--r--attic/doc/generated/group_fs_metadata_flags.qbk66
-rw-r--r--attic/doc/generated/group_io_req.qbk11
-rw-r--r--attic/doc/generated/group_macros.qbk37
-rw-r--r--attic/doc/generated/group_make_io_req.qbk115
-rw-r--r--attic/doc/generated/group_metadata_flags.qbk80
-rw-r--r--attic/doc/generated/group_normalise_path.qbk41
-rw-r--r--attic/doc/generated/group_process_threadpool.qbk37
-rw-r--r--attic/doc/generated/group_read.qbk272
-rw-r--r--attic/doc/generated/group_rmdir.qbk293
-rw-r--r--attic/doc/generated/group_rmfile.qbk293
-rw-r--r--attic/doc/generated/group_rmsymlink.qbk293
-rw-r--r--attic/doc/generated/group_statfs.qbk180
-rw-r--r--attic/doc/generated/group_symlink.qbk311
-rw-r--r--attic/doc/generated/group_sync.qbk142
-rw-r--r--attic/doc/generated/group_to_asio_buffers.qbk305
-rw-r--r--attic/doc/generated/group_truncate.qbk134
-rw-r--r--attic/doc/generated/group_utils.qbk200
-rw-r--r--attic/doc/generated/group_when_all_futures.qbk11
-rw-r--r--attic/doc/generated/group_write.qbk272
-rw-r--r--attic/doc/generated/group_zero.qbk146
-rw-r--r--attic/doc/generated/struct_directory_entry_hash.qbk45
-rw-r--r--attic/doc/generated/struct_enumerate_req.qbk146
-rw-r--r--attic/doc/generated/struct_handle_1_1mapped_file.qbk96
-rw-r--r--attic/doc/generated/struct_io_req.qbk136
-rw-r--r--attic/doc/generated/struct_io_req_3_01const_01_t_01_4.qbk169
-rw-r--r--attic/doc/generated/struct_io_req_3_01const_01void_01_4.qbk132
-rw-r--r--attic/doc/generated/struct_io_req_3_01void_01_4.qbk112
-rw-r--r--attic/doc/generated/struct_is_future.qbk38
-rw-r--r--attic/doc/generated/struct_is_future_3_01future_3_01_t_01_4_01_4.qbk38
-rw-r--r--attic/doc/generated/struct_lock_req.qbk133
-rw-r--r--attic/doc/generated/struct_path_1_1direct.qbk30
-rw-r--r--attic/doc/generated/struct_path_1_1make_absolute.qbk321
-rw-r--r--attic/doc/generated/struct_path_hash.qbk45
-rw-r--r--attic/doc/generated/struct_path_req.qbk135
-rw-r--r--attic/doc/generated/struct_path_req_1_1absolute.qbk64
-rw-r--r--attic/doc/generated/struct_path_req_1_1relative.qbk73
-rw-r--r--attic/doc/generated/struct_stat_t.qbk79
-rw-r--r--attic/doc/generated/struct_statfs_t.qbk55
-rw-r--r--attic/doc/generated/struct_statfs_t_1_1f_flags_t.qbk37
-rw-r--r--attic/doc/generated/struct_utils_1_1page_allocator_1_1rebind.qbk37
-rw-r--r--attic/doc/generated/struct_utils_1_1page_allocator_3_01void_01_4_1_1rebind.qbk37
-rw-r--r--attic/doc/html/myboostbook.css723
-rw-r--r--attic/doc/latencies.xlsxbin0 -> 39808 bytes
-rwxr-xr-xattic/doc/make_qbk.py245
-rw-r--r--attic/doc/power_loss_safety_table.qbk17
-rw-r--r--attic/doc/quickstart.qbk1261
-rw-r--r--attic/doc/reference.qbk110
-rw-r--r--attic/doc/release_notes.qbk833
-rw-r--r--attic/doc/src/images/afio_latencies.pdfbin0 -> 210760 bytes
-rw-r--r--attic/doc/src/images/afio_latencies.pngbin0 -> 34920 bytes
-rw-r--r--attic/doc/src/images/afio_latencies_1.2.pdfbin0 -> 205378 bytes
-rw-r--r--attic/doc/src/images/afio_latencies_1.2.pngbin0 -> 67723 bytes
-rw-r--r--attic/doc/src/images/afio_latencies_1.3.pdfbin0 -> 210760 bytes
-rw-r--r--attic/doc/src/images/afio_latencies_1.3.pngbin0 -> 34920 bytes
-rw-r--r--attic/doc/src/images/boost.pngbin0 -> 13364 bytes
-rw-r--r--attic/doc/src/images/boost_full.pngbin0 -> 6308 bytes
-rw-r--r--attic/doc/src/images/boost_proposed.pngbin0 -> 13364 bytes
-rw-r--r--attic/doc/src/images/boost_proposed.svg449
-rw-r--r--attic/doc/src/images/workshop_atomic_updates_insertions.pngbin0 -> 22412 bytes
-rw-r--r--attic/doc/src/images/workshop_atomic_updates_insertions_1.3.pngbin0 -> 22412 bytes
-rw-r--r--attic/doc/src/images/workshop_atomic_updates_lookups.pngbin0 -> 25160 bytes
-rw-r--r--attic/doc/src/images/workshop_atomic_updates_lookups_1.3.pngbin0 -> 25160 bytes
-rw-r--r--attic/doc/src/images/workshop_naive_insertions.pngbin0 -> 18169 bytes
-rw-r--r--attic/doc/src/images/workshop_naive_insertions_1.3.pngbin0 -> 18169 bytes
-rw-r--r--attic/doc/src/images/workshop_naive_lookups.pngbin0 -> 21792 bytes
-rw-r--r--attic/doc/src/images/workshop_naive_lookups_1.3.pngbin0 -> 21792 bytes
-rw-r--r--attic/doc/workshop.xlsxbin0 -> 21693 bytes
-rw-r--r--attic/example/.clang-format57
-rw-r--r--attic/example/adopt_example.cpp57
-rw-r--r--attic/example/barrier_example.cpp67
-rw-r--r--attic/example/benchmark_asio.cpp47
-rw-r--r--attic/example/benchmark_atomic_log.cpp521
-rw-r--r--attic/example/benchmark_chained1.cpp48
-rw-r--r--attic/example/benchmark_chained2.cpp43
-rw-r--r--attic/example/benchmark_latency.cpp162
-rw-r--r--attic/example/benchmark_unchained1.cpp43
-rw-r--r--attic/example/benchmark_unchained2.cpp37
-rw-r--r--attic/example/call_example.cpp25
-rw-r--r--attic/example/closure_execution_afio_io_example.cpp61
-rw-r--r--attic/example/closure_execution_traditional_io_example.cpp53
-rw-r--r--attic/example/completion_example1.cpp58
-rw-r--r--attic/example/completion_example2.cpp77
-rw-r--r--attic/example/determine_legal_filenames.cpp35
-rw-r--r--attic/example/enumerate_example.cpp43
-rw-r--r--attic/example/filecopy_example.cpp185
-rw-r--r--attic/example/filedir_example.cpp74
-rw-r--r--attic/example/filter_example.cpp36
-rw-r--r--attic/example/find_in_files_afio.cpp351
-rw-r--r--attic/example/find_in_files_iostreams.cpp109
-rw-r--r--attic/example/readallof_example.cpp54
-rw-r--r--attic/example/readwrite_example.cpp78
-rw-r--r--attic/example/readwrite_example_traditional.cpp46
-rw-r--r--attic/example/statfs_example.cpp18
-rw-r--r--attic/example/workshop_atomic_updates_afio.cpp2
-rw-r--r--attic/example/workshop_atomic_updates_afio.ipp323
-rw-r--r--attic/example/workshop_benchmark.cpp151
-rw-r--r--attic/example/workshop_final_afio.cpp2
-rw-r--r--attic/example/workshop_final_afio.ipp665
-rw-r--r--attic/example/workshop_naive.cpp2
-rw-r--r--attic/example/workshop_naive.ipp126
-rw-r--r--attic/example/workshop_naive_afio.cpp2
-rw-r--r--attic/example/workshop_naive_afio.ipp269
-rw-r--r--attic/example/workshop_naive_async_afio.cpp2
-rw-r--r--attic/example/workshop_naive_async_afio.ipp272
-rw-r--r--attic/include/boost/afio/v2/afio.hpp6350
-rw-r--r--attic/include/boost/afio/v2/config.hpp491
-rw-r--r--attic/include/boost/afio/v2/detail/ErrorHandling.hpp81
-rw-r--r--attic/include/boost/afio/v2/detail/Undoer.hpp97
-rw-r--r--attic/include/boost/afio/v2/detail/Utility.hpp295
-rw-r--r--attic/include/boost/afio/v2/detail/impl/ErrorHandling.ipp184
-rw-r--r--attic/include/boost/afio/v2/detail/impl/afio.ipp3670
-rw-r--r--attic/include/boost/afio/v2/detail/impl/afio_iocp.ipp1957
-rw-r--r--attic/include/boost/afio/v2/detail/impl/nt_kernel_stuff.hpp726
-rw-r--r--attic/include/boost/afio/v2/detail/valgrind/helgrind.h739
-rw-r--r--attic/include/boost/afio/v2/detail/valgrind/memcheck.h287
-rw-r--r--attic/include/boost/afio/v2/detail/valgrind/valgrind.h5415
-rw-r--r--attic/index.html3
-rwxr-xr-xattic/multiabi_alltests_gcc.sh26
-rw-r--r--attic/multiabi_alltests_msvc.bat10
-rwxr-xr-xattic/send_to_wandbox.sh24
-rw-r--r--attic/src/afio.cpp27
-rw-r--r--attic/standalone_alltests_gcc.bat1
-rwxr-xr-xattic/standalone_alltests_gcc.sh26
-rw-r--r--attic/standalone_alltests_msvc.bat18
-rw-r--r--attic/test/Aligned_Allocator.hpp259
-rw-r--r--attic/test/Jamfile.v2240
-rw-r--r--attic/test/afio_pch.hpp7
-rw-r--r--attic/test/asan.supp0
-rw-r--r--attic/test/blacklist.supp39
-rw-r--r--attic/test/drd.supp88
-rw-r--r--attic/test/json_encode.py3
-rw-r--r--attic/test/memcheck.supp81
-rw-r--r--attic/test/msan.supp0
-rw-r--r--attic/test/test_all.cpp32
-rw-r--r--attic/test/test_all_multiabi.cpp36
-rw-r--r--attic/test/test_file_glob.bat6
-rwxr-xr-xattic/test/test_file_glob.sh8
-rw-r--r--attic/test/test_functions.hpp926
-rw-r--r--attic/test/test_inline_linkage1.cpp33
-rw-r--r--attic/test/test_inline_linkage2.cpp33
-rw-r--r--attic/test/test_inline_linkage_master.cpp9
-rw-r--r--attic/test/tests/api_error_check.cpp39
-rw-r--r--attic/test/tests/async_data_op_req_compilation.cpp449
-rw-r--r--attic/test/tests/async_io_adopt_test.cpp57
-rw-r--r--attic/test/tests/async_io_barrier_test.cpp98
-rw-r--r--attic/test/tests/async_io_enumerate_works.cpp62
-rw-r--r--attic/test/tests/async_io_errors_test.cpp167
-rw-r--r--attic/test/tests/async_io_lstat_works.cpp81
-rw-r--r--attic/test/tests/async_io_pagesize.cpp219
-rw-r--r--attic/test/tests/async_io_statfs_test.cpp47
-rw-r--r--attic/test/tests/async_io_sync_test.cpp35
-rw-r--r--attic/test/tests/async_io_threadpool_test.cpp54
-rw-r--r--attic/test/tests/async_io_torture_autoflush_test.cpp12
-rw-r--r--attic/test/tests/async_io_torture_direct_sync_test.cpp12
-rw-r--r--attic/test/tests/async_io_torture_direct_test.cpp10
-rw-r--r--attic/test/tests/async_io_torture_sync_test.cpp12
-rw-r--r--attic/test/tests/async_io_torture_test.cpp10
-rw-r--r--attic/test/tests/async_io_works_1_autoflush_test.cpp13
-rw-r--r--attic/test/tests/async_io_works_1_prime_test.cpp10
-rw-r--r--attic/test/tests/async_io_works_1_sync_test.cpp12
-rw-r--r--attic/test/tests/async_io_works_1_test.cpp10
-rw-r--r--attic/test/tests/async_io_works_64_autoflush_test.cpp12
-rw-r--r--attic/test/tests/async_io_works_64_direct_test.cpp10
-rw-r--r--attic/test/tests/async_io_works_64_directsync_test.cpp12
-rw-r--r--attic/test/tests/async_io_works_64_sync_test.cpp12
-rw-r--r--attic/test/tests/async_io_works_64_test.cpp10
-rw-r--r--attic/test/tests/async_io_zero_test.cpp130
-rw-r--r--attic/test/tests/atomic_log_append_test.cpp150
-rw-r--r--attic/test/tests/delete_stability.cpp73
-rw-r--r--attic/test/tests/free_functions_test.cpp31
-rw-r--r--attic/test/tests/op_container_deduced_compilation.cpp20
-rw-r--r--attic/test/tests/path_works.cpp177
-rw-r--r--attic/test/tests/race_protection_works.cpp180
-rw-r--r--attic/test/tsan.supp33
-rw-r--r--attic/test/unittests.vcxproj104
-rwxr-xr-xattic/test/update_coveralls.sh41
411 files changed, 47318 insertions, 0 deletions
diff --git a/attic/.travis.yml b/attic/.travis.yml
new file mode 100644
index 00000000..87e6b8ba
--- /dev/null
+++ b/attic/.travis.yml
@@ -0,0 +1,92 @@
+# Travis CI script to invoke coveralls.io coverage reporting
+# (C) 2015 Niall Douglas
+
+language: cpp
+os:
+ - linux
+ - osx
+notifications:
+ email:
+# recipients:
+# - one@example.com
+# - other@example.com
+ on_success: change # [always|never|change] # default: change
+ on_failure: change # [always|never|change] # default: always
+
+env:
+ matrix:
+ - __="GCC 4.8 + Standalone unit/functional tests coverage" VALGRIND=0 CCFLAGS="-fprofile-arcs -ftest-coverage -DBOOST_AFIO_COMPILING_FOR_GCOV -DNDEBUG" GCCVER="g++-4.8" GCOV=1 UNITTESTS=1
+ - __="MacOS X build" VALGRIND=0 CCFLAGS="-DNDEBUG" GCCVER="clang++" GCOV=0 UNITTESTS=0
+ - __="MacOS X test" VALGRIND=0 CCFLAGS="-DNDEBUG" GCCVER="clang++" GCOV=0 UNITTESTS=1
+ global:
+ # Coveralls.io API key
+ - secure: "LU8flpdvLjaFIVgy8oqhHzy92AUPRgw3bRv+/Q91fnWNrvbeRVMEOj+vML+zDS7iU3L2ZbppNf/1YkavGhHh9HfD8PC0dYPEmU0xjS/3WdoKxMd0MCYUZxQ+6zgx3GLgZS2NxciVlruZSs9Q+hKbguvvjwGGSVin6LlMmC2KqGA="
+ # The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
+ # via the "travis encrypt" command using the project repo's public key
+ - secure: "ouEimnVEYE28pbGvib6ks+i3mxl1CE0nBAZuvqvVrEbbcSaauEZZCyYiL291l2j90aSPJOJzbny+AhVwsr8mNLyiI9OGRtcnCFNjOHEXPF0Ep78ebmVLX4oyqv7DEPmeJSR/va/ebCrbAbOlPBCPGOFO5UfNtwvx2qJouMtNQMI="
+
+#addons:
+# coverity_scan:
+# project:
+# name: "ned14/boost.afio"
+# description: "Build submitted via Travis CI"
+# notification_email: s_github@nedprod.com
+# build_command_prepend: "cd test && bash ./test_file_glob.sh && cd .."
+# build_command: "$GCCVER -o test_all -g -O0 -std=c++11 test/test_all.cpp -Iinclude -Itest -DBOOST_AFIO_RUNNING_IN_CI=1 -DBOOST_CONSTEXPR= -DBOOST_CXX14_CONSTEXPR= -DBOOST_AFIO_INCLUDE_SPOOKY_IMPL=1 -Wno-constexpr-not-const -Wno-c++1y-extensions -Wno-unused-value -lboost_filesystem -lboost_system -lpthread -ldl"
+# branch_pattern: master
+
+matrix:
+ exclude:
+ - os: linux
+ - os: osx
+ include:
+ - os: linux
+ env: __="GCC 4.8 + Standalone unit/functional tests coverage" VALGRIND=0 CCFLAGS="-fprofile-arcs -ftest-coverage -DBOOST_AFIO_COMPILING_FOR_GCOV -DNDEBUG" GCCVER="g++-4.8" GCOV=1 UNITTESTS=1
+ - os: osx
+ env: __="MacOS X build" VALGRIND=0 CCFLAGS="-DNDEBUG" GCCVER="clang++" GCOV=0 UNITTESTS=0
+ - os: osx
+ env: __="MacOS X test" VALGRIND=0 CCFLAGS="-DNDEBUG" GCCVER="clang++" GCOV=0 UNITTESTS=1
+
+
+before_install:
+ - if [ "$__" = "MacOS X build" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then exit 0; fi
+ - if [ "$__" = "MacOS X test" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then exit 0; fi
+ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test; fi
+ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo add-apt-repository -y ppa:afrank/boost; fi
+ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get update -qq; fi
+ - if [ "$TRAVIS_OS_NAME" = "linux" ]; then sudo apt-get install -qq libboost1.57-all-dev; fi
+ - if [ "$TRAVIS_OS_NAME" = "linux" ] && [ $GCCVER != "" ]; then sudo apt-get install -qq $GCCVER; fi
+ - git submodule update --init --recursive
+
+script:
+ - if [ -e "cov-int" ]; then ls -l cov-int; fi
+ - if [ -e "cov-int/build-log.txt" ]; then cat cov-int/build-log.txt; fi
+ - if [ -e "cov-int/scm_log.txt" ]; then cat cov-int/scm_log.txt; fi
+ - if [ "$__" = "MacOS X build" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then exit 0; fi
+ - if [ "$__" = "MacOS X test" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then exit 0; fi
+ - CCFLAGS="$CCFLAGS -g -O0 -std=c++11 test/test_all.cpp detail/SpookyV2.cpp -Iinclude -Itest -DBOOST_AFIO_RUNNING_IN_CI=1 -DBOOST_CXX14_CONSTEXPR= -Wno-constexpr-not-const -Wno-c++1y-extensions -Wno-unused-value -lboost_filesystem -lboost_system -lpthread"
+ - if [ $GCOV -eq 1 ]; then LINKFLAGS="$LINKFLAGS -lgcov"; fi
+ - cd test
+ - bash ./test_file_glob.sh
+ - cd ..
+ - $GCCVER -o test_all $CCFLAGS $LINKFLAGS
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then launchctl limit maxfiles; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then sudo launchctl limit maxfiles 10000 10000; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then launchctl limit maxfiles; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ulimit -a; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ulimit -n 10000; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ulimit -a; fi
+# - if [ "$TRAVIS_OS_NAME" = "osx" ]; then sysctl -a; fi
+ - if [ $UNITTESTS -eq 1 ]; then ./test_all; fi
+
+after_success:
+ - if [ "$__" = "MacOS X build" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then exit 0; fi
+ - if [ "$__" = "MacOS X test" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then exit 0; fi
+ - if [ $GCOV -eq 1 ]; then bash -x test/update_coveralls.sh `pwd`; fi
+
+after_failure:
+ - if [ "$__" = "MacOS X build" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then exit 0; fi
+ - if [ "$__" = "MacOS X test" ] && [ "$TRAVIS_OS_NAME" = "linux" ]; then exit 0; fi
+# - if [ "$TRAVIS_OS_NAME" = "osx" ]; then ls -l /cores/; fi
+# - if [ "$TRAVIS_OS_NAME" = "osx" ]; then lldb --help; fi
+ - if [ "$TRAVIS_OS_NAME" = "osx" ] && [ $UNITTESTS -eq 1 ]; then expect scripts/travis_lldb.expect; fi
diff --git a/attic/Readme.md b/attic/Readme.md
new file mode 100644
index 00000000..dcc0e6fa
--- /dev/null
+++ b/attic/Readme.md
@@ -0,0 +1,201 @@
+<p align="center">
+<a href="http://boostgsoc13.github.io/boost.afio/">Documentation can be found here</a>
+</p>
+<h3 align="center">
+Boost.AFIO Jenkins CI status:
+</h3>
+<p align="center">Unit test code coverage is: <a href='https://coveralls.io/r/BoostGSoC13/boost.afio'><img src='https://coveralls.io/repos/BoostGSoC13/boost.afio/badge.png' alt='Coverage Status' /></a></p>
+<p align="center">Appveyor: <a href='https://ci.appveyor.com/project/ned14/boost-afio'><img src='https://ci.appveyor.com/api/projects/status/f89cv89kj8c2nmvb/branch/master'/></a></p>
+
+<center>
+<table border="1" cellpadding="2">
+<tr><th>OS</th><th>Compiler</th><th>STL</th><th>CPU</th><th>Build</th><th>Unit tests</th></tr>
+
+<!-- static analysis clang -->
+<tr align="center"><td rowspan="2">Static analysis</td><td>clang 3.7 tidy + static analyser + GCC 4.8</td><td></td><td></td><td>
+<div><a href='https://ci.nedprod.com/job/Boost.AFIO%20Static%20Analysis%20clang/'><img src='https://ci.nedprod.com/buildStatus/icon?job=Boost.AFIO%20Static%20Analysis%20clang' /></a></div></td><td></td>
+</tr>
+
+<!-- static analysis MSVC -->
+<tr align="center"><td>VS2015</td><td></td><td></td><td>
+<div><a href='https://ci.nedprod.com/job/Boost.AFIO%20Static%20Analysis%20MSVC/'><img src='https://ci.nedprod.com/buildStatus/icon?job=Boost.AFIO%20Static%20Analysis%20MSVC' /></a></div></td><td></td>
+</tr>
+
+<!-- sanitiser -->
+<tr align="center"><td>Thread Sanitiser</td><td>clang 3.4</td><td>libstdc++ 4.9</td><td>x64</td><td></td><td>
+<div><a href='https://ci.nedprod.com/job/Boost.AFIO%20Sanitise%20Linux%20clang%203.4/'><img src='https://ci.nedprod.com/buildStatus/icon?job=Boost.AFIO%20Sanitise%20Linux%20clang%203.4' /></a></div></td>
+</tr>
+
+<!-- valgrind -->
+<tr align="center"><td>Valgrind</td><td>GCC 4.8</td><td>libstdc++ 4.8</td><td>x64</td><td></td><td>
+<div><a href='https://ci.nedprod.com/job/Boost.AFIO%20Valgrind%20Linux%20GCC%204.8/'><img src='https://ci.nedprod.com/buildStatus/icon?job=Boost.AFIO%20Valgrind%20Linux%20GCC%204.8' /></a></div></td>
+</tr>
+
+<!-- sep -->
+<tr></tr>
+
+<!-- os x -->
+<tr align="center"><td>Apple Mac OS X 10.9</td><td>clang 3.5</td><td>libc++</td><td>x64</td><td>
+<div><a href="https://travis-ci.org/BoostGSoC13/boost.afio"><img valign="middle" src="https://travis-ci.org/BoostGSoC13/boost.afio.png?branch=master"/></a></div></td><td>
+<div><a href="https://travis-ci.org/BoostGSoC13/boost.afio"><img valign="middle" src="https://travis-ci.org/BoostGSoC13/boost.afio.png?branch=master"/></a></div></td>
+</tr>
+
+<tr align="center"><td rowspan="2">Android 5.0</td><td>clang 3.5</td><td>libc++</td><td>x86</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td>GCC 4.8</td><td>libc++</td><td>x86</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td rowspan="1">FreeBSD 10.1 on ZFS</td><td>clang 3.4</td><td>libc++</td><td>x86</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=freebsd10-clang3.3/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=freebsd10-clang3.3/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=freebsd10-clang3.3/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=freebsd10-clang3.3/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=freebsd10-clang3.3/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=freebsd10-clang3.3/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=freebsd10-clang3.3/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=freebsd10-clang3.3/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=freebsd10-clang3.3/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=freebsd10-clang3.3/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=freebsd10-clang3.3/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=freebsd10-clang3.3/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td rowspan="2">Ubuntu Linux 12.04 LTS</td><td>clang 3.3</td><td>libstdc++ 4.8</td><td>x86</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=linux-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=linux-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=linux-gcc-clang/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=linux-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=linux-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=linux-gcc-clang/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td>clang 3.4</td><td>libstdc++ 4.8</td><td>x86</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=static,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=static,label=linux-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=shared,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=shared,label=linux-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=standalone,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=standalone,label=linux-gcc-clang/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=static,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=static,label=linux-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=shared,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=shared,label=linux-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=standalone,label=linux-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=standalone,label=linux-gcc-clang/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td rowspan="8">Ubuntu Linux 14.04 LTS</td><td>clang 3.5</td><td>libstdc++ 4.9</td><td>x64</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td>clang 3.5</td><td>libstdc++ 4.9</td><td>ARMv7</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td>clang 3.6</td><td>libstdc++ 4.9</td><td>x64</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td>clang 3.7</td><td>libstdc++ 4.9</td><td>x64</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td>GCC 4.8</td><td>libstdc++ 4.8</td><td>x64</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td>GCC 4.9</td><td>libstdc++ 4.9</td><td>x64</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td>GCC 4.9</td><td>libstdc++ 4.9</td><td>ARMv7</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td>GCC 5.1</td><td>libstdc++ 5.1</td><td>x64</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td rowspan="3">Microsoft Windows 8.1</td><td>Mingw-w64 GCC 4.8</td><td>libstdc++ 4.8</td><td>x64</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=mingw64,LINKTYPE=static,label=win8-msvc-mingw/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=mingw64,LINKTYPE=static,label=win8-msvc-mingw/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=mingw64,LINKTYPE=shared,label=win8-msvc-mingw/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=mingw64,LINKTYPE=shared,label=win8-msvc-mingw/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=mingw64,LINKTYPE=static,label=win8-msvc-mingw/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=mingw64,LINKTYPE=static,label=win8-msvc-mingw/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=mingw64,LINKTYPE=shared,label=win8-msvc-mingw/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=mingw64,LINKTYPE=shared,label=win8-msvc-mingw/badge/icon" /></a></div>
+</td></tr>
+<tr align="center"><td>Visual Studio 2015</td><td>Dinkumware</td><td>x64</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=static,label=win8-msvc-mingw/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=static,label=win8-msvc-mingw/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=win8-msvc-mingw/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=win8-msvc-mingw/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=standalone,label=win8-msvc-mingw/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=standalone,label=win8-msvc-mingw/badge/icon" /></a></div>
+</td><td>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=static,label=win8-msvc-mingw/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=static,label=win8-msvc-mingw/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=win8-msvc-mingw/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=win8-msvc-mingw/badge/icon" /></a></div>
+ <div><a href="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=standalone,label=win8-msvc-mingw/"><img src="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=standalone,label=win8-msvc-mingw/badge/icon" /></a></div>
+</td></tr>
+</table>
+
+</center>
+
diff --git a/attic/appveyor.yml b/attic/appveyor.yml
new file mode 100644
index 00000000..76e17988
--- /dev/null
+++ b/attic/appveyor.yml
@@ -0,0 +1,41 @@
+# version format
+version: 1.40.{build}-{branch}
+
+# branches to build
+branches:
+ # blacklist
+ except:
+ - gh-pages
+skip_tags: true
+
+# Operating system (build VM template)
+os: Visual Studio 2015
+init:
+ - git config --global core.longpaths true
+
+clone_folder: c:\boost.afio
+platform: x64
+configuration: Release
+
+before_build:
+ - git submodule update --init --recursive
+ - git submodule foreach git checkout master
+ - git submodule foreach git pull origin master
+ - cd test
+ - call test_file_glob.bat
+ - cd ..
+ - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64
+build_script:
+ - standalone_alltests_msvc.bat
+after_build:
+
+before_test:
+test_script:
+# - test_all.exe --reporter junit --out results_all.xml
+ - test_all.exe
+after_test:
+# - type results_all.xml
+
+on_success:
+on_failure:
+on_finish:
diff --git a/attic/build/Jamfile.v2 b/attic/build/Jamfile.v2
new file mode 100644
index 00000000..ae23817a
--- /dev/null
+++ b/attic/build/Jamfile.v2
@@ -0,0 +1,62 @@
+# Boost AFIO Library Jamfile
+#
+# Copyright (c) Paul Kirth 2013.
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+import modules ;
+import ../../config/checks/config : requires ;
+.argv = [ modules.peek : ARGV ] ;
+
+project boost/afio
+ : requirements
+ [ requires cxx11_variadic_templates cxx11_template_aliases cxx11_noexcept cxx11_constexpr ]
+ <threading>multi
+ <link>shared:<define>BOOST_AFIO_DYN_LINK=1
+ <link>shared:<define>BOOST_AFIO_SOURCE=1
+ <link>static:<define>BOOST_AFIO_SOURCE=1
+ <include>../../../boost/afio
+ <toolset>gcc:<cxxflags>"-fvisibility-inlines-hidden -fstrict-aliasing -Wstrict-aliasing -Wno-unused -fargument-noalias -fvisibility=hidden -fasynchronous-unwind-tables"
+ <toolset>gcc-mingw:<cxxflags>"-DWIN32 -D_UNICODE -DUNICODE -Wno-missing-braces"
+ <toolset>gcc-mingw:<linkflags>"-lws2_32"
+ <toolset>clang:<cxxflags>"-fvisibility-inlines-hidden -fstrict-aliasing -Wstrict-aliasing -Wno-unused -Wno-mismatched-tags -Wno-unknown-pragmas -fvisibility=hidden -fasynchronous-unwind-tables"
+ <toolset>msvc:<cxxflags>"/GF /Gy"
+ <toolset>msvc:<linkflags>"/LARGEADDRESSAWARE /DYNAMICBASE /NXCOMPAT" # /VERSION:1.00.0"
+ <toolset>msvc:<define>WIN32
+ <toolset>msvc:<define>_WINDOWS
+ <toolset>msvc:<define>UNICODE
+ <toolset>msvc:<define>_UNICODE
+ <target-os>linux:<linkflags>"-lpthread -ldl"
+ <target-os>freebsd:<linkflags>"-lpthread -lexecinfo"
+ <link>shared:<define>BOOST_AFIO_DYN_LINK=1
+ <link>shared:<library>../../system/build//boost_system
+ <link>shared:<library>../../filesystem/build//boost_filesystem
+ <link>shared:<library>../../atomic/build//boost_atomic
+ <link>shared:<library>../../thread/build//boost_thread
+
+ : usage-requirements
+ <include>.
+
+ : source-location ../src
+;
+
+lib boost_afio
+ : afio.cpp
+;
+
+# Have AFIO bundle in an exported copy of ASIO. Saves compiling
+# ASIO later which in turn speeds up compiling AFIO users.
+lib boost_afio_dyn_asio
+ : afio.cpp
+ : <define>BOOST_ASIO_SEPARATE_COMPILATION
+ <define>BOOST_ASIO_DYN_LINK
+ <define>BOOST_AFIO_ENABLE_BENCHMARKING_COMPLETION
+ <toolset>gcc:<cxxflags>"-fvisibility=default" # Work around bug in ASIO where he isn't exporting all his symbols correctly
+;
+explicit boost_afio_dyn_asio ;
+
+boost-install boost_afio ;
+
+
diff --git a/attic/build/afio.vcxproj b/attic/build/afio.vcxproj
new file mode 100644
index 00000000..55cc52a6
--- /dev/null
+++ b/attic/build/afio.vcxproj
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\detail\SpookyV2.cpp" />
+ <ClCompile Include="..\src\afio.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\..\boost.afio\boost\afio\detail\impl\nt_kernel_stuff.hpp" />
+ <ClInclude Include="..\..\..\boost\afio\afio.hpp" />
+ <ClInclude Include="..\..\..\boost\afio\config.hpp" />
+ <ClInclude Include="..\..\..\boost\afio\detail\Aligned_Allocator.hpp" />
+ <ClInclude Include="..\..\..\boost\afio\detail\ErrorHandling.hpp" />
+ <ClInclude Include="..\..\..\boost\afio\detail\Preprocessor_variadic.hpp" />
+ <ClInclude Include="..\..\..\boost\afio\detail\SpookyV2.h" />
+ <ClInclude Include="..\..\..\boost\afio\detail\std_filesystem.hpp" />
+ <ClInclude Include="..\..\..\boost\afio\detail\Undoer.hpp" />
+ <ClInclude Include="..\..\..\boost\afio\detail\Utility.hpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="..\..\..\..\boost.afio\boost\afio\detail\impl\afio.ipp" />
+ <None Include="..\..\..\..\boost.afio\boost\afio\detail\impl\afio_iocp.ipp" />
+ <None Include="..\..\..\..\boost.afio\boost\afio\detail\impl\ErrorHandling.ipp" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{31A5E53D-7416-4E1D-891A-1C69EF8553BD}</ProjectGuid>
+ <Keyword>MakeFileProj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <NMakeOutput>afio.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>..\..\..\b2 link=static ../test --link-test -j 8 pch=off --test=../example/read_write_example.cpp</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>..\..\..\b2 -a</NMakeReBuildCommandLine>
+ <IncludePath>C:\Users\Project Kyo\Documents\GitHub\boost;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <NMakeOutput>afio.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>..\..\..\b2 address-model=64 link=static ../test --link-test -j 8</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>..\..\..\b2 address-model=64 -a</NMakeReBuildCommandLine>
+ <IncludePath>$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);..\..\..</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <NMakeOutput>afio.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;NDEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>..\..\..\b2 release</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>..\..\..\b2 release -a</NMakeReBuildCommandLine>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <NMakeOutput>afio.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;NDEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>..\..\..\b2 address-model=64 release</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>..\..\..\b2 address-model=64 release -a</NMakeReBuildCommandLine>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/attic/build/afio_standalone.vcxproj b/attic/build/afio_standalone.vcxproj
new file mode 100644
index 00000000..8d3b6ee2
--- /dev/null
+++ b/attic/build/afio_standalone.vcxproj
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{CC50D481-CD9C-4406-82C9-221A3A7EEED1}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>afio_standalone</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);AFIO_STANDALONE=1;SPINLOCK_STANDALONE=1;ASIO_STANDALONE=1;BOOST_TEST_DYN_LINK=1</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ <AdditionalIncludeDirectories>..\include;..\test;..\asio\asio\include </AdditionalIncludeDirectories>
+ <DisableSpecificWarnings>4456;4458;4503;4100;4457;4127;4706</DisableSpecificWarnings>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level3</WarningLevel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <SDLCheck>true</SDLCheck>
+ </ClCompile>
+ <Link>
+ <SubSystem>Console</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\detail\SpookyV2.cpp" />
+ <ClCompile Include="..\test\test_all.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\include\boost\afio\v2\afio.hpp" />
+ <ClInclude Include="..\include\boost\afio\v2\config.hpp" />
+ <ClInclude Include="..\include\boost\afio\v2\detail\ErrorHandling.hpp" />
+ <ClInclude Include="..\include\boost\afio\v2\detail\impl\nt_kernel_stuff.hpp" />
+ <ClInclude Include="..\include\boost\afio\v2\detail\Undoer.hpp" />
+ <ClInclude Include="..\include\boost\afio\v2\detail\Utility.hpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="..\include\boost\afio\v2\detail\impl\afio.ipp" />
+ <None Include="..\include\boost\afio\v2\detail\impl\afio_iocp.ipp" />
+ <None Include="..\include\boost\afio\v2\detail\impl\ErrorHandling.ipp" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/attic/clang-reformat.sh b/attic/clang-reformat.sh
new file mode 100755
index 00000000..560ad442
--- /dev/null
+++ b/attic/clang-reformat.sh
@@ -0,0 +1,17 @@
+#!/bin/bash -x
+find example -name "*.cpp" -exec clang-format-3.6 -i {} ';'
+clang-format-3.6 -i include/boost/afio/afio.hpp
+#clang-format-3.6 -i include/boost/afio/config.hpp
+find include/boost/afio/detail -name "*.hpp" -exec clang-format-3.6 -i {} ';'
+find include/boost/afio/detail -name "*.ipp" -exec clang-format-3.6 -i {} ';'
+find test -name "*.hpp" -exec clang-format-3.6 -i {} ';'
+find test -name "*.cpp" -exec clang-format-3.6 -i {} ';'
+
+PWD=$(pwd)
+find example -name "*.cpp" -exec "$PWD/include/boost/afio/bindlib/scripts/IndentCmacros.py" {} ';'
+include/boost/afio/bindlib/scripts/IndentCmacros.py include/boost/afio/afio.hpp
+include/boost/afio/bindlib/scripts/IndentCmacros.py include/boost/afio/config.hpp
+find include/boost/afio/detail -name "*.hpp" -exec "$PWD/include/boost/afio/bindlib/scripts/IndentCmacros.py" {} ';'
+find include/boost/afio/detail -name "*.ipp" -exec "$PWD/include/boost/afio/bindlib/scripts/IndentCmacros.py" {} ';'
+find test -name "*.hpp" -exec "$PWD/include/boost/afio/bindlib/scripts/IndentCmacros.py" {} ';'
+find test -name "*.cpp" -exec "$PWD/include/boost/afio/bindlib/scripts/IndentCmacros.py" {} ';'
diff --git a/attic/detail/SpookyV2.cpp b/attic/detail/SpookyV2.cpp
new file mode 100644
index 00000000..76d745dd
--- /dev/null
+++ b/attic/detail/SpookyV2.cpp
@@ -0,0 +1,350 @@
+// Spooky Hash
+// A 128-bit noncryptographic hash, for checksums and table lookup
+// By Bob Jenkins. Public domain.
+// Oct 31 2010: published framework, disclaimer ShortHash isn't right
+// Nov 7 2010: disabled ShortHash
+// Oct 31 2011: replace End, ShortMix, ShortEnd, enable ShortHash again
+// April 10 2012: buffer overflow on platforms without unaligned reads
+// July 12 2012: was passing out variables in final to in/out in short
+// July 30 2012: I reintroduced the buffer overflow
+// August 5 2012: SpookyV2: d = should be d += in short hash, and remove extra mix from long hash
+
+#include <memory.h>
+#include "SpookyV2.h"
+
+#define ALLOW_UNALIGNED_READS 0
+
+//
+// short hash ... it could be used on any message,
+// but it's used by Spooky just for short messages.
+//
+void SpookyHash::Short(
+ const void *message,
+ size_t length,
+ uint64 *hash1,
+ uint64 *hash2)
+{
+ uint64 buf[2*sc_numVars];
+ union
+ {
+ const uint8 *p8;
+ uint32 *p32;
+ uint64 *p64;
+ size_t i;
+ } u;
+
+ u.p8 = (const uint8 *)message;
+
+ if (!ALLOW_UNALIGNED_READS && (u.i & 0x7))
+ {
+ memcpy(buf, message, length);
+ u.p64 = buf;
+ }
+
+ size_t remainder = length%32;
+ uint64 a=*hash1;
+ uint64 b=*hash2;
+ uint64 c=sc_const;
+ uint64 d=sc_const;
+
+ if (length > 15)
+ {
+ const uint64 *end = u.p64 + (length/32)*4;
+
+ // handle all complete sets of 32 bytes
+ for (; u.p64 < end; u.p64 += 4)
+ {
+ c += u.p64[0];
+ d += u.p64[1];
+ ShortMix(a,b,c,d);
+ a += u.p64[2];
+ b += u.p64[3];
+ }
+
+ //Handle the case of 16+ remaining bytes.
+ if (remainder >= 16)
+ {
+ c += u.p64[0];
+ d += u.p64[1];
+ ShortMix(a,b,c,d);
+ u.p64 += 2;
+ remainder -= 16;
+ }
+ }
+
+ // Handle the last 0..15 bytes, and its length
+ d += ((uint64)length) << 56;
+ switch (remainder)
+ {
+ case 15:
+ d += ((uint64)u.p8[14]) << 48;
+ case 14:
+ d += ((uint64)u.p8[13]) << 40;
+ case 13:
+ d += ((uint64)u.p8[12]) << 32;
+ case 12:
+ d += u.p32[2];
+ c += u.p64[0];
+ break;
+ case 11:
+ d += ((uint64)u.p8[10]) << 16;
+ case 10:
+ d += ((uint64)u.p8[9]) << 8;
+ case 9:
+ d += (uint64)u.p8[8];
+ case 8:
+ c += u.p64[0];
+ break;
+ case 7:
+ c += ((uint64)u.p8[6]) << 48;
+ case 6:
+ c += ((uint64)u.p8[5]) << 40;
+ case 5:
+ c += ((uint64)u.p8[4]) << 32;
+ case 4:
+ c += u.p32[0];
+ break;
+ case 3:
+ c += ((uint64)u.p8[2]) << 16;
+ case 2:
+ c += ((uint64)u.p8[1]) << 8;
+ case 1:
+ c += (uint64)u.p8[0];
+ break;
+ case 0:
+ c += sc_const;
+ d += sc_const;
+ }
+ ShortEnd(a,b,c,d);
+ *hash1 = a;
+ *hash2 = b;
+}
+
+
+
+
+// do the whole hash in one call
+void SpookyHash::Hash128(
+ const void *message,
+ size_t length,
+ uint64 *hash1,
+ uint64 *hash2)
+{
+ if (length < sc_bufSize)
+ {
+ Short(message, length, hash1, hash2);
+ return;
+ }
+
+ uint64 h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
+ uint64 buf[sc_numVars];
+ uint64 *end;
+ union
+ {
+ const uint8 *p8;
+ uint64 *p64;
+ size_t i;
+ } u;
+ size_t remainder;
+
+ h0=h3=h6=h9 = *hash1;
+ h1=h4=h7=h10 = *hash2;
+ h2=h5=h8=h11 = sc_const;
+
+ u.p8 = (const uint8 *)message;
+ end = u.p64 + (length/sc_blockSize)*sc_numVars;
+
+ // handle all whole sc_blockSize blocks of bytes
+ if (ALLOW_UNALIGNED_READS || ((u.i & 0x7) == 0))
+ {
+ while (u.p64 < end)
+ {
+ Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ u.p64 += sc_numVars;
+ }
+ }
+ else
+ {
+ while (u.p64 < end)
+ {
+ memcpy(buf, u.p64, sc_blockSize);
+ Mix(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ u.p64 += sc_numVars;
+ }
+ }
+
+ // handle the last partial block of sc_blockSize bytes
+ remainder = (length - ((const uint8 *)end-(const uint8 *)message));
+ memcpy(buf, end, remainder);
+ memset(((uint8 *)buf)+remainder, 0, sc_blockSize-remainder);
+ // ned March 2013: Don't mix in length related anything ((uint8 *)buf)[sc_blockSize-1] = remainder;
+
+ // do some final mixing
+ End(buf, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ *hash1 = h0;
+ *hash2 = h1;
+}
+
+
+
+// init spooky state
+void SpookyHash::Init(uint64 seed1, uint64 seed2)
+{
+ m_length = 0;
+ m_remainder = 0;
+ m_state[0] = seed1;
+ m_state[1] = seed2;
+}
+
+
+// add a message fragment to the state
+void SpookyHash::Update(const void *message, size_t length)
+{
+ uint64 h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11;
+ size_t newLength = length + m_remainder;
+ uint8 remainder;
+ union
+ {
+ const uint8 *p8;
+ uint64 *p64;
+ size_t i;
+ } u;
+ const uint64 *end;
+
+ // Is this message fragment too short? If it is, stuff it away.
+ if (newLength < sc_bufSize)
+ {
+ memcpy(&((uint8 *)m_data)[m_remainder], message, length);
+ m_length = length + m_length;
+ m_remainder = (uint8)newLength;
+ return;
+ }
+
+ // init the variables
+ if (m_length < sc_bufSize)
+ {
+ h0=h3=h6=h9 = m_state[0];
+ h1=h4=h7=h10 = m_state[1];
+ h2=h5=h8=h11 = sc_const;
+ }
+ else
+ {
+ h0 = m_state[0];
+ h1 = m_state[1];
+ h2 = m_state[2];
+ h3 = m_state[3];
+ h4 = m_state[4];
+ h5 = m_state[5];
+ h6 = m_state[6];
+ h7 = m_state[7];
+ h8 = m_state[8];
+ h9 = m_state[9];
+ h10 = m_state[10];
+ h11 = m_state[11];
+ }
+ m_length = length + m_length;
+
+ // if we've got anything stuffed away, use it now
+ if (m_remainder)
+ {
+ uint8 prefix = sc_bufSize-m_remainder;
+ memcpy(&(((uint8 *)m_data)[m_remainder]), message, prefix);
+ u.p64 = m_data;
+ Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ Mix(&u.p64[sc_numVars], h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ u.p8 = ((const uint8 *)message) + prefix;
+ length -= prefix;
+ }
+ else
+ {
+ u.p8 = (const uint8 *)message;
+ }
+
+ // handle all whole blocks of sc_blockSize bytes
+ end = u.p64 + (length/sc_blockSize)*sc_numVars;
+ remainder = (uint8)(length-((const uint8 *)end-u.p8));
+ if (ALLOW_UNALIGNED_READS || (u.i & 0x7) == 0)
+ {
+ while (u.p64 < end)
+ {
+ Mix(u.p64, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ u.p64 += sc_numVars;
+ }
+ }
+ else
+ {
+ while (u.p64 < end)
+ {
+ memcpy(m_data, u.p8, sc_blockSize);
+ Mix(m_data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ u.p64 += sc_numVars;
+ }
+ }
+
+ // stuff away the last few bytes
+ m_remainder = remainder;
+ memcpy(m_data, end, remainder);
+
+ // stuff away the variables
+ m_state[0] = h0;
+ m_state[1] = h1;
+ m_state[2] = h2;
+ m_state[3] = h3;
+ m_state[4] = h4;
+ m_state[5] = h5;
+ m_state[6] = h6;
+ m_state[7] = h7;
+ m_state[8] = h8;
+ m_state[9] = h9;
+ m_state[10] = h10;
+ m_state[11] = h11;
+}
+
+
+// report the hash for the concatenation of all message fragments so far
+void SpookyHash::Final(uint64 *hash1, uint64 *hash2)
+{
+ // init the variables
+ if (m_length < sc_bufSize)
+ {
+ *hash1 = m_state[0];
+ *hash2 = m_state[1];
+ Short( m_data, m_length, hash1, hash2);
+ return;
+ }
+
+ const uint64 *data = (const uint64 *)m_data;
+ uint8 remainder = m_remainder;
+
+ uint64 h0 = m_state[0];
+ uint64 h1 = m_state[1];
+ uint64 h2 = m_state[2];
+ uint64 h3 = m_state[3];
+ uint64 h4 = m_state[4];
+ uint64 h5 = m_state[5];
+ uint64 h6 = m_state[6];
+ uint64 h7 = m_state[7];
+ uint64 h8 = m_state[8];
+ uint64 h9 = m_state[9];
+ uint64 h10 = m_state[10];
+ uint64 h11 = m_state[11];
+
+ if (remainder >= sc_blockSize)
+ {
+ // m_data can contain two blocks; handle any whole first block
+ Mix(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ data += sc_numVars;
+ remainder -= sc_blockSize;
+ }
+
+ // mix in the last partial block, and the length mod sc_blockSize
+ memset(&((uint8 *)data)[remainder], 0, (sc_blockSize-remainder));
+
+ ((uint8 *)data)[sc_blockSize-1] = remainder;
+
+ // do some final mixing
+ End(data, h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+
+ *hash1 = h0;
+ *hash2 = h1;
+}
diff --git a/attic/detail/SpookyV2.h b/attic/detail/SpookyV2.h
new file mode 100644
index 00000000..41d312f8
--- /dev/null
+++ b/attic/detail/SpookyV2.h
@@ -0,0 +1,302 @@
+#ifndef SPOOKY_HASH_H
+#define SPOOKY_HASH_H
+
+//
+// SpookyHash: a 128-bit noncryptographic hash function
+// By Bob Jenkins, public domain
+// Oct 31 2010: alpha, framework + SpookyHash::Mix appears right
+// Oct 31 2011: alpha again, Mix only good to 2^^69 but rest appears right
+// Dec 31 2011: beta, improved Mix, tested it for 2-bit deltas
+// Feb 2 2012: production, same bits as beta
+// Feb 5 2012: adjusted definitions of uint* to be more portable
+// Mar 30 2012: 3 bytes/cycle, not 4. Alpha was 4 but wasn't thorough enough.
+// August 5 2012: SpookyV2 (different results)
+//
+// Up to 3 bytes/cycle for long messages. Reasonably fast for short messages.
+// All 1 or 2 bit deltas achieve avalanche within 1% bias per output bit.
+//
+// This was developed for and tested on 64-bit x86-compatible processors.
+// It assumes the processor is little-endian. There is a macro
+// controlling whether unaligned reads are allowed (by default they are).
+// This should be an equally good hash on big-endian machines, but it will
+// compute different results on them than on little-endian machines.
+//
+// Google's CityHash has similar specs to SpookyHash, and CityHash is faster
+// on new Intel boxes. MD4 and MD5 also have similar specs, but they are orders
+// of magnitude slower. CRCs are two or more times slower, but unlike
+// SpookyHash, they have nice math for combining the CRCs of pieces to form
+// the CRCs of wholes. There are also cryptographic hashes, but those are even
+// slower than MD5.
+//
+
+#include <stddef.h>
+
+#ifdef _MSC_VER
+# define INLINE __forceinline
+ typedef unsigned __int64 uint64;
+ typedef unsigned __int32 uint32;
+ typedef unsigned __int16 uint16;
+ typedef unsigned __int8 uint8;
+#else
+# include <stdint.h>
+# define INLINE inline
+ typedef uint64_t uint64;
+ typedef uint32_t uint32;
+ typedef uint16_t uint16;
+ typedef uint8_t uint8;
+#endif
+
+
+class SpookyHash
+{
+public:
+ //
+ // SpookyHash: hash a single message in one call, produce 128-bit output
+ //
+ static void Hash128(
+ const void *message, // message to hash
+ size_t length, // length of message in bytes
+ uint64 *hash1, // in/out: in seed 1, out hash value 1
+ uint64 *hash2); // in/out: in seed 2, out hash value 2
+
+ //
+ // Hash64: hash a single message in one call, return 64-bit output
+ //
+ static uint64 Hash64(
+ const void *message, // message to hash
+ size_t length, // length of message in bytes
+ uint64 seed) // seed
+ {
+ uint64 hash1 = seed;
+ Hash128(message, length, &hash1, &seed);
+ return hash1;
+ }
+
+ //
+ // Hash32: hash a single message in one call, produce 32-bit output
+ //
+ static uint32 Hash32(
+ const void *message, // message to hash
+ size_t length, // length of message in bytes
+ uint32 seed) // seed
+ {
+ uint64 hash1 = seed, hash2 = seed;
+ Hash128(message, length, &hash1, &hash2);
+ return (uint32)hash1;
+ }
+
+ //
+ // Init: initialize the context of a SpookyHash
+ //
+ void Init(
+ uint64 seed1, // any 64-bit value will do, including 0
+ uint64 seed2); // different seeds produce independent hashes
+
+ //
+ // Update: add a piece of a message to a SpookyHash state
+ //
+ void Update(
+ const void *message, // message fragment
+ size_t length); // length of message fragment in bytes
+
+
+ //
+ // Final: compute the hash for the current SpookyHash state
+ //
+ // This does not modify the state; you can keep updating it afterward
+ //
+ // The result is the same as if SpookyHash() had been called with
+ // all the pieces concatenated into one message.
+ //
+ void Final(
+ uint64 *hash1, // out only: first 64 bits of hash value.
+ uint64 *hash2); // out only: second 64 bits of hash value.
+
+ //
+ // left rotate a 64-bit value by k bytes
+ //
+ static INLINE uint64 Rot64(uint64 x, int k)
+ {
+ return (x << k) | (x >> (64 - k));
+ }
+
+ //
+ // This is used if the input is 96 bytes long or longer.
+ //
+ // The internal state is fully overwritten every 96 bytes.
+ // Every input bit appears to cause at least 128 bits of entropy
+ // before 96 other bytes are combined, when run forward or backward
+ // For every input bit,
+ // Two inputs differing in just that input bit
+ // Where "differ" means xor or subtraction
+ // And the base value is random
+ // When run forward or backwards one Mix
+ // I tried 3 pairs of each; they all differed by at least 212 bits.
+ //
+ static INLINE void Mix(
+ const uint64 *data,
+ uint64 &s0, uint64 &s1, uint64 &s2, uint64 &s3,
+ uint64 &s4, uint64 &s5, uint64 &s6, uint64 &s7,
+ uint64 &s8, uint64 &s9, uint64 &s10,uint64 &s11)
+ {
+ s0 += data[0]; s2 ^= s10; s11 ^= s0; s0 = Rot64(s0,11); s11 += s1;
+ s1 += data[1]; s3 ^= s11; s0 ^= s1; s1 = Rot64(s1,32); s0 += s2;
+ s2 += data[2]; s4 ^= s0; s1 ^= s2; s2 = Rot64(s2,43); s1 += s3;
+ s3 += data[3]; s5 ^= s1; s2 ^= s3; s3 = Rot64(s3,31); s2 += s4;
+ s4 += data[4]; s6 ^= s2; s3 ^= s4; s4 = Rot64(s4,17); s3 += s5;
+ s5 += data[5]; s7 ^= s3; s4 ^= s5; s5 = Rot64(s5,28); s4 += s6;
+ s6 += data[6]; s8 ^= s4; s5 ^= s6; s6 = Rot64(s6,39); s5 += s7;
+ s7 += data[7]; s9 ^= s5; s6 ^= s7; s7 = Rot64(s7,57); s6 += s8;
+ s8 += data[8]; s10 ^= s6; s7 ^= s8; s8 = Rot64(s8,55); s7 += s9;
+ s9 += data[9]; s11 ^= s7; s8 ^= s9; s9 = Rot64(s9,54); s8 += s10;
+ s10 += data[10]; s0 ^= s8; s9 ^= s10; s10 = Rot64(s10,22); s9 += s11;
+ s11 += data[11]; s1 ^= s9; s10 ^= s11; s11 = Rot64(s11,46); s10 += s0;
+ }
+
+ //
+ // Mix all 12 inputs together so that h0, h1 are a hash of them all.
+ //
+ // For two inputs differing in just the input bits
+ // Where "differ" means xor or subtraction
+ // And the base value is random, or a counting value starting at that bit
+ // The final result will have each bit of h0, h1 flip
+ // For every input bit,
+ // with probability 50 +- .3%
+ // For every pair of input bits,
+ // with probability 50 +- 3%
+ //
+ // This does not rely on the last Mix() call having already mixed some.
+ // Two iterations was almost good enough for a 64-bit result, but a
+ // 128-bit result is reported, so End() does three iterations.
+ //
+ static INLINE void EndPartial(
+ uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3,
+ uint64 &h4, uint64 &h5, uint64 &h6, uint64 &h7,
+ uint64 &h8, uint64 &h9, uint64 &h10,uint64 &h11)
+ {
+ h11+= h1; h2 ^= h11; h1 = Rot64(h1,44);
+ h0 += h2; h3 ^= h0; h2 = Rot64(h2,15);
+ h1 += h3; h4 ^= h1; h3 = Rot64(h3,34);
+ h2 += h4; h5 ^= h2; h4 = Rot64(h4,21);
+ h3 += h5; h6 ^= h3; h5 = Rot64(h5,38);
+ h4 += h6; h7 ^= h4; h6 = Rot64(h6,33);
+ h5 += h7; h8 ^= h5; h7 = Rot64(h7,10);
+ h6 += h8; h9 ^= h6; h8 = Rot64(h8,13);
+ h7 += h9; h10^= h7; h9 = Rot64(h9,38);
+ h8 += h10; h11^= h8; h10= Rot64(h10,53);
+ h9 += h11; h0 ^= h9; h11= Rot64(h11,42);
+ h10+= h0; h1 ^= h10; h0 = Rot64(h0,54);
+ }
+
+ static INLINE void End(
+ const uint64 *data,
+ uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3,
+ uint64 &h4, uint64 &h5, uint64 &h6, uint64 &h7,
+ uint64 &h8, uint64 &h9, uint64 &h10,uint64 &h11)
+ {
+ h0 += data[0]; h1 += data[1]; h2 += data[2]; h3 += data[3];
+ h4 += data[4]; h5 += data[5]; h6 += data[6]; h7 += data[7];
+ h8 += data[8]; h9 += data[9]; h10 += data[10]; h11 += data[11];
+ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ EndPartial(h0,h1,h2,h3,h4,h5,h6,h7,h8,h9,h10,h11);
+ }
+
+ //
+ // The goal is for each bit of the input to expand into 128 bits of
+ // apparent entropy before it is fully overwritten.
+ // n trials both set and cleared at least m bits of h0 h1 h2 h3
+ // n: 2 m: 29
+ // n: 3 m: 46
+ // n: 4 m: 57
+ // n: 5 m: 107
+ // n: 6 m: 146
+ // n: 7 m: 152
+ // when run forwards or backwards
+ // for all 1-bit and 2-bit diffs
+ // with diffs defined by either xor or subtraction
+ // with a base of all zeros plus a counter, or plus another bit, or random
+ //
+ static INLINE void ShortMix(uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3)
+ {
+ h2 = Rot64(h2,50); h2 += h3; h0 ^= h2;
+ h3 = Rot64(h3,52); h3 += h0; h1 ^= h3;
+ h0 = Rot64(h0,30); h0 += h1; h2 ^= h0;
+ h1 = Rot64(h1,41); h1 += h2; h3 ^= h1;
+ h2 = Rot64(h2,54); h2 += h3; h0 ^= h2;
+ h3 = Rot64(h3,48); h3 += h0; h1 ^= h3;
+ h0 = Rot64(h0,38); h0 += h1; h2 ^= h0;
+ h1 = Rot64(h1,37); h1 += h2; h3 ^= h1;
+ h2 = Rot64(h2,62); h2 += h3; h0 ^= h2;
+ h3 = Rot64(h3,34); h3 += h0; h1 ^= h3;
+ h0 = Rot64(h0,5); h0 += h1; h2 ^= h0;
+ h1 = Rot64(h1,36); h1 += h2; h3 ^= h1;
+ }
+
+ //
+ // Mix all 4 inputs together so that h0, h1 are a hash of them all.
+ //
+ // For two inputs differing in just the input bits
+ // Where "differ" means xor or subtraction
+ // And the base value is random, or a counting value starting at that bit
+ // The final result will have each bit of h0, h1 flip
+ // For every input bit,
+ // with probability 50 +- .3% (it is probably better than that)
+ // For every pair of input bits,
+ // with probability 50 +- .75% (the worst case is approximately that)
+ //
+ static INLINE void ShortEnd(uint64 &h0, uint64 &h1, uint64 &h2, uint64 &h3)
+ {
+ h3 ^= h2; h2 = Rot64(h2,15); h3 += h2;
+ h0 ^= h3; h3 = Rot64(h3,52); h0 += h3;
+ h1 ^= h0; h0 = Rot64(h0,26); h1 += h0;
+ h2 ^= h1; h1 = Rot64(h1,51); h2 += h1;
+ h3 ^= h2; h2 = Rot64(h2,28); h3 += h2;
+ h0 ^= h3; h3 = Rot64(h3,9); h0 += h3;
+ h1 ^= h0; h0 = Rot64(h0,47); h1 += h0;
+ h2 ^= h1; h1 = Rot64(h1,54); h2 += h1;
+ h3 ^= h2; h2 = Rot64(h2,32); h3 += h2;
+ h0 ^= h3; h3 = Rot64(h3,25); h0 += h3;
+ h1 ^= h0; h0 = Rot64(h0,63); h1 += h0;
+ }
+
+private:
+
+ //
+ // Short is used for messages under 192 bytes in length
+ // Short has a low startup cost, the normal mode is good for long
+ // keys, the cost crossover is at about 192 bytes. The two modes were
+ // held to the same quality bar.
+ //
+ static void Short(
+ const void *message, // message (array of bytes, not necessarily aligned)
+ size_t length, // length of message (in bytes)
+ uint64 *hash1, // in/out: in the seed, out the hash value
+ uint64 *hash2); // in/out: in the seed, out the hash value
+
+ // number of uint64's in internal state
+ static const size_t sc_numVars = 12;
+
+ // size of the internal state
+ static const size_t sc_blockSize = sc_numVars*8;
+
+ // size of buffer of unhashed data, in bytes
+ static const size_t sc_bufSize = 2*sc_blockSize;
+
+ //
+ // sc_const: a constant which:
+ // * is not zero
+ // * is odd
+ // * is a not-very-regular mix of 1's and 0's
+ // * does not need any other special mathematical properties
+ //
+ static const uint64 sc_const = 0xdeadbeefdeadbeefULL;
+
+ uint64 m_data[2*sc_numVars]; // unhashed data, for partial messages
+ uint64 m_state[sc_numVars]; // internal state of the hash
+ size_t m_length; // total length of the input so far
+ uint8 m_remainder; // length of unhashed data stashed in m_data
+};
+
+
+#endif
diff --git a/attic/doc/Jamfile.v2 b/attic/doc/Jamfile.v2
new file mode 100644
index 00000000..c5f25789
--- /dev/null
+++ b/attic/doc/Jamfile.v2
@@ -0,0 +1,74 @@
+# Boost.AFIO
+#
+# This documentation machinery is derived from Boost.Geometry
+
+# Use, modification and distribution is subject to the Boost Software License,
+# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+
+import os ;
+
+project afio/doc ;
+using quickbook ;
+
+path-constant here : . ;
+
+make disqus_comments.html : ./disqus_comments.html : @copy_disqus_identifiers ;
+if [ os.name ] = NT
+{
+ actions copy_disqus_identifiers
+ {
+ for %%a in ("$(>)") do set src_path=%%~dpa\disqus_identifiers
+ for %%a in ("$(<)") do set dest_path=%%~dpa\disqus_identifiers
+ robocopy /E %src_path% %dest_path%
+ copy /B $(>) $(<)
+ }
+}
+else
+{
+ actions copy_disqus_identifiers
+ {
+ cp -a `dirname $(>)`/disqus_identifiers `dirname $(<)`/disqus_identifiers
+ cp -a $(>) $(<)
+ }
+}
+
+boostbook afio
+ : afio.qbk
+ : <dependency>Jamfile.v2
+ <dependency>generated/class_dispatcher.qbk
+ <xsl:param>chunk.section.depth=4
+# <auto-index>off
+# <auto-index-internal>on
+# <auto-index-verbose>off
+# <xsl:param>index.on.type=1
+# <format>html
+ <xsl:param>chunk.first.sections=1
+ <xsl:param>toc.section.depth=4
+ <xsl:param>toc.max.depth=4
+ <xsl:param>generate.section.toc.level=4
+ <xsl:param>boost.root=../..
+ <format>onehtml:<xsl:param>img.src.path=../../../../libs/afio/doc/src/images/a/b/
+ <format>html:<xsl:param>img.src.path=../../../../libs/afio/doc/src/images/a/b/
+ <format>xhtml:<xsl:param>img.src.path=../../../../libs/afio/doc/src/images/a/b/
+ <format>pdf:<xsl:param>img.src.path=../../../../../../libs/afio/doc/src/images/
+ <xsl:param>root.filename=afio
+ <xsl:param>html.stylesheet=../../libs/afio/doc/html/myboostbook.css
+ <quickbook-define>enable_index
+ <include>$(here)
+ <format>pdf:<xsl:param>boost.url.prefix=https://ci.nedprod.com/
+ <xsl:param>fop1.extensions=0
+ #<format>pdf:<xsl:param>xep.extensions=1
+ # TOC generation: this is needed for FOP 0.2, but must not be set to zero for FOP-0.9!
+ <format>pdf:<xsl:param>fop.extensions=0
+ # No indent on body text:
+ <format>pdf:<xsl:param>body.start.indent=0pt
+ # Margin size:
+ <format>pdf:<xsl:param>page.margin.inner=0.5in
+ # Margin size:
+ <format>pdf:<xsl:param>page.margin.outer=0.5in
+;
+
+install pdfinstall : afio/<format>pdf : <location>. <name>afio.pdf <install-type>PDF ;
+explicit pdfinstall ;
diff --git a/attic/doc/Readme.txt b/attic/doc/Readme.txt
new file mode 100644
index 00000000..2a4c2ee1
--- /dev/null
+++ b/attic/doc/Readme.txt
@@ -0,0 +1,63 @@
+Instructions on how to build the AFIO docs
+==========================================
+Niall Douglas
+
+The Boost.AFIO docs are borrowed heavily from those of Boost.Geometry, so all
+credit should go there. The following might look Windows specific, but I had
+a terrible time matching up the right versions of binaries on POSIX from vendor
+provided package repositories, so it's probably actually easier to configure
+on Windows crazily enough.
+
+1. Install doxygen, java and python 2.7 such that it runs from PATH.
+
+2. Make a directory in a path containing no spaces somewhere to hold many tools.
+Let's say C:\BoostBook or /home/ned/BoostBook.
+
+3. Follow these instructions from http://www.boost.org/doc/libs/1_55_0/doc/html/quickbook/install.html.
+
+WINDOWS ONLY: Get the xsltproc, libxml2, libxslt and the Docbook XSL mentioned
+by the document above all into C:\BoostBook. The directory structure would look
+like this:
+
+C:\BoostBook\bin\xsltproc.exe
+...
+C:\BoostBook\xml\docbook-xsl
+...
+
+Note that the link on the above page to the Docbook XSL gives you the -ns version
+which won't work. You need the -1 version from
+http://sourceforge.net/projects/docbook/files/docbook-xsl/1.78.1/
+
+4. OPTIONAL: Compile Boost QuickBook and copy the output binary into BoostBook\bin.
+
+5. If on Boost prior to v1.54, apply the doxygen_xml2qbk.patch in this directory
+to Boost. This will add support for producing QuickBook documentation from public
+member variables. Compile libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk
+using something like:
+
+./b2 libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk
+
+... and copy the output binary into BoostBook\bin.
+
+6. I simply can't get any version past or present of Apache FOP to work with
+the present DocBook XSLTs, so go to http://www.renderx.com/download/personal.html,
+register for a personal edition of RenderX XEP, and follow the instructions from
+http://www.boost.org/doc/libs/1_54_0/doc/html/boostbook/getting/started.html
+to configure your user-config.jam with all the right paths but using XEP
+instead of FOP.
+
+7. Open a command box and cd to this directory (libs/afio/doc). Do:
+
+set DOXYGEN_XML2QBK="C:\BoostBook\bin\doxygen_xml2qbk.exe"
+python make_qbk.py
+
+8. Return to the boost top directory. Do:
+
+cd doc
+../b2 ../libs/afio/doc
+
+HTML documentation is output alongside other Boost documentation.
+
+9. For PDF output do
+
+../b2 ../libs/afio/doc pdf
diff --git a/attic/doc/acknowledgments.qbk b/attic/doc/acknowledgments.qbk
new file mode 100644
index 00000000..0d57f1d3
--- /dev/null
+++ b/attic/doc/acknowledgments.qbk
@@ -0,0 +1,65 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+[section Acknowledgments]
+
+We would like to thank all the people who contributed to the development of this library, many of whom
+are not listed here but nevertheless are due our thanks. Our especial thanks goes (in chronological
+order since 2012):
+
+* To my former team at BlackBerry (of whom Tony van Eerd is best known to the C++ community) for
+the off the cuff discussions which led to me to conceptualise AFIO as an out-of-work side project.
+
+* To Google, for sponsoring Paul to work on AFIO as part of Google Summer of Code 2013.
+Paul did the lion's share of the work of porting the preexisting pure C++ 11 code
+base to Boost and to older compilers, both tasks which could never be called fun by anyone.
+
+* To the family and friends who had to put up with us during Google Summer of Code 2013
+adding 15-25 hours per week above
+and beyond our normal work hours which meant time not spent with them. This part is
+without doubt the hardest part of developing a new Boost library __dash__ what it requires
+you to sacrifice in exchange for what little free time is already left over after
+a work week.
+
+* To my former team and division at BlackBerry who successfully obtained the first ever
+corporate permission for an employee to contribute to a non-business open source project.
+The many months of wrangling to achieve that win was made moot when I was downsized
+towards the end of GSoC just weeks after gaining the landmark approval from Legal, nevertheless my thanks are due to BlackBerry for trying to
+change itself and letting me be a part of that.
+
+* To Christopher M. Kohlhoff for providing Boost.ASIO, which has been a high benchmark
+to aspire towards.
+
+* To Beman Dawes for providing Boost.Filesystem and the Filesystem TS, without which filesystem
+paths would be far more tricky to handle well in C++.
+
+* To Artur Laksberg for reviewing AFIO early on and making a number of valuable observations.
+
+* To Bjorn Reese for taking the time to be an early adopter of AFIO, and making many
+observations of where AFIO could be obviously improved, almost all of which were our
+delight to enact.
+
+* To Vicente J. Botet Escriba for very kindly adding a method to Boost.Thread's future's
+for directly fetching their exception_ptr. This greatly improved AFIO's exception
+propagation performance.
+
+* To Megan and Clara for putting up with the nightly 12am to 4am weekday after-work slots
+during 2015 which were necessary to make any progress on AFIO and its sublibraries APIBind,
+Spinlock and Monad.
+
+* To the many people on the boost-dev mailing list who contributed feedback on the design
+of the sublibraries APIBind, Spinlock and Monad.
+
+* To Ahmed Charles for volunteering to review manage AFIO after two years of waiting in the
+Boost peer review queue - thank you Ahmed!
+
+* And finally to all those who have submitted bugs and pull requests for AFIO, too many to
+list here.
+
+[endsect]
+
diff --git a/attic/doc/advanced_topics.qbk b/attic/doc/advanced_topics.qbk
new file mode 100644
index 00000000..002551dc
--- /dev/null
+++ b/attic/doc/advanced_topics.qbk
@@ -0,0 +1,106 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+[section:advanced_topics Advanced Topics]
+
+[section:custom_completion_handler How to write your own custom AFIO completion handler]
+
+If you want to extend __boost_afio__ with additional asynchronous functionality,
+you're probably going to have to implement your own custom AFIO completion handler
+which can be scheduled using __afio_completion__.
+These are the second most lowest level, second most primitive completion handler AFIO has and while a
+non-trivial amount of programmer effort to utilise, they do offer a maximum amount
+of flexibility, functionality and of course, performance[footnote If you're willing to go even ['more] low level, AFIO's
+`async_file_io_dispatcher_base` class exposes as protected member functions the variadic
+templated function `chain_async_op()` and a suite of specialised `chain_async_ops()` helper
+overloads. Therefore if you subclass `async_file_io_dispatcher_base`, you can enjoy the overhead of
+just one `std::function<>` call.]. If you are NOT extending
+AFIO with extra functionality, you almost certainly want __afio_call__ instead.
+
+Some advantages of custom completion handlers include:
+
+* Access to op id and any precondition op which was supplied at the point of scheduling.
+* Ability to schedule immediate completions which are executed instantly after
+their precondition instead of later when the thread source gets round to them.
+This is particularly useful when your host OS has real async i/o support, because
+you can use immediate completions to schedule non-blocking async i/o operations
+as quickly as possible. Another use is taking advantage of Lx cache locality while
+the cache lines are still paged in.
+* Direct control of exception handling and error propagation, where arbitrary ops can be
+completed at any time by any thread in any circumstance.
+* Performance: completion handlers only have a single `std::function<>` invocation
+overhead, rather than more as would be the case with `call()`. The reason why nesting
+`std::function<>` invocations is bad is because each entails type erasure, which requires
+the CPU to do an indirect jump (like a virtual function call) and while modern branch
+predictors eliminate any penalty for executing just one of those, they most certainly
+struggle when they have to execute a sequence of indirect jumps. For example, a single
+indirect jump might cost just two CPU cycles, while a mispredicted indirect jump might
+cost as much as 18-25 CPU cycles.
+
+Some disadvantages of custom completion handlers include:
+
+* Unlike __afio_call__, there is no custom return type machinery i.e. you'll have to
+implement your own. This isn't hard __dash__ note how __afio_enumerate__'s internal completion
+handler in afio.cpp takes a shared pointer as a parameter containing the promises of output for
+the enumerations, thus allowing __afio_enumerate__ to set up a batch of futures
+corresponding to those promises and return those to the user.
+* Exception handling is rather more manual. That comes of course with flexibility as well as hassle.
+
+So, let's have a look at an example of a custom AFIO completion handler, this being
+pretty much the simplest formulation possible:
+
+[completion_example1]
+
+Note how you have fair amount of flexibility of defining any input parameters you like so long as
+the standard duo of id and precondition are there. This is enabled basically
+through `std::bind<>` to prebind any custom parameters you may have to produce a `std::function<>`
+instance with the aforementioned standard duo of parameters. As AFIO's completion
+implementation natively works with `std::function<>`, you effectively get custom parameter
+binds for free anyway.
+
+The above shows a simple custom completion handler __dash__ as mentioned earlier, one can also
+do immediate completions, and this is an example of just that:
+
+[completion_example2]
+
+Note the new function for completing a deferred completion: `complete_async_op()`, which
+can take a `std::exception_ptr` if you want AFIO to return an errored state.
+
+As of AFIO v1.2 all of the constraints which were imposed on immediate completions
+are eliminated, and deferred completions were completely eliminated as now all completions
+are always deferred. Therefore, as of v1.2, you can do anything you can do in a normally
+scheduled op in an immediately scheduled op. Nevertheless, you should be aware of the
+following when using any kind of completion handler:
+
+* As far as AFIO is concerned, the precondition is completed and all knowledge of it
+deleted from AFIO's records by the time you see it in a completion handler (it is deleted
+as soon as possible to save on RAM footprint).
+* Therefore adding completions to the precondition of the completion being executed simply schedules
+them immediately (or executes them immediately if they are immediate) which may not be what
+you intended.
+* If you want to read in the handle output by the precondition for your own use, be aware
+that using the `async_io_op::get()` function will by default rethrow any exception stored
+into the precondition op. This has a useful side effect if you always do the following at
+the top of your completion handler implementation:
+
+ `std::shared_ptr<async_io_handle> h(op.get());`
+
+ ... by immediately rethrowing any precondition exception before you execute any more code.
+ You'll see this pattern very, very frequently throughout AFIO's source code and you are
+ encouraged to do the same. If you really don't want to propage exceptions immediately then do:
+
+ `std::shared_ptr<async_io_handle> h(op.get(true)); // Ignore any error state until later`
+
+ This will place a default constructed (i.e. empty) shared pointer into h if there is an
+ errored state, otherwise the correct handle. If you need to determine whether the precondition
+ is a null handle or an errored state, you can always use `get_exception_ptr()` on the
+ shared future referred to by the h member variable of the precondition op.
+
+[endsect] [/custom_completion_handler]
+
+[endsect] [/advanced_topics]
diff --git a/attic/doc/afio.qbk b/attic/doc/afio.qbk
new file mode 100644
index 00000000..bbadaf52
--- /dev/null
+++ b/attic/doc/afio.qbk
@@ -0,0 +1,238 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+[library Boost.AFIO
+ [quickbook 1.7]
+ [version 1.40]
+ [id afio]
+ [category afio]
+ [authors [Douglas, Niall], [Kirth, Paul]]
+ [copyright 2013-2015 Niall Douglas and Paul Kirth]
+ [purpose A portable asynchronous file i/o and filesystem library extending Boost.ASIO]
+ [license
+ Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or copy at
+ [@http://www.boost.org/LICENSE_1_0.txt])
+ ]
+ [source-mode c++]
+]
+
+[def __boost__ Boost]
+[def __boost_afio__ Boost.AFIO]
+[def __boost_bindlib__ [@https://github.com/ned14/Boost.APIBind Boost.APIBind]]
+[def __boost_outcome__ [@https://ned14.github.io/boost.outcome/group__future__promise.html Boost.Outcome]]
+[def __triplegit__ TripleGit]
+[def __fileurl__ file:///]
+[def __dash__ \u2014]
+[def __tick__ [role aligncenter [role green \u2714]]]
+[def __itick__ [role aligncenter [role red \u2714]]]
+[def __cross__ [role aligncenter [role red \u2718]]]
+[def __icross__ [role aligncenter [role green \u2718]]]
+[def __boost_asio__ [@http://www.boost.org/libs/asio/ Boost.ASIO]]
+[def __boost_thread__ [@http://www.boost.org/libs/thread/ Boost.Thread]]
+[def __boost_filesystem__ [@http://www.boost.org/libs/filesystem/ Boost.Filesystem]]
+[def __boost_iostreams__ [@http://www.boost.org/libs/iostreams/ Boost.Iostreams]]
+[def __boost_test__ [@http://www.boost.org/libs/iostreams/ Boost.Test]]
+[def __catch__ [@https://github.com/philsquared/Catch]]
+
+[/ Commonly used links]
+[def __afio_enumerate_req__ [link afio.reference.structs.enumerate_req `enumerate_req`]]
+[def __afio_io_req__ [link afio.reference.structs.io_req `io_req`]]
+[def __afio_path_req__ [link afio.reference.structs.path_req `path_req`]]
+[def __afio_stat_t__ [link afio.reference.structs.stat_t `stat_t`]]
+[def __afio_statfs_t__ [link afio.reference.structs.statfs_t `statfs_t`]]
+
+[def __afio_directory_entry__ [link afio.reference.classes.directory_entry `directory_entry`]]
+[def __afio_dispatcher__ [link afio.reference.classes.dispatcher `dispatcher`]]
+[def __afio_op__ [link afio.reference.classes.future `future<T>`]]
+[def __afio_handle__ [link afio.reference.classes.handle `handle`]]
+[def __afio_path__ [link afio.reference.classes.path `path`]]
+
+[def __afio_enumerate__ [link afio.reference.functions.enumerate `async_enumerate()`]]
+[def __afio_extents__ [link afio.reference.functions.extents `async_extents()`]]
+[def __afio_make_dispatcher__ [link afio.reference.functions.make_dispatcher `make_dispatcher()`]]
+[def __afio_statfs__ [link afio.reference.functions.statfs `async_statfs()`]]
+[def __afio_truncate__ [link afio.reference.functions.truncate `async_truncate()`]]
+[def __afio_zero__ [link afio.reference.functions.zero `async_zero()`]]
+[def __afio_when_all__ `when_all_p()`]
+
+[/ Templates]
+[template raceguarantees[contents]
+'''<informaltable frame="all"><tgroup cols="2"><thead><row><entry>Operating system</entry><entry>Race guarantees under a changing file system</entry></row></thead><tbody>'''[contents]'''</tbody></tgroup></informaltable>'''
+]
+[template raceguarantee[os descr]'''<row><entry>'''[os]'''</entry><entry>'''[descr]'''</entry></row>''']
+
+[import ../example/workshop_naive.ipp]
+[import ../example/workshop_naive_afio.ipp]
+[import ../example/workshop_naive_async_afio.ipp]
+[import ../example/workshop_atomic_updates_afio.ipp]
+[import ../example/workshop_final_afio.ipp]
+
+[import ../example/adopt_example.cpp]
+[import ../example/barrier_example.cpp]
+[import ../example/benchmark_atomic_log.cpp]
+[import ../example/call_example.cpp]
+[import ../example/closure_execution_afio_io_example.cpp]
+[import ../example/closure_execution_traditional_io_example.cpp]
+[import ../example/completion_example1.cpp]
+[import ../example/completion_example2.cpp]
+[import ../example/enumerate_example.cpp]
+[import ../example/filecopy_example.cpp]
+[import ../example/filedir_example.cpp]
+[import ../example/filter_example.cpp]
+[import ../example/find_in_files_afio.cpp]
+[import ../example/find_in_files_iostreams.cpp]
+[import ../example/readallof_example.cpp]
+[import ../example/readwrite_example.cpp]
+[import ../example/readwrite_example_traditional.cpp]
+[import ../example/statfs_example.cpp]
+[import ../test/tests/atomic_log_append_test.cpp]
+[import ../test/tests/race_protection_works.cpp]
+
+[section:introduction Introduction]
+
+'''<?dbhtml-include href="disqus_identifiers/introduction.html"?>'''
+
+__boost_afio__ is a C++ library which lets you schedule an ordered dependency graph of file and filesystem
+input/output operations to be executed asynchronously to the maximum capacity of your hardware. If you want
+to do portable asynchronous filesystem and file i/o in C++, especially if you need to easily order issues of reads and writes,
+this is the correct library to be looking at.
+
+As a quick check list, if you have ever experienced any of these problems, then AFIO may be useful to you:
+
+# Your spinning magnetic rust hard drive goes bananas when some routine in your code
+ tries to do something to storage, and latency per op starts heading into the seconds range.
+
+# Your super fast SSD which is supposed to be delivering hundreds of thousands of ops/sec
+ is barely managing a tenth of its supposed ability with your code. After reading about the
+ importance of high queue depth to maximising performance from SSDs, you try opening many
+ handles to the same file and firing an army of thread pool workers at the problem to try
+ and increase queue depth, but your performance actually drops over the single threaded case.
+
+# Your code has to interact with a regularly changing filesystem and not get weird race errors e.g. you
+ try to create a new file in path /X/Y/Z, but some other program has just renamed directory /X/Y
+ to /A/B in the time between you deciding on /X/Y/Z and getting round to it.
+
+# Your code keeps file handles open a long time in a place where others might delete or rename
+ them, including any part of the directory hierarchy preceding the file.
+
+# Deleting directory trees randomly fails on Microsoft Windows for no obvious reason.
+
+# Your code needs to read and write files concurrently to other code without resorting to
+ shared memory region tricks e.g. if the files reside on a Samba or NFS network shared drive.
+
+# Your CPU needs to be doing more useful work instead of copying memory to and from disc i/o
+ buffers. As great as the STL iostream buffering is, unless disabled it doubles the LL cache
+ pressure on your CPU, evicting other more useful data. The STL iostreams design almost certainly won't
+ allow the kernel use VM tricks to directly busmaster DMA from its buffers to the hard drive, so
+ the kernel will have to copy those buffers a third time. That means that for every 1Kb you read
+ or write you are evicted, as a minimum, 3Kb from the LL caches in your CPU, all of which must be
+ refilled with more useful data later.
+
+# Your code wants to experience various filing system features identically across platforms which
+ also work on shared Samba and NFS network drives, such as:
+ * Deleting and renaming open files.
+ * Files having unique inode values.
+ * POSIX timestamping of last accessed, last modified, last status changed and created.
+ * File extent management and traversal.
+ * Explicitly documented filing system race guarantees.
+ * Interrogation of filing system characteristics, devices and mount points.
+ * Ten million item directories, or more. We have tested twenty five million item directories on NTFS
+ and ext4 and performance was actually tolerable with under a second pause. Ten million item directories is plenty fast, and
+ one million item directories you won't notice over a ten item directory. Note that your GUI file
+ explorer will very likely hang on ten million item directories, indeed so do most command line tools.
+ * Exclusive lock files (manually operated support already there, async support coming in v1.5).
+ * File change monitoring (coming in v1.5).
+ * File byte range advisory locking (coming in v1.5).
+
+[note Lest there be any disappointment in expectations, [*using AFIO alone will not magically improve your filesystem
+performance], if anything there is a performance penalty in naive use of AFIO as a direct replacement for
+naive synchronous file i/o. What AFIO gives you is a large amount of ['control] with easy to twiddle knobs
+for benchmarking optimal filesystem strategies under various use cases. In other words, using AFIO lets you
+more easily write and test code that ['never performs really badly] in corner cases.]
+
+__boost_afio__ is a __boost_bindlib__ based Boost library, and therefore is capable of any combination of
+the following build configurations:
+
+* Uses either Boost OR the C++ 11 STL for atomic, thread, future, chrono etc.
+* Uses either Boost OR the C++ 1y STL for Fileystem. At the time of writing, only VS2015 provides Filesystem
+in its STL though libstdc++ 6.0 is expected to also do so.
+* Uses either Boost OR standalone ASIO for the i/o engine.
+
+You may note that as a result AFIO can be used as a [@https://boostgsoc13.github.io/boost.afio/afio-stable.tar.bz2 completely standalone header-only library totally independent from
+any dependencies on Boost] which can be dropped into any existing build system as [@https://boostgsoc13.github.io/boost.afio/afio-single-header.hpp.bz2 a simple single header include]. This,
+incidentally, also extends to its unit test suite which can use either __boost_test__ OR __catch__ (actually my own thread
+safe fork of CATCH).
+
+__boost_afio__ provides a pure portable POSIX file i/o backend and specialised file i/o backends
+making use of host OS asynchronous file i/o facilities are provided for:
+
+* Windows NT IOCP (since v1.0)
+* Linux KAIO (planned, would reduce thread pool blocking for read() and write() only)
+* POSIX AIO, suitable for BSD only (planned, would reduce thread pool blocking for read() and write() only)
+* WinRT (if there is demand, currently WinRT is not supported at all)
+
+__boost_afio__ is regularly compiled and per-commit unit tested on these platforms:
+
+* Android with ext4 (libc++ STL toolchain only).
+* Apple Mac OS X 10.9 with HFS+ (should still work on 10.5). Note that OS X does not provide race free filesystem due to insufficient APIs provided.
+* FreeBSD 10.1 with ZFS (should also work on 10.0). Note that the BSDs only provide race free filesystem for directories not files due to insufficient APIs provided.
+* Linux kernels 2.6.32 to 3.13 with ext4.
+* Microsoft Windows 8.1 with NTFS (should still work on XP).
+
+__boost_afio__ extends __boost_asio__ and is therefore dependent on ASIO (Boost or standalone).
+With a good modern compiler [link afio.FAQ.closure_performance you can expect 50-90% of the
+throughput of using raw Boost.ASIO] at a latency of [link afio.FAQ.closure_latency about 60,000 +/- 600 CPU cycles to get notified
+of the completion of an operation]. This library was brought to __boost__ as part of Google
+Summer of Code 2013.
+
+__boost_afio__ is a C++ 11 only library, and it requires, as an absolute minimum, a compiler with:
+
+* Rvalue reference support.
+* Variadic templates.
+* Template aliasing.
+* Noexcept.
+* Constexpr (C++ 11).
+
+Some popular compilers known to be minimally sufficient thanks to [@https://ci.nedprod.com/ our Jenkins CI bot] include:
+
+* Microsoft Visual Studio 2015, released in 2015. v1.0-v1.2 supported VS2010, v1.3 supported VS2013.
+* GNU Compiler Collection v4.8, released in 2013. v1.0-v1.2 supported GCC 4.6, v1.3 supported GCC v4.7.
+* clang v3.3, released in 2013. v1.0-v1.3 supported clang v3.2. clang v3.1 is known to produce segfaulting binaries.
+
+The Jenkins CI bot runs a full suite of static analysis tools (currently clang and MSVC static analysers and
+clang-tidy, [@http://ispras.linuxbase.org/index.php/ABI_compliance_checker the ABI stability compliance checker] is planned),
+runtime analysis tools (currently the clang undefined behaviour
+and thread sanitisers plus valgrind memcheck) plus a full set of unit tests for all supported compilers
+on all supported platforms for every single commit to master branch and every single pull request. Additionally,
+the Travis CI bot runs a full set of code coverage for the unit tests which is pushed to coveralls.io.
+[link unit_test_dashboard You can view the build and unit test CI dashboard for all compilers and platforms here].
+
+[important Note that Boost.AFIO has not passed Boost peer review, and therefore is not a part of the Boost C++ libraries]
+
+As a very quick example of minimal usage:
+
+[filedir_example]
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[endsect] [/introduction]
+
+[include design_rationale.qbk]
+[include compiling.qbk]
+[include quickstart.qbk]
+[include reference.qbk]
+[/ [include advanced_topics.qbk]]
+[include release_notes.qbk]
+[include acknowledgments.qbk]
+
+[section:index Index]
+'''
+<index/>
+'''
+[endsect] [/index]
diff --git a/attic/doc/benchmarks.xlsx b/attic/doc/benchmarks.xlsx
new file mode 100644
index 00000000..e01e1a33
--- /dev/null
+++ b/attic/doc/benchmarks.xlsx
Binary files differ
diff --git a/attic/doc/compiling.qbk b/attic/doc/compiling.qbk
new file mode 100644
index 00000000..83e772f0
--- /dev/null
+++ b/attic/doc/compiling.qbk
@@ -0,0 +1,1196 @@
+[/==============================================================================
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+===============================================================================/]
+
+
+
+[section Compilation]
+
+[def __msvc__ MSVC]
+[def __stlport__ [@http://sourceforge.net/projects/stlport STLport]]
+
+__boost_afio__ is by default a headers-only library, unless you define the macro
+`BOOST_AFIO_HEADERS_ONLY` to zero before including afio.hpp. Users only need to include the
+library headers in their programs in order to be able to access definitions
+and algorithms provided by the __boost_afio__ library. No linking against
+any binaries is required.
+
+As of v1.3 of the engine, __boost_afio__ is now capable of being used standalone
+with no dependencies. You choose the dependencies by setting the following macros or
+leaving them default:
+
+* `BOOST_AFIO_USE_BOOST_THREAD` (defaults to 0 except on Mingw32 only, Mingw-w64 has a working
+std thread library): Use Boost instead of the C++ 11 STL for
+atomic, chrono, future, mutex and thread. This is useful where your C++ 11 STL is deficient
+or where Boost's facilities are more useful.
+
+* `BOOST_AFIO_USE_BOOST_FILESYSTEM` (defaults to 0 except on VS2015 and later): Use
+__boost_filesystem__ instead of the C++ 1y Filesystem TS provided by your STL. As
+currently only VS2015 provides Filesystem, it only defaults to 1 for that platform.
+
+* `ASIO_STANDALONE` (defaults to undefined): Use standalone ASIO instead of __boost_asio__.
+You will need to provide a copy of standalone ASIO on your include path.
+
+If you wish to run unit tests, you may also adjust:
+
+* `BOOST_AFIO_USE_BOOST_UNIT_TEST` (defaults to 0): Use __boost_test__ instead
+of __boost_bindlib__'s emulation based on __catch__.
+
+Thanks to __boost_bindlib__, if your compiler has inline namespace support, you can multiply use the above four build config
+macros in [*any] combination you like, ['including] within the same translation unit
+i.e. a Boost.Thread based AFIO coexists happily with a C++ 11 STL AFIO in the same compiland,
+and any combination thereof. Simply undefine the old values of the macros, and define new values
+including afio.hpp afterwards.
+
+If you do make use of __boost_filesystem__ in your AFIO build, note that
+Boost.Filesystem is dependant on Boost.System which will also need to be linked in.
+On some platforms (e.g. ARM) you may also need Boost.Atomic.
+
+After defining your build macros of choice, making latest AFIO available to your
+code is as simple as:
+
+ #include "boost.afio/include/boost/afio.hpp" // latest version
+ #include "boost.afio/include/boost/v1/afio.hpp" // specific version
+
+if being used standalone or if being used as part of Boost:
+
+ #include "boost/afio.hpp" // latest version
+ #include "boost/afio/v1/afio.hpp" // specific version
+
+[heading:trunk_status Supported Compilers And Current Trunk Build And Unit Test State]
+[#unit_test_dashboard]
+
+'''
+<informaltable frame="all" border="1" color="black">
+ <tgroup cols="6">
+ <colspec colnum="1" colname="col1"/>
+ <colspec colnum="2" colname="col2"/>
+ <colspec colnum="3" colname="col3"/>
+ <colspec colnum="4" colname="col4"/>
+ <colspec colnum="5" colname="col5"/>
+ <colspec colnum="6" colname="col6"/>
+ <thead>
+ <row>
+ <entry>OS</entry>
+ <entry>Compiler</entry>
+ <entry>STL</entry>
+ <entry>CPU</entry>
+ <entry>Build</entry>
+ <entry>Unit tests</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry morerows="1">Static analysis</entry>
+ <entry>clang 3.7 tidy + static analyser + GCC 4.8</entry>
+ <entry/>
+ <entry/>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Static%20Analysis%20clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/buildStatus/icon?job=Boost.AFIO%20Static%20Analysis%20clang"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry/>
+ </row>
+ <row>
+ <entry>VS2013</entry>
+ <entry/>
+ <entry/>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Static%20Analysis%20MSVC/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/buildStatus/icon?job=Boost.AFIO%20Static%20Analysis%20MSVC"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry/>
+ </row>
+ <row>
+ <entry>Thread Sanitiser</entry>
+ <entry>clang 3.4</entry>
+ <entry>libstdc++ 4.9</entry>
+ <entry>x64</entry>
+ <entry/>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Sanitise%20Linux%20clang%203.4/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/buildStatus/icon?job=Boost.AFIO%20Sanitise%20Linux%20clang%203.4"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>Valgrind</entry>
+ <entry>GCC 4.8</entry>
+ <entry>libstdc++ 4.8</entry>
+ <entry>x64</entry>
+ <entry/>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Valgrind%20Linux%20GCC%204.8/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/buildStatus/icon?job=Boost.AFIO%20Valgrind%20Linux%20GCC%204.8"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>Apple Mac OS X 10.9</entry>
+ <entry>clang 3.5</entry>
+ <entry>libc++</entry>
+ <entry>x64</entry>
+ <entry>
+ <ulink url="https://travis-ci.org/BoostGSoC13/boost.afio">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://travis-ci.org/BoostGSoC13/boost.afio.png?branch=master"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://travis-ci.org/BoostGSoC13/boost.afio">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://travis-ci.org/BoostGSoC13/boost.afio.png?branch=master"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry morerows="1">Android 5.0</entry>
+ <entry>clang 3.5</entry>
+ <entry>libc++</entry>
+ <entry>x86</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>GCC 4.8</entry>
+ <entry>libc++</entry>
+ <entry>x86</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=android-ndk/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>FreeBSD 10.1 on ZFS</entry>
+ <entry>clang 3.4</entry>
+ <entry>libc++</entry>
+ <entry>x86</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=freebsd10-clang3.3/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=freebsd10-clang3.3/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=freebsd10-clang3.3/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=freebsd10-clang3.3/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=freebsd10-clang3.3/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=freebsd10-clang3.3/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=freebsd10-clang3.3/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=freebsd10-clang3.3/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=freebsd10-clang3.3/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=freebsd10-clang3.3/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=freebsd10-clang3.3/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=freebsd10-clang3.3/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry morerows="1">Ubuntu Linux 12.04 LTS</entry>
+ <entry>clang 3.3</entry>
+ <entry>libstdc++ 4.8</entry>
+ <entry>x86</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=static,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=shared,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.3,LINKTYPE=standalone,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>clang 3.4</entry>
+ <entry>libstdc++ 4.8</entry>
+ <entry>x86</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=static,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=static,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=shared,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=shared,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=standalone,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=standalone,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=static,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=static,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=shared,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=shared,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=standalone,label=linux-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.4,LINKTYPE=standalone,label=linux-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry morerows="7">Ubuntu Linux 14.04 LTS</entry>
+ <entry>clang 3.5</entry>
+ <entry>libstdc++ 4.9</entry>
+ <entry>x64</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>clang 3.5</entry>
+ <entry>libstdc++ 4.9</entry>
+ <entry>ARMv7</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=static,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>clang 3.6</entry>
+ <entry>libstdc++ 4.9</entry>
+ <entry>x64</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.6,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>clang 3.7</entry>
+ <entry>libstdc++ 4.9</entry>
+ <entry>x64</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=clang++-3.7,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>GCC 4.8</entry>
+ <entry>libstdc++ 4.8</entry>
+ <entry>x64</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>GCC 4.9</entry>
+ <entry>libstdc++ 4.9</entry>
+ <entry>x64</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>GCC 4.9</entry>
+ <entry>libstdc++ 4.9</entry>
+ <entry>ARMv7</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=static,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-4.9,LINKTYPE=standalone,label=arm-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>GCC 5.1</entry>
+ <entry>libstdc++ 5.1</entry>
+ <entry>x64</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=static,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=shared,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=g++-5,LINKTYPE=standalone,label=linux64-gcc-clang/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry morerows="2">Microsoft Windows 8.1</entry>
+ <entry>Mingw-w64 GCC 4.8</entry>
+ <entry>libstdc++ 4.8</entry>
+ <entry>x64</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=mingw64,LINKTYPE=static,label=win8-msvc-mingw/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=mingw64,LINKTYPE=static,label=win8-msvc-mingw/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=mingw64,LINKTYPE=shared,label=win8-msvc-mingw/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=mingw64,LINKTYPE=shared,label=win8-msvc-mingw/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=mingw64,LINKTYPE=static,label=win8-msvc-mingw/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=mingw64,LINKTYPE=static,label=win8-msvc-mingw/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=mingw64,LINKTYPE=shared,label=win8-msvc-mingw/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++11,CXX=mingw64,LINKTYPE=shared,label=win8-msvc-mingw/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ <row>
+ <entry>Visual Studio 2015</entry>
+ <entry>Dinkumware</entry>
+ <entry>x64</entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=static,label=win8-msvc-mingw/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=static,label=win8-msvc-mingw/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=win8-msvc-mingw/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=win8-msvc-mingw/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=standalone,label=win8-msvc-mingw/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=standalone,label=win8-msvc-mingw/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ <entry>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=static,label=win8-msvc-mingw/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=static,label=win8-msvc-mingw/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=win8-msvc-mingw/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=shared,label=win8-msvc-mingw/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ <ulink url="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=standalone,label=win8-msvc-mingw/">
+ <mediaobject>
+ <imageobject>
+ <imagedata fileref="https://ci.nedprod.com/job/Boost.AFIO%20Test/CPPSTD=c++14,CXX=msvc-14.0,LINKTYPE=standalone,label=win8-msvc-mingw/badge/icon"/>
+ </imageobject>
+ </mediaobject>
+ </ulink>
+ </entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </informaltable>
+'''
+
+
+[heading Boost AFIO Installation into Boost]
+
+As of v1.3 of the engine, you can simply download [@https://boostgsoc13.github.io/boost.afio/afio-stable.tar.bz2 the afio stable
+tarball] (same as git clone __boost_afio__ followed by a git submodules recursive update and init) and get to work. If however you
+wish to use AFIO as part of the Boost libraries, AFIO is also a Boost module:
+
+# Download the latest version of the Boost Libraries from boost.org.
+ At the time of writing the current version is Boost 1.59.
+ Installing AFIO as a Boost module is not compatible with versions of Boost earlier than 1.56 as this was
+ the first Boost version to become source code modularised.
+
+
+# Download the latest Boost AFIO stable tarball from [@https://boostgsoc13.github.io/boost.afio/afio-stable.tar.bz2 https://boostgsoc13.github.io/boost.afio/afio-stable.tar.bz2].
+ We do however recommend using the git repository, as it will allow for future updates. If you are unfamiliar
+ with git, check the github website for instructions on how to use git. This is a good place to
+ start: [@https://help.github.com/articles/set-up-git]. Don't forget that after cloning you need to
+ do a `git submodule update --init --recursive` to have git fill in the submodules.
+
+
+# Assuming that your copy of Boost lives at /home/ned/boost do:
+
+ ``
+ cd /home/ned/boost/libs
+ tar jxf afio-stable.tar.bz2 afio
+ cd ..
+ ./b2 headers
+ ./b2 libs/afio/build cxxflags=-std=c++11
+ ``
+
+ On Microsoft Windows it's almost the same, just don't specify the `-std=c++11` if you are using VS2015.
+Note if you forget the `-std` currently you need to wipe the `bin.v2` directory to reset it as the result is cached
+by Boost.Build.
+
+# If you'd also like to execute the unit test suite, do:
+ ``
+ ./b2 libs/afio/test cxxflags=-std=c++11
+ ``
+
+# Similar if you'd like to build documentation, do:
+ ``
+ cd doc
+ ../b2 ../libs/afio/doc
+ ../b2 ../libs/afio/doc pdf
+ ``
+
+ I may be missing something, but the pdf appears inside bin.v2, not in pdf for some reason.
+ Note that the PDF has some significant formatting problems.
+
+# Thanks to modular Boost, AFIO is now exactly the same as any other Boost library and can
+be used in exactly the same way as if AFIO were already part of Boost.
+
+
+[heading Running the Unit Tests]
+
+To run the unit tests as a standalone build, something like this will probably work for POSIX (it will default to clang on FreeBSD):
+
+``
+ ./standalone_alltests_gcc.sh
+ ./test_all
+``
+
+Note this has a dependency on __boost_filesystem__ as libstdc++ 4.9 (the last I tested) has insufficient Filesystem support. For VS2015:
+
+``
+ standalone_alltests_msvc.bat
+ test_all.exe
+``
+
+VS2015 ships with Filesystem, so this is totally standalone.
+
+
+To run the unit tests under Boost with [*bjam], run from the boost top directory:
+``
+ ./b2 libs/afio/test cxxflags=-std=c++11
+``
+This will run the entire suite of unit tests. Some useful command line arguments for Boost.Build:
+
+* [*--link-test]: will only compile and link the test files. This option can be combined with
+ the [*--test=] option to only compile and link a specific subset of unit tests.
+ Note that [*--link-test] supersedes [*--test-all] and [*--test-each] options,
+ and cannot be combined with the other tests.
+
+* [*--running-on-ci]: will compile, link and execute only those tests which don't use `fsync()`.
+
+* [*--lto]: perform link-time code assembly. On POSIX with clang, you NEED the gold linker installed for LTO
+ to work - otherwise it appears to work, but in fact is doing a normal link.
+
+* [*--test-all]: will run a comprehensive unit tests that combines all the other unit tests into a single
+ continuous test. This is useful to detect errors that will only show up at longer running
+ times. Note that [*--test-each] is not compatible with this option.
+
+* [*--test-each]: will run a series of individually compiled unit tests in succession.
+ This option forgoes the lengthy monolithic test-suite in favor of a
+ series of small tests. This option can be combined with the [*--test=]
+ option to only run a specific subset of unit tests. Note that [*--test-all]
+ is not compatible with this option.
+
+* [*--test=]: will use only the test files designated by the string following this flag.
+
+For example:
+
+ ``
+ b2 link=static toolset=gcc-5 cxxflags=-std=c++1z --test="first_test.cpp second_test.cpp"
+ ``
+
+will only compile and run only the tests in `first_test.cpp` and `second_test.cpp`. All other unit tests will
+be ignored, and all other command-line options still apply.
+
+[endsect] [/ end of Compilation]
diff --git a/attic/doc/copyright_block.qbk b/attic/doc/copyright_block.qbk
new file mode 100644
index 00000000..5b447d56
--- /dev/null
+++ b/attic/doc/copyright_block.qbk
@@ -0,0 +1,7 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
diff --git a/attic/doc/design_rationale.qbk b/attic/doc/design_rationale.qbk
new file mode 100644
index 00000000..617e29ff
--- /dev/null
+++ b/attic/doc/design_rationale.qbk
@@ -0,0 +1,462 @@
+[/==============================================================================
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+===============================================================================/]
+
+[section:overview AFIO single page cheat sheet for the very impatient]
+
+'''<?dbhtml-include href="disqus_identifiers/overview.html"?>'''
+
+* AFIO models file descriptors/handles as a reference counted `std::shared_ptr<__afio_handle__>` (aliased as `afio::handle_ptr`) where `handle` is an
+opaque abstract base class. `handle` may, or may not, refer to a currently open file descriptor/handle
+i.e. it is possible to close it before the reference count reaches zero. You can create your own custom
+implementations of `handle`.
+
+* AFIO models a filesystem as a reference counted `std::shared_ptr<__afio_dispatcher__>` (aliased as `afio::dispatcher_ptr`) where `dispatcher` is also
+an opaque abstract base class. When constructed using __afio_make_dispatcher__ you specify a URI such as [*`__fileurl__`] which specifies what
+namespace the dispatcher will dispatch onto. You can create your own custom `dispatcher` implementations for some URI regex pattern too.
+
+* AFIO provides its own custom future type `afio::__afio_op__<T = void>` which [*always] represents a future `handle_ptr` plus an optional `T`
+which is usually `void` (hence defaulting `T` to `void`). This custom future type is implemented using __boost_outcome__'s lightweight
+monadic future factory toolkit, and therefore provides all the C++ 1z Concurrency TS and __boost_thread__ extensions including
+continuations and wait composure. Note that lightweight futures can carry an `error_code` as well as an `exception_ptr`, and therefore
+much of the need for non-throwing `error_code` taking API overloads is not needed (though any function not returning a future still
+uses that idiom).
+
+* AFIO provides its own custom filesystem path type `afio::__afio_path__` which is a thin wrap of your STL/Boost
+Filesystem path. This is done because on Microsoft Windows, AFIO [*always] uses NT kernel paths,
+never Win32 paths. By skipping the Win32 translation layer one achieves significantly better
+performance and much closer to POSIX semantics including case sensitivity, plus there are no issues
+with path length limits. On Windows `afio::path` (NT kernel paths) will usually auto convert to and from `filesystem::path`
+(Win32 paths), however do note there is not always a one to one mapping from a NT kernel path to
+a Win32 path in which case the Win32 paths to send in will never be the Win32 paths you get out.
+See the documentation for __afio_path__.
+
+ [caution Until [@https://github.com/BoostGSoC13/boost.afio/issues/79 issue #79] is resolved, don't
+do directory enumerations from an x86 binary on a x64 Windows system. This is due to a bug in
+Microsoft's WOW64 syscall translation layer which Microsoft have decided is wontfix.]
+
+* Error reporting [*always] follows the ["earliest possible reporting] rule. As absolutely
+everything is asynchronous in AFIO, that also means that error and exception handling also occurs
+asynchronously. So if you try to use a future as an input and that future is
+errored at the point of use, the errored state is immediately rethrown. As futures may
+become errored asynchronously, that means you must always write code which assumes any
+API which consumes a future can throw (unless you know for a fact the future is ready and
+not errored).
+
+ [note Gathering error states from many futures at once is made easier using the `when_all_p()`
+(when all propagating) extended wait composure function from __boost_outcome__ __dash__ this
+propagates any error in any of the entry futures into the composed output future, thus saving
+you having to check the futures for errors by hand.]
+
+* Handles to directories may ignore requests to close them i.e. `handle_ptr->close()`
+is a no-op. This is due to a performance feature called ["directory handle caching] which shares
+read-only directory handles amongst all users unless specifically requested not to do so. You
+can test for this using the `available_to_directory_cache()` member function of `handle`.
+
+* As a general rule, AFIO spots when you try to delete a currently open file handle where
+that is illegal on your platform (Microsoft Windows) and will instead rename the file to a
+cryptographically strong random name of the form `<32 chars of random hex>.afiod` and ask the operating system to delete
+it later when the last handle is closed. Any enumerations of directories containing these
+randomised named files will omit those entries by default. This emulates POSIX semantics, albeit imperfectly.
+One caveat is it cannot always do this with directories,
+though it tries hard to try again later where it can. A future version of AFIO will add `tmpfs`
+support which will let us relocate troublesome directories into the temp directory where they can
+be deleted much later, thus more closely matching POSIX semantics still further.
+
+__boost_afio__ version 1.4 provides the following operations:
+
+[table:supported_operations
+[[Operation][Asynchronous batch `__afio_dispatcher__` functions][Synchronous `__afio_handle__` functions][Asynchronous free functions][Synchronous free functions][Related types]]
+[[[link afio.reference.functions.dir Open/create a directory: `dir()`]] [1] [] [2] [4] [__afio_path_req__]]
+[[[link afio.reference.functions.rmdir Delete a directory: `rmdir()`]] [1] [] [2] [4] [__afio_path_req__]]
+[[[link afio.reference.functions.file Open/create a file: `file()`]] [1] [] [2] [4] [__afio_path_req__]]
+[[[link afio.reference.functions.rmfile Delete a file: `rmfile()`]] [1] [] [2] [4] [__afio_path_req__]]
+[[[link afio.reference.functions.symlink Open/create a symlink: `symlink()`]] [1] [] [2] [4] [__afio_path_req__]]
+[[[link afio.reference.functions.rmsymlink Delete a symlink: `rmsymlink()`]] [1] [] [2] [4] [__afio_path_req__]]
+[[[link afio.reference.functions.sync Synchronise changes to physical storage: `sync()`]] [1] [] [1] [2] []]
+[[[link afio.reference.functions.zero Deallocate/zero physical storage: `zero()`]] [1] [] [1] [2] []]
+[[[link afio.reference.functions.close Close a fd/handle: `close()`]] [1] [] [1] [2] []]
+[[[link afio.reference.functions.read Scatter read file contents: `read()`]] [1] [] [2] [4] [__afio_io_req__]]
+[[[link afio.reference.functions.write Gather write file contents: `write()`]] [1] [] [2] [4] [__afio_io_req__]]
+[[[link afio.reference.functions.truncate Set maximum file extent: `truncate()`]] [1] [] [1] [2] []]
+[[[link afio.reference.functions.enumerate Enumerate directory contents/
+fetch file metadata: `enumerate()`]] [1] [] [3] [6] [__afio_enumerate_req__, __afio_directory_entry__, __afio_stat_t__]]
+[[[link afio.reference.functions.extents Enumerate file physical storage extents: `extents()`]] [1] [] [1] [2] []]
+[[[link afio.reference.functions.statfs Examine mounted storage volume: `statfs()`]] [1] [] [1] [2] [__afio_statfs_t__]]
+
+[[[link afio.reference.classes.handle.path Get current path of an open fd/handle
+even if other processes are renaming it
+(Linux, Windows; FreeBSD directories only;
+not OS X): `path()`]] [] [2] [] [] [__afio_path__]]
+[[[link afio.reference.classes.handle.direntry Get open fd/handle metadata: `direntry()`, `lstat()`]][] [2] [] [] [__afio_directory_entry__, __afio_stat_t__]]
+[[[link afio.reference.classes.handle.target Get target path of a symlink: `target()`]] [] [1] [] [] []]
+[[[link afio.reference.classes.handle Map a read-only file into memory: `try_mapfile()`]] [] [1] [] [] []]
+[[[link afio.reference.classes.handle.link Hard link an open fd/handle to a new path: `link()`]][] [1] [] [] [__afio_path_req__]]
+[[[link afio.reference.classes.handle.unlink Unlink an open fd/handle from its existing
+path (caution!): `unlink()`]] [] [1] [] [] [__afio_path_req__]]
+[[[link afio.reference.classes.handle.atomic_relink ['Strong guaranteed atomically] relink an open
+fd/handle from its existing path to a different path: `atomic_relink()`]] [] [1] [] [] [__afio_path_req__]]
+]
+
+Planned new operations coming next version of AFIO so you know what's coming next:
+
+* Mutual exclusion and locking which works across NFS and Samba:
+ * The ability to tag many `handle`'s with what locking to do per operation.
+ * Asynchronous exclusive lock files.
+ * Asynchronous multi-file byte range locking.
+ * Individual file change monitoring (needed to implement locking).
+* [*`tmp://`] URI backend for a temporary file filesystem.
+* Optional inline hashing of file reads and writes with SpookyHash/Blake2b/SHA256.
+* Maybe Boost.Fiber support to gain coroutines on all compilers.
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[endsect] [/overview]
+
+
+[section:design Design Introduction and Rationale]
+
+'''<?dbhtml-include href="disqus_identifiers/design_rationale.html"?>'''
+
+__boost_afio__ came about out of the need for a scalable, high performance, portable asynchronous
+file i/o and filesystem implementation library for a forthcoming filing system based graph store ACID compliant transactional
+persistence layer called __triplegit__ __dash__ call it a ["SQLite3 but for graphstores][footnote The
+[@http://unqlite.org/ UnQLite embedded NoSQL database engine] is exactly one of those of course.
+Unfortunately I intend TripleGit for implementing portable Component Objects for
+C++ extending C++ Modules, which means I need a database engine suitable for incorporation into
+a dynamic linker, which unfortunately is not quite UnQLite.]. The fact that a portable
+asynchronous file i/o and filesystem library for C++ was needed at all came as a bit of a surprise: one
+thinks of these things as done and dusted decades ago, but it turns out that the fully featured
+[@http://nikhilm.github.io/uvbook/filesystem.html libuv], a C library, is good enough for most
+people needing portable asynchronous file i/o. However as great as libuv is, it isn't very C++-ish, and
+hooking it in with __boost_asio__ (parts of which are expected to enter the ISO C++ language
+standard) isn't particularly clean. I therefore resolved to write a native Boost asynchronous
+file i/o and filesystem implementation, and keep it as simple as possible.
+
+[heading A quick version history]
+
+AFIO started life as a C++ 0x library written for an early Visual Studio 2013 Community Preview back
+in 2012 as a outside-of-work side project when I was working at BlackBerry. It was ported to Boost
+during Google Summer of Code 2013 with the help of student Paul Kirth, and VS2012 and VS2010 support
+was added. For v1.0, AFIO used a simple dispatch engine which kept the extant ops in a hash table,
+and the entire dispatch engine was protected by a single giant and recursive mutex. Performance
+never exceeded about 150k ops/sec maximum on a four core Intel Ivy Bridge CPU.
+
+That performance was embarrassing, so for v1.1 the entire engine was rewritten using atomic shared
+pointers to be completely lock free, and very nearly wait free if it weren't for
+the thin spin locks around the central ops hash table. Now performance
+can reach 1.5m ops/sec on a four core Intel Ivy Bridge CPU, or more than half of Boost.ASIO's
+maximum dispatch rate.
+
+For the v1.2 engine, another large refactor was done, this time to substantially simplify the
+engine by removing the use of `std::packaged_task<>` completely, replacing it with a
+new intrusive-capable `enqueued_task<>` which permits the engine to early out in many cases,
+plus allowing the consolidation of all spinlocked points down to just two: one in dispatch,
+and one other in completion, which is now optimal. Performance of the v1.2 engine rose
+by about 20% over the v1.1 engine, plus AFIO is now fully clean on all race detecting tools.
+
+For the v1.3 engine, yet another large refactor was done, though not for performance but rather
+to make it much easier to maintain AFIO in the future, especially after acceptance into Boost
+whereupon one cannot arbitrarily break API anymore, and one must maintain backwards compatibility.
+To this end the dependencies between AFIO and Boost were completely abstracted into a substitutable
+symbol aliasing layer such that any combination of Boost and C++ 11 STL threading/chrono, filesystem
+and networking can be selected externally using macros. Indeed, any of the eight build combinations
+can coexist in the same translation unit too, I have unit test runs which prove it! With the v1.3
+engine AFIO optionally no longer needs Boost at all, not even for its unit testing. However the
+cost was dropping support for all Visual Studios before 2013 and all GCCs before 4.7 as they
+don't have the template aliasing support needed to implement the STL abstraction layer. A very large
+amount of legacy cruft code e.g. support for non-variadic templates was cleaned out for the v1.3 release.
+
+[heading This version 1.4 of AFIO]
+During ACCU and C++ Now 2015 I spoke with a number of ISO WG21 committee members about the structural
+design problems in iostreams and the Filesystem TS (lack of filesystem race freedom, lack of context
+dependant filesystem), and what design the committee would prefer to see to fix those problems. As it
+happens, we were all close to the same page, so from the v1.4 engine onwards I resolved to refactor the AFIO API thusly:
+
+# Since v1.0 AFIO implemented the Concurrency TS continuations atop standard futures using a central hash table
+to keep the continuations information per future. This had the big advantage that standard STL and Boost
+futures could be used, but it came with many other problems, mainly performance and code complexity related.
+An additional problem was that the Concurrency TS had moved on considerably since 2012, and AFIO's emulation
+was now significantly out of date.
+For v1.4 a new lightweight future-promise factory toolkit library called __boost_outcome__ was written between the
+end of C++ Now (May) and the beginning of the AFIO peer review (July) which makes
+easy the writing of arbitrarily customisable future-promises for C++ which:
+
+ * Implements an almost complete Concurrency TS ([@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4399.html N4399])
+future-promise specification with almost all the Boost.Thread future-promise extensions.
+ * Are very considerably more performant (2x-3x) and much more reliably low latency (no memory allocation).
+ * Permit arbitrary wait composure of any kind of custom future-promise with one another.
+ * Are part of a general purpose lightweight C++ monadic programming implementation, so futures are merely
+asynchronous monads.
+ * Natively support C++ 1z coroutines ([@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4499.pdf N4499])
+which are currently only supported by Visual Studio 2015.
+
+ The use of this future factory toolkit makes the AFIO continuations infrastructure redundant, and it will
+therefore be removed shortly. The monadic programming library also makes quite a bit of internal AFIO
+implementation code much more simplified as thanks to the monads, one can use noexcept design throughout and therefore
+skip dealing directly with exception safety as the monads take away the potential of control flow being reversed
+by an exception throw.
+
+ [note This version of AFIO being presented for Boost review does not yet make use of lightweight future-promises,
+and instead mocks up the eventual API using the existing highly mature and very well tested engine. The API presented
+is expected to be final, except for the very few items specified as deprecated (see below for a list). This has been
+done in order to test that the engine rewrite based on lightweight future-promise exactly matches the behaviour of
+the current engine using an identical unit test suite.
+
+I should emphasise that I expect any programs written to match the presented API to continue to work after the engine
+rewrite __dash__ after all the internal unit test suite will do so.]
+
+# For race free filesystem programming, you really need to base all path related operations on an open file
+descriptor or handle, so something like:
+
+ ``
+ afio::handle_ptr &h; // Some open file handle
+
+ // Create a sibling file to h->path() race free
+ afio::handle_ptr newfileh=afio::file(h, "../newfile", afio::file_flags::create);
+
+ // Asynchronously create a sibling file to h->path() race free
+ // afio::future has type void because afio::future *always* carries a shared pointer to a handle
+ // (it only gets a type when the operation returns more than a file handle)
+ afio::future<void> newfilefh2=afio::async_file(h, "../newfile", afio::file_flags::create);
+
+ // Wait for the asynchronous file creation to complete, rethrowing any error or exception
+ afio::handle_ptr newfileh2=newfilefh2.get_handle();
+ ``
+
+ I'll admit this design isn't quite what the members of WG21 had in mind, especially the notion that `afio::future<void>`
+with type void always carries a shared pointer to a handle and that implicit type slicing from `afio::future<T>`
+to `afio::future<void>` is not just allowed but absolutely essential. However apart from that, the
+above API is probably quite close to what members of WG21 were thinking.
+
+# One oft-observed design limitation in the Filesystem TS is that it cannot support filesystems in filesystems,
+with the most classic example being a ZIP archive on a filesystem where it might be nice to allow generic C++
+filesystem code to not need to be aware that the filesystem it sees is inside a ZIP archive. The solution could be one or both of these options:
+
+ # Make the Filesystem TS operations hang as virtual member functions off some `filesystem` abstract base class.
+ # Have a thread local variable set the current `filesystem` instance to be used by the global Filesystem TS
+functions on that thread.
+
+ AFIO v1.4 and probably v1.5 won't implement this as this is really a thing for Boost.Filesystem to do. However,
+AFIO's __afio_make_dispatcher__ already takes a URI and there is a RAII facility for setting the current
+thread local dispatcher, so AFIO is ready for a Filesystem TS implementation matching the above design to be written on top of it in that
+a `dispatcher` instance has a suite of virtual member functions
+which define what some filesystem is or does. AFIO v1.4 only provides POSIX and NT kernel filesystem backends
+currently, however it is expected that v1.5 will add a new temporary filesystem backend which lets programs
+portably work inside `tmpfs` in whichever form that takes across Linux, FreeBSD, Apple OS X and Microsoft Windows.
+Additional backends implementing say a ZIP archive filesystem are similarly easy to add on.
+
+As mentioned above, note that due to the above refactoring some parts of this v1.4 release of AFIO are deprecated and are expected
+to be removed shortly. You can find a list of these shortly to be removed APIs and parts [link afio.release_notes here].
+The list is not long, and the removals are obvious.
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[section:acid_write_ordering Write ordering constraints, and how these can
+be used to achieve some of the Durability in ACID without needing `fsync()`]
+
+'''<?dbhtml-include href="disqus_identifiers/design_rationale.html"?>'''
+
+[*ACID] stands for Atomic Consistent Isolated Durable, and it is a key requirement for
+database implementations which manage data you don't want to lose, which tends to be
+a common use case in databases. Whilst achieving all four is non-trivial, achieving Durability is
+simultaneously both the easiest and the hardest of the four. This is because the easy
+way of ensuring durability is to always wait for each write to reach non-volatile storage,
+yet such a naive solution is typically exceptionally slow. Achieving performant
+data durability is without doubt a wicked hard problem in computer science.
+
+Because a majority of users of __boost_afio__ are going to be people needing
+some form of data persistence guarantees, this section is a short essay on the current
+state of data persistence on various popular platforms. Any errors or omissions, both
+of which are probable, are entirely the fault of this author Niall Douglas. Note also that
+the forthcoming information was probably correct around the winter of 2014, and it highly
+likely to become less correct over time as filing system implementations evolve.
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[section:background Background on how filing systems work]
+
+'''<?dbhtml-include href="disqus_identifiers/design_rationale.html"?>'''
+
+Filing system implementations traditionally offer three methods of ensuring that writes
+have reached non-volatile storage:
+
+# The family of `fsync()` or its equivalent functions, which flush any cached written
+data not yet stored onto non-volatile storage. These are usually synchronous operations,
+in that they do not return until they have finished. A big caveat with these functions
+is that some filing systems e.g. ext3 flush ['every] bit of pending write data for
+the filing system instead of just the pending writes for the file handle specified i.e. they
+are equivalent to a synchronous `sync()` as described below.
+
+# The family of `O_SYNC` or its equivalent per file handle flags, which simply
+disable any form of write back caching. These usually make all data write functions
+not return until written data has reached non-volatile storage. This flag, for all intents
+and purposes, effectively asks for ["old fashioned] filing system behaviour from before
+when filing systems tried to be clever by not actually writing changes when a program
+writes changes.
+
+# The whole filing system cached written data flush, often performed by a function
+like `sync()`. Unlike the previous two, this is usually an asynchronous operation
+and there is usually no portable way of knowing when it has completed. Nevertheless,
+it is important because on traditional Unix implementations data persistence is simply
+`sync()` on a regular period cronjob, and while modern Unix implementations usually
+no longer do this, the end implementation has not fundamentally changed much[footnote
+The main change is that individual writes get an individual lifetime before they must
+be written to storage rather flushing everything according to some external wall clock.].
+
+There is also the matter of the difference between data and ['meta]data: metadata
+is the stuff a filing system stores such that it knows about your data. For each
+of the first two of the above three families of functions, most systems provide
+three variants: flush metadata, flush data, and flush both metadata and data, so
+for clarity:
+
+[table:data_persistence_types Mechanisms for enforcing data persistence onto physical storage
+ [[][Flush file metadata][Flush file data][Flush both metadata and data]]
+ [[Once off][`fsync(parentdir_fd)`][`fdatasync(fd)`][`fsync(fd)`]]
+ [[Always][Varies[footnote Many filing systems (NTFS, HFS+, ext3/4 with `data=ordered`) keep back a metadata flush until when a file handle close causes data to finish reaching physical storage. This ensures that file entries don't appear in directories with zero sizes.]][`fcntl(fd, F_SETFL, O_DSYNC)`][`fcntl(fd, F_SETFL, O_SYNC)`]]
+]
+
+In addition to manually flushing data to physical storage, every filing system also
+implements some form of timer based flush whereby a piece of written data will always
+be sent to physical storage within some predefined period of time after the write.
+Most filing systems implement different timeouts for metadata and data, but typically
+on almost all production filing systems __dash__ unless they are in a power-saving laptop mode --
+any data write is guaranteed to be sent to non-volatile storage within one minute.
+Let me be clear here for later argument's sake: ['the filing system is allowed to
+reorder writes by up to one minute in time from the order in which they were issued].
+Or put another way, most filing systems have a one minute temporal constraint on
+write order.
+
+Most people think of `fsync()`, `O_SYNC` and `sync()` in terms of flushing caches.
+An alternative way of thinking about them is that they ['impose an order on
+writes] to non-volatile storage which acts above and beyond the timeout based write order. There
+is no doubt that they are a very crude and highly inefficient way of doing so because
+they are all or nothing, but they do open the option of ['emulating]
+native filing system support for write ordering constraints when nothing else better
+is available. So why is the ability to constrain write ordering important?
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[endsect] [/background]
+
+[section:write_ordering_data Write ordering data and durability: why does it matter?]
+
+'''<?dbhtml-include href="disqus_identifiers/design_rationale.html"?>'''
+
+[note You may find the paper presented at C++ Now 2014 [@http://arxiv.org/abs/1405.3323 ["Large Code Base Change Ripple Management in C++: My thoughts on how a new Boost C++ Library could help]] of interest here.]
+
+Implementing performant Durability essentially reduces down to answering two questions: (i) how long does it take to restore
+a consistent state after an unexpected power loss, and (ii) how much of your most recent data are
+you willing to lose? AFIO has been designed and written as the asynchronous file i/o
+portability layer for the forthcoming direct
+filing system graphstore database __triplegit__ which, like as with ZFS, implements late Durability i.e.
+you are guaranteed that your writes from some wall clock distance from now can never
+be lost. As discussing how TripleGit will use AFIO is probably useful to many others,
+that is what the remainder of this section does.
+
+__triplegit__ will achieve the Consistent and Isolated parts of being a reliable database by placing
+abortable, garbage collectable concurrent writes of new data into separate files, and pushing
+the atomicity enforcement into a very small piece of ordering logic in order to reduce
+transaction contention by multiple writers as much as possible.
+If you wish to never lose most recent data, to implement a transaction
+one (i) writes one's data to the filing
+system, (ii) ensure it has reached non-volatile storage, (iii) appends the knowledge it
+definitely is on non-volatile storage to the intent log, and then (iv) ensure one's
+append also has reached non-volatile storage. This is presently the only way to ensure
+that valuable data definitely is never lost on any filing system that I know of. The obvious
+problem is that this method involves writing all your data with `O_SYNC` and using `fsync()`
+on the intent log. This might perform okay with a single writer, but with multiple
+writers performance is usually awful, especially on storage incapable of high
+queue depths and potentially many hundreds of milliseconds of latency (e.g. SD Cards).
+Despite the performance issues, there are many valid use cases for especially precious
+data, and TripleGit of course will offer such a facility, at both the per-graph and per-update
+levels.
+
+__triplegit__'s normal persistence strategy is a bit more clever: write all your data, but keep a hash like a SHA of its contents
+as you write it[footnote TripleGit actually uses a different, much faster 256 bit 3 cycles/byte cryptographic hash
+called [@https://blake2.net/ Blake2] by default, but one can force use of SHA256/512 on a per-graph basis,
+or indeed if your CPU has SHA hardware offload instructions these may be used by default.]. When you write your intent log,
+atomically append all the SHAs of the items
+you just wrote and skip `O_DATA` and `fsync()` completely. If power gets removed
+before all the data is written to non-volatile storage, you can figure out that
+the database is dirty easily enough, and you simply parse from the end of the intent
+log backwards, checking each item's contents to ensure their SHAs match up, throwing
+away any transaction where any file is missing or any file's contents don't match.
+On a filing system such as ext4 where data is guaranteed to be sent to non-volatile
+storage after one minute[footnote This is the default, and it may be changed by a
+system e.g. I have seen thirty minutes set for laptops. Note that the Linux-specific
+call `syncfs()` lets one artifically schedule whole filing system flushes.], and of
+course so long as you don't mind losing up to one
+minute's worth of data, this solution can have much better performance than the
+previous solution with lots of simultaneous writers.
+
+The problem though is that while better, performance is still far less than optimal.
+Firstly, you have to calculate a whole load of hashes all the time, and that isn't trivial
+especially on lower end platforms like a mobile phone where 25-30 cycles per byte
+SHA256 performance might be typical. Secondly, dirty database reconstruction is
+rather like how ext2 had to call `fsck` on boot: a whole load of time and i/o
+must be expended to fix up damage in the store, and while it's running one generally
+must wait.
+
+What would be really, really useful is if the filing system exposed its internal
+write ordering constraint implementation to user mode code, so one could say ["schedule writing A, B,
+C and D in any order whenever you get round to it, but you ['must] write all
+of those before you write any of E, F and G]. Such an ability gives maximum
+scope to the filing system to reorder and coalesce writes as it sees fit,
+but still allows database implementations to ensure that a transaction's intent
+log entry can never appear without all the data it refers to. Such an ability
+would eliminate the need for expensive dirty database checking and reconstruction,
+or the need for any journalling infrastructure used to skip the manual integrity checking.
+
+Unfortunately I know of no filing system which makes publicly available such
+a facility. The closest that I know of is ZFS which internally uses a concept of transaction
+groups which are, for all intents and purposes, partial whole filing system snapshots issued once every
+five seconds. Data writes may be reordered into any order within
+a transaction group, but transaction group commits are strongly tied to the wall
+clock and are [*always] committed sequentially. Since the
+addition of the ZFS Write Throttle, the default settings are to accept new writes
+as fast as RAM can handle, buffering up to
+thirty wall clock seconds of writes before pacing the acceptance of new write data to match
+the speed of the non-volatile storage (which may be a ZFS Intent Log (ZIL) device if you're doing
+synchronous writes). This implies up to
+thirty seconds of buffered data could be lost, but note that ZFS still guarantees
+transaction group sequential write order. Therefore, what ZFS is in fact guaranteeing
+is this: ["we may reorder your write by up to five seconds away from the sequence
+in which you wrote it and other writes surrounding it. Other than that, we guarantee
+the order in which you write is the order in which that data reaches physical storage.]
+[footnote Source: [@http://www.c0t0d0s0.org/archives/5343-Insights-into-ZFS-today-The-nature-of-writing-things.html]]
+
+What this means is this: on ZFS, TripleGit can turn off all write synchronisation and
+replace it with a five second delay between writing new data and updating the intent log,
+and in so doing guaranteeing that the intent log's contents will [*always] refer to data
+definitely on storage (or rather, close enough that one need not perform a lot of repair
+work on first use after power loss). One can additionally skip SHA hashing on reads because ZFS guarantees file and metadata will always match and as TripleGit always
+copy on writes data, either a copy's length matches the intent log's or it doesn't (i.e.
+the file's length as reported by the filing system really is how much true data it contains), plus the file
+modified timestamp always reflects the actual last modifed timestamp of the data.
+
+Note that ext3 and ext4 can also guarantee that file and metadata will always match using
+the (IOPS expensive) mount option `data=journal`, which can be detected from `/proc/mounts`.
+If combined with the proprietary Linux call `syncfs()`, one can reasonably emulate ZFS's
+behaviour, albeit rather inefficiently. Another option is to have an idle thread issue fsync for writes
+in the order they were issued after some timeout period, thus making sure that writes definitely will reach physical storage within
+some given timeout and in their order of issue __dash__ this can be used to emulate the ZFS wall clock based write order
+consistency guarantees.
+
+[note You may find the tutorial of interest which implements an ACID transactional key-value store
+using the theory in this section.]
+
+Sadly, most use of __triplegit__ and __boost_afio__ will be without the luxury of ZFS, so here
+is a quick table of power loss data safety. Once again, I reiterate that errors and omissions are my fault alone.
+
+[include power_loss_safety_table.qbk]
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[endsect] [/write_ordering_data]
+
+[endsect] [/acid_write_ordering]
+
+[endsect] [/ end of section Design Rationale]
diff --git a/attic/doc/disqus_comments.html b/attic/doc/disqus_comments.html
new file mode 100644
index 00000000..9f13aae6
--- /dev/null
+++ b/attic/doc/disqus_comments.html
@@ -0,0 +1,42 @@
+<div><a name="comments" /><div id="disqus_thread"></div>
+<script type="text/javascript">
+<![CDATA[
+// Get the hX element with class "title"
+var titles = document.getElementsByClassName("title");
+for (var n = 0; n < titles.length; n++) {
+ var title = titles[n];
+ if(title.lastElementChild.nodeName!="a")
+ {
+ var a_elem = document.createElement("a");
+ a_elem.setAttribute("href", "#comments");
+ var span_elem = document.createElement("span");
+ span_elem.setAttribute("class", "disqus-comment-count");
+ span_elem.setAttribute("data-disqus-identifier", disqus_identifier);
+ var text_node = document.createTextNode("Comments");
+ span_elem.appendChild(text_node);
+ a_elem.appendChild(span_elem);
+ title.parentElement.appendChild(a_elem);
+ title.setAttribute("style", "clear: both; display: inline-block;")
+ }
+}
+ /* * * CONFIGURATION VARIABLES * * */
+ var disqus_shortname = 'boostafio';
+
+ /* * * DON'T EDIT BELOW THIS LINE * * */
+ (function () {
+ var s = document.createElement('script'); s.async = true;
+ s.type = 'text/javascript';
+ s.src = 'https://' + disqus_shortname + '.disqus.com/count.js';
+ (document.getElementsByTagName('HEAD')[0] || document.getElementsByTagName('BODY')[0]).appendChild(s);
+ }());
+
+ /* * * DON'T EDIT BELOW THIS LINE * * */
+ (function() {
+ var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
+ dsq.src = 'https://' + disqus_shortname + '.disqus.com/embed.js';
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
+ })();
+]]>
+</script>
+<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript" rel="nofollow">comments powered by Disqus.</a></noscript>
+</div> \ No newline at end of file
diff --git a/attic/doc/disqus_identifiers/async_close.html b/attic/doc/disqus_identifiers/async_close.html
new file mode 100644
index 00000000..b2c93318
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_close.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_close';
+var disqus_title = 'Boost.AFIO async_close';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_dir_2_absolute.html b/attic/doc/disqus_identifiers/async_dir_2_absolute.html
new file mode 100644
index 00000000..642d49e5
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_dir_2_absolute.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_dir_2_absolute';
+var disqus_title = 'Boost.AFIO async_dir (absolute)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_dir_3_relative.html b/attic/doc/disqus_identifiers/async_dir_3_relative.html
new file mode 100644
index 00000000..c920b147
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_dir_3_relative.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_dir_3_relative';
+var disqus_title = 'Boost.AFIO async_dir (relative)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_enumerate_6_glob_first.html b/attic/doc/disqus_identifiers/async_enumerate_6_glob_first.html
new file mode 100644
index 00000000..cb5b6928
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_enumerate_6_glob_first.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_enumerate_6_glob_first';
+var disqus_title = 'Boost.AFIO async_enumerate (glob first)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_enumerate_6_maxitems_first.html b/attic/doc/disqus_identifiers/async_enumerate_6_maxitems_first.html
new file mode 100644
index 00000000..1780f687
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_enumerate_6_maxitems_first.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_enumerate_6_maxitems_first';
+var disqus_title = 'Boost.AFIO async_enumerate (maxitems first)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_enumerate_6_metadata_first.html b/attic/doc/disqus_identifiers/async_enumerate_6_metadata_first.html
new file mode 100644
index 00000000..25389f03
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_enumerate_6_metadata_first.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_enumerate_6_metadata_first';
+var disqus_title = 'Boost.AFIO async_enumerate (metadata first)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_extents.html b/attic/doc/disqus_identifiers/async_extents.html
new file mode 100644
index 00000000..58948b24
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_extents.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_extents';
+var disqus_title = 'Boost.AFIO async_extents';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_file_2_absolute.html b/attic/doc/disqus_identifiers/async_file_2_absolute.html
new file mode 100644
index 00000000..fc7fa7b5
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_file_2_absolute.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_file_2_absolute';
+var disqus_title = 'Boost.AFIO async_file (absolute)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_file_3_relative.html b/attic/doc/disqus_identifiers/async_file_3_relative.html
new file mode 100644
index 00000000..022b80e4
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_file_3_relative.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_file_3_relative';
+var disqus_title = 'Boost.AFIO async_file (relative)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_op_flags.html b/attic/doc/disqus_identifiers/async_op_flags.html
new file mode 100644
index 00000000..87162f3f
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_op_flags.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_op_flags';
+var disqus_title = 'Boost.AFIO async_op_flags';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_read_3_length_deducing.html b/attic/doc/disqus_identifiers/async_read_3_length_deducing.html
new file mode 100644
index 00000000..6e212146
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_read_3_length_deducing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_read_3_length_deducing';
+var disqus_title = 'Boost.AFIO async_read (length deducing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_read_4_length_specifying.html b/attic/doc/disqus_identifiers/async_read_4_length_specifying.html
new file mode 100644
index 00000000..b928bda4
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_read_4_length_specifying.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_read_4_length_specifying';
+var disqus_title = 'Boost.AFIO async_read (length specifying)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_rmdir_2_absolute.html b/attic/doc/disqus_identifiers/async_rmdir_2_absolute.html
new file mode 100644
index 00000000..fef4b17c
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_rmdir_2_absolute.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_rmdir_2_absolute';
+var disqus_title = 'Boost.AFIO async_rmdir (absolute)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_rmdir_3_relative.html b/attic/doc/disqus_identifiers/async_rmdir_3_relative.html
new file mode 100644
index 00000000..fa4a2fe3
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_rmdir_3_relative.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_rmdir_3_relative';
+var disqus_title = 'Boost.AFIO async_rmdir (relative)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_rmfile_2_absolute.html b/attic/doc/disqus_identifiers/async_rmfile_2_absolute.html
new file mode 100644
index 00000000..28813118
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_rmfile_2_absolute.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_rmfile_2_absolute';
+var disqus_title = 'Boost.AFIO async_rmfile (absolute)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_rmfile_3_relative.html b/attic/doc/disqus_identifiers/async_rmfile_3_relative.html
new file mode 100644
index 00000000..6f59d726
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_rmfile_3_relative.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_rmfile_3_relative';
+var disqus_title = 'Boost.AFIO async_rmfile (relative)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_rmsymlink_2_absolute.html b/attic/doc/disqus_identifiers/async_rmsymlink_2_absolute.html
new file mode 100644
index 00000000..360c2e10
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_rmsymlink_2_absolute.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_rmsymlink_2_absolute';
+var disqus_title = 'Boost.AFIO async_rmsymlink (absolute)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_rmsymlink_3_relative.html b/attic/doc/disqus_identifiers/async_rmsymlink_3_relative.html
new file mode 100644
index 00000000..9c61dc3b
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_rmsymlink_3_relative.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_rmsymlink_3_relative';
+var disqus_title = 'Boost.AFIO async_rmsymlink (relative)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_statfs.html b/attic/doc/disqus_identifiers/async_statfs.html
new file mode 100644
index 00000000..1f2216ba
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_statfs.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_statfs';
+var disqus_title = 'Boost.AFIO async_statfs';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_symlink_3_absolute.html b/attic/doc/disqus_identifiers/async_symlink_3_absolute.html
new file mode 100644
index 00000000..71a1616e
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_symlink_3_absolute.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_symlink_3_absolute';
+var disqus_title = 'Boost.AFIO async_symlink (absolute)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_symlink_4_relative.html b/attic/doc/disqus_identifiers/async_symlink_4_relative.html
new file mode 100644
index 00000000..0aa157a0
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_symlink_4_relative.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_symlink_4_relative';
+var disqus_title = 'Boost.AFIO async_symlink (relative)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_sync.html b/attic/doc/disqus_identifiers/async_sync.html
new file mode 100644
index 00000000..c8b864bf
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_sync.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_sync';
+var disqus_title = 'Boost.AFIO async_sync';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_truncate.html b/attic/doc/disqus_identifiers/async_truncate.html
new file mode 100644
index 00000000..f6d3e379
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_truncate.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_truncate';
+var disqus_title = 'Boost.AFIO async_truncate';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_write_3_length_deducing.html b/attic/doc/disqus_identifiers/async_write_3_length_deducing.html
new file mode 100644
index 00000000..c746931d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_write_3_length_deducing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_write_3_length_deducing';
+var disqus_title = 'Boost.AFIO async_write (length deducing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_write_4_length_specifying.html b/attic/doc/disqus_identifiers/async_write_4_length_specifying.html
new file mode 100644
index 00000000..aaf1dfe3
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_write_4_length_specifying.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_write_4_length_specifying';
+var disqus_title = 'Boost.AFIO async_write (length specifying)';
+</script>
diff --git a/attic/doc/disqus_identifiers/async_zero.html b/attic/doc/disqus_identifiers/async_zero.html
new file mode 100644
index 00000000..ac2fdbf3
--- /dev/null
+++ b/attic/doc/disqus_identifiers/async_zero.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'async_zero';
+var disqus_title = 'Boost.AFIO async_zero';
+</script>
diff --git a/attic/doc/disqus_identifiers/atomic_logging.html b/attic/doc/disqus_identifiers/atomic_logging.html
new file mode 100644
index 00000000..fabd3186
--- /dev/null
+++ b/attic/doc/disqus_identifiers/atomic_logging.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'atomic_logging';
+var disqus_title = 'Boost.AFIO Achieving atomicity on the filing system';
+</script>
diff --git a/attic/doc/disqus_identifiers/atomic_relink.html b/attic/doc/disqus_identifiers/atomic_relink.html
new file mode 100644
index 00000000..baff8655
--- /dev/null
+++ b/attic/doc/disqus_identifiers/atomic_relink.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'atomic_relink';
+var disqus_title = 'Boost.AFIO atomic_relink';
+</script>
diff --git a/attic/doc/disqus_identifiers/boost_afio_validate_inputs.html b/attic/doc/disqus_identifiers/boost_afio_validate_inputs.html
new file mode 100644
index 00000000..04b38e87
--- /dev/null
+++ b/attic/doc/disqus_identifiers/boost_afio_validate_inputs.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'boost_afio_validate_inputs';
+var disqus_title = 'Boost.AFIO BOOST_AFIO_VALIDATE_INPUTS';
+</script>
diff --git a/attic/doc/disqus_identifiers/close_1_batch.html b/attic/doc/disqus_identifiers/close_1_batch.html
new file mode 100644
index 00000000..16966b7d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/close_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'close_1_batch';
+var disqus_title = 'Boost.AFIO close (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/close_1_throwing.html b/attic/doc/disqus_identifiers/close_1_throwing.html
new file mode 100644
index 00000000..3b5805c3
--- /dev/null
+++ b/attic/doc/disqus_identifiers/close_1_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'close_1_throwing';
+var disqus_title = 'Boost.AFIO close (throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/close_2_non_throwing.html b/attic/doc/disqus_identifiers/close_2_non_throwing.html
new file mode 100644
index 00000000..f54a7f4f
--- /dev/null
+++ b/attic/doc/disqus_identifiers/close_2_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'close_2_non_throwing';
+var disqus_title = 'Boost.AFIO close (non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/compilation.html b/attic/doc/disqus_identifiers/compilation.html
new file mode 100644
index 00000000..ba4f9c05
--- /dev/null
+++ b/attic/doc/disqus_identifiers/compilation.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'compilation';
+var disqus_title = 'Boost.AFIO Compilation';
+</script>
diff --git a/attic/doc/disqus_identifiers/complete_async_op_2_errored.html b/attic/doc/disqus_identifiers/complete_async_op_2_errored.html
new file mode 100644
index 00000000..62eb6a5f
--- /dev/null
+++ b/attic/doc/disqus_identifiers/complete_async_op_2_errored.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'complete_async_op_2_errored';
+var disqus_title = 'Boost.AFIO complete_async_op (errored)';
+</script>
diff --git a/attic/doc/disqus_identifiers/complete_async_op_3_normal.html b/attic/doc/disqus_identifiers/complete_async_op_3_normal.html
new file mode 100644
index 00000000..3105f198
--- /dev/null
+++ b/attic/doc/disqus_identifiers/complete_async_op_3_normal.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'complete_async_op_3_normal';
+var disqus_title = 'Boost.AFIO complete_async_op (normal)';
+</script>
diff --git a/attic/doc/disqus_identifiers/current_dispatcher.html b/attic/doc/disqus_identifiers/current_dispatcher.html
new file mode 100644
index 00000000..db0db905
--- /dev/null
+++ b/attic/doc/disqus_identifiers/current_dispatcher.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'current_dispatcher';
+var disqus_title = 'Boost.AFIO current_dispatcher';
+</script>
diff --git a/attic/doc/disqus_identifiers/current_dispatcher_guard.html b/attic/doc/disqus_identifiers/current_dispatcher_guard.html
new file mode 100644
index 00000000..616ba439
--- /dev/null
+++ b/attic/doc/disqus_identifiers/current_dispatcher_guard.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'current_dispatcher_guard';
+var disqus_title = 'Boost.AFIO current_dispatcher_guard';
+</script>
diff --git a/attic/doc/disqus_identifiers/depends.html b/attic/doc/disqus_identifiers/depends.html
new file mode 100644
index 00000000..38cde85b
--- /dev/null
+++ b/attic/doc/disqus_identifiers/depends.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'depends';
+var disqus_title = 'Boost.AFIO depends';
+</script>
diff --git a/attic/doc/disqus_identifiers/design_rationale.html b/attic/doc/disqus_identifiers/design_rationale.html
new file mode 100644
index 00000000..86a3c735
--- /dev/null
+++ b/attic/doc/disqus_identifiers/design_rationale.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'design_rationale';
+var disqus_title = 'Boost.AFIO Design Introduction and Rationale';
+</script>
diff --git a/attic/doc/disqus_identifiers/dir_1_batch.html b/attic/doc/disqus_identifiers/dir_1_batch.html
new file mode 100644
index 00000000..a3233b3a
--- /dev/null
+++ b/attic/doc/disqus_identifiers/dir_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'dir_1_batch';
+var disqus_title = 'Boost.AFIO dir (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/dir_2_absolute_throwing.html b/attic/doc/disqus_identifiers/dir_2_absolute_throwing.html
new file mode 100644
index 00000000..f47d4d8f
--- /dev/null
+++ b/attic/doc/disqus_identifiers/dir_2_absolute_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'dir_2_absolute_throwing';
+var disqus_title = 'Boost.AFIO dir (absolute throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/dir_3_absolute_non_throwing.html b/attic/doc/disqus_identifiers/dir_3_absolute_non_throwing.html
new file mode 100644
index 00000000..a6412efd
--- /dev/null
+++ b/attic/doc/disqus_identifiers/dir_3_absolute_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'dir_3_absolute_non_throwing';
+var disqus_title = 'Boost.AFIO dir (absolute non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/dir_3_relative_throwing.html b/attic/doc/disqus_identifiers/dir_3_relative_throwing.html
new file mode 100644
index 00000000..c1b7ebfd
--- /dev/null
+++ b/attic/doc/disqus_identifiers/dir_3_relative_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'dir_3_relative_throwing';
+var disqus_title = 'Boost.AFIO dir (relative throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/dir_4_relative_non_throwing.html b/attic/doc/disqus_identifiers/dir_4_relative_non_throwing.html
new file mode 100644
index 00000000..d344ce32
--- /dev/null
+++ b/attic/doc/disqus_identifiers/dir_4_relative_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'dir_4_relative_non_throwing';
+var disqus_title = 'Boost.AFIO dir (relative non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/directory_entry.html b/attic/doc/disqus_identifiers/directory_entry.html
new file mode 100644
index 00000000..3296f02b
--- /dev/null
+++ b/attic/doc/disqus_identifiers/directory_entry.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'directory_entry';
+var disqus_title = 'Boost.AFIO directory_entry';
+</script>
diff --git a/attic/doc/disqus_identifiers/directory_entry_hash.html b/attic/doc/disqus_identifiers/directory_entry_hash.html
new file mode 100644
index 00000000..3648f329
--- /dev/null
+++ b/attic/doc/disqus_identifiers/directory_entry_hash.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'directory_entry_hash';
+var disqus_title = 'Boost.AFIO directory_entry_hash';
+</script>
diff --git a/attic/doc/disqus_identifiers/direntry.html b/attic/doc/disqus_identifiers/direntry.html
new file mode 100644
index 00000000..2de1ad6e
--- /dev/null
+++ b/attic/doc/disqus_identifiers/direntry.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'direntry';
+var disqus_title = 'Boost.AFIO direntry';
+</script>
diff --git a/attic/doc/disqus_identifiers/dispatcher.html b/attic/doc/disqus_identifiers/dispatcher.html
new file mode 100644
index 00000000..857985c1
--- /dev/null
+++ b/attic/doc/disqus_identifiers/dispatcher.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'dispatcher';
+var disqus_title = 'Boost.AFIO dispatcher';
+</script>
diff --git a/attic/doc/disqus_identifiers/enqueued_task.html b/attic/doc/disqus_identifiers/enqueued_task.html
new file mode 100644
index 00000000..137233d7
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enqueued_task.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enqueued_task';
+var disqus_title = 'Boost.AFIO enqueued_task';
+</script>
diff --git a/attic/doc/disqus_identifiers/enqueued_task_r___.html b/attic/doc/disqus_identifiers/enqueued_task_r___.html
new file mode 100644
index 00000000..40a724a8
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enqueued_task_r___.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enqueued_task_r___';
+var disqus_title = 'Boost.AFIO enqueued_task&lt; R()&gt;';
+</script>
diff --git a/attic/doc/disqus_identifiers/enqueued_task_void___.html b/attic/doc/disqus_identifiers/enqueued_task_void___.html
new file mode 100644
index 00000000..97a496c0
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enqueued_task_void___.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enqueued_task_void___';
+var disqus_title = 'Boost.AFIO enqueued_task&lt; void()&gt;';
+</script>
diff --git a/attic/doc/disqus_identifiers/enumerate_1_batch.html b/attic/doc/disqus_identifiers/enumerate_1_batch.html
new file mode 100644
index 00000000..aa429d0c
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enumerate_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enumerate_1_batch';
+var disqus_title = 'Boost.AFIO enumerate (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/enumerate_6_glob_first_throwing.html b/attic/doc/disqus_identifiers/enumerate_6_glob_first_throwing.html
new file mode 100644
index 00000000..eb6f9df9
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enumerate_6_glob_first_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enumerate_6_glob_first_throwing';
+var disqus_title = 'Boost.AFIO enumerate (glob first throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/enumerate_6_max_items_first_throwing.html b/attic/doc/disqus_identifiers/enumerate_6_max_items_first_throwing.html
new file mode 100644
index 00000000..f0a922bf
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enumerate_6_max_items_first_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enumerate_6_max_items_first_throwing';
+var disqus_title = 'Boost.AFIO enumerate (max items first throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/enumerate_6_metadata_first_throwing.html b/attic/doc/disqus_identifiers/enumerate_6_metadata_first_throwing.html
new file mode 100644
index 00000000..e46c7492
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enumerate_6_metadata_first_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enumerate_6_metadata_first_throwing';
+var disqus_title = 'Boost.AFIO enumerate (metadata first throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/enumerate_7_glob_first_non_throwing.html b/attic/doc/disqus_identifiers/enumerate_7_glob_first_non_throwing.html
new file mode 100644
index 00000000..d91c7de6
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enumerate_7_glob_first_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enumerate_7_glob_first_non_throwing';
+var disqus_title = 'Boost.AFIO enumerate (glob first non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/enumerate_7_max_items_first_non_throwing.html b/attic/doc/disqus_identifiers/enumerate_7_max_items_first_non_throwing.html
new file mode 100644
index 00000000..e3ac326f
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enumerate_7_max_items_first_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enumerate_7_max_items_first_non_throwing';
+var disqus_title = 'Boost.AFIO enumerate (max items first non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/enumerate_7_metadata_first_non_throwing.html b/attic/doc/disqus_identifiers/enumerate_7_metadata_first_non_throwing.html
new file mode 100644
index 00000000..b29b2662
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enumerate_7_metadata_first_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enumerate_7_metadata_first_non_throwing';
+var disqus_title = 'Boost.AFIO enumerate (metadata first non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/enumerate_req.html b/attic/doc/disqus_identifiers/enumerate_req.html
new file mode 100644
index 00000000..83ba5797
--- /dev/null
+++ b/attic/doc/disqus_identifiers/enumerate_req.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'enumerate_req';
+var disqus_title = 'Boost.AFIO enumerate_req';
+</script>
diff --git a/attic/doc/disqus_identifiers/extents_1_batch.html b/attic/doc/disqus_identifiers/extents_1_batch.html
new file mode 100644
index 00000000..7b65be4f
--- /dev/null
+++ b/attic/doc/disqus_identifiers/extents_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'extents_1_batch';
+var disqus_title = 'Boost.AFIO extents (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/extents_1_throwing.html b/attic/doc/disqus_identifiers/extents_1_throwing.html
new file mode 100644
index 00000000..5cc507b1
--- /dev/null
+++ b/attic/doc/disqus_identifiers/extents_1_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'extents_1_throwing';
+var disqus_title = 'Boost.AFIO extents (throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/extents_2_non_throwing.html b/attic/doc/disqus_identifiers/extents_2_non_throwing.html
new file mode 100644
index 00000000..d9ef8a1e
--- /dev/null
+++ b/attic/doc/disqus_identifiers/extents_2_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'extents_2_non_throwing';
+var disqus_title = 'Boost.AFIO extents (non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/file_1_batch.html b/attic/doc/disqus_identifiers/file_1_batch.html
new file mode 100644
index 00000000..16138891
--- /dev/null
+++ b/attic/doc/disqus_identifiers/file_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'file_1_batch';
+var disqus_title = 'Boost.AFIO file (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/file_2_absolute_throwing.html b/attic/doc/disqus_identifiers/file_2_absolute_throwing.html
new file mode 100644
index 00000000..aa29f2a7
--- /dev/null
+++ b/attic/doc/disqus_identifiers/file_2_absolute_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'file_2_absolute_throwing';
+var disqus_title = 'Boost.AFIO file (absolute throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/file_3_absolute_non_throwing.html b/attic/doc/disqus_identifiers/file_3_absolute_non_throwing.html
new file mode 100644
index 00000000..8e2823ef
--- /dev/null
+++ b/attic/doc/disqus_identifiers/file_3_absolute_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'file_3_absolute_non_throwing';
+var disqus_title = 'Boost.AFIO file (absolute non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/file_3_relative_throwing.html b/attic/doc/disqus_identifiers/file_3_relative_throwing.html
new file mode 100644
index 00000000..d33f7ba5
--- /dev/null
+++ b/attic/doc/disqus_identifiers/file_3_relative_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'file_3_relative_throwing';
+var disqus_title = 'Boost.AFIO file (relative throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/file_4_relative_non_throwing.html b/attic/doc/disqus_identifiers/file_4_relative_non_throwing.html
new file mode 100644
index 00000000..87da22c6
--- /dev/null
+++ b/attic/doc/disqus_identifiers/file_4_relative_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'file_4_relative_non_throwing';
+var disqus_title = 'Boost.AFIO file (relative non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/file_buffer_default_size.html b/attic/doc/disqus_identifiers/file_buffer_default_size.html
new file mode 100644
index 00000000..5b01320d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/file_buffer_default_size.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'file_buffer_default_size';
+var disqus_title = 'Boost.AFIO file_buffer_default_size';
+</script>
diff --git a/attic/doc/disqus_identifiers/file_concat.html b/attic/doc/disqus_identifiers/file_concat.html
new file mode 100644
index 00000000..2734ee7a
--- /dev/null
+++ b/attic/doc/disqus_identifiers/file_concat.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'file_concat';
+var disqus_title = 'Boost.AFIO Concatenating files';
+</script>
diff --git a/attic/doc/disqus_identifiers/file_flags.html b/attic/doc/disqus_identifiers/file_flags.html
new file mode 100644
index 00000000..30ab0cf1
--- /dev/null
+++ b/attic/doc/disqus_identifiers/file_flags.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'file_flags';
+var disqus_title = 'Boost.AFIO file_flags';
+</script>
diff --git a/attic/doc/disqus_identifiers/filesystem_races.html b/attic/doc/disqus_identifiers/filesystem_races.html
new file mode 100644
index 00000000..983c7068
--- /dev/null
+++ b/attic/doc/disqus_identifiers/filesystem_races.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'filesystem_races';
+var disqus_title = 'Boost.AFIO Handling races on the filing system';
+</script>
diff --git a/attic/doc/disqus_identifiers/filter.html b/attic/doc/disqus_identifiers/filter.html
new file mode 100644
index 00000000..a5ee0da8
--- /dev/null
+++ b/attic/doc/disqus_identifiers/filter.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'filter';
+var disqus_title = 'Boost.AFIO filter';
+</script>
diff --git a/attic/doc/disqus_identifiers/from_hex_string.html b/attic/doc/disqus_identifiers/from_hex_string.html
new file mode 100644
index 00000000..62ff2557
--- /dev/null
+++ b/attic/doc/disqus_identifiers/from_hex_string.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'from_hex_string';
+var disqus_title = 'Boost.AFIO from_hex_string';
+</script>
diff --git a/attic/doc/disqus_identifiers/fs_metadata_flags.html b/attic/doc/disqus_identifiers/fs_metadata_flags.html
new file mode 100644
index 00000000..385aeb5b
--- /dev/null
+++ b/attic/doc/disqus_identifiers/fs_metadata_flags.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'fs_metadata_flags';
+var disqus_title = 'Boost.AFIO fs_metadata_flags';
+</script>
diff --git a/attic/doc/disqus_identifiers/future.html b/attic/doc/disqus_identifiers/future.html
new file mode 100644
index 00000000..ff4ec506
--- /dev/null
+++ b/attic/doc/disqus_identifiers/future.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'future';
+var disqus_title = 'Boost.AFIO future';
+</script>
diff --git a/attic/doc/disqus_identifiers/future_void_.html b/attic/doc/disqus_identifiers/future_void_.html
new file mode 100644
index 00000000..9c66b728
--- /dev/null
+++ b/attic/doc/disqus_identifiers/future_void_.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'future_void_';
+var disqus_title = 'Boost.AFIO future&lt; void &gt;';
+</script>
diff --git a/attic/doc/disqus_identifiers/handle.html b/attic/doc/disqus_identifiers/handle.html
new file mode 100644
index 00000000..5b2716de
--- /dev/null
+++ b/attic/doc/disqus_identifiers/handle.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'handle';
+var disqus_title = 'Boost.AFIO handle';
+</script>
diff --git a/attic/doc/disqus_identifiers/handle_mapped_file.html b/attic/doc/disqus_identifiers/handle_mapped_file.html
new file mode 100644
index 00000000..5d15abaa
--- /dev/null
+++ b/attic/doc/disqus_identifiers/handle_mapped_file.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'handle_mapped_file';
+var disqus_title = 'Boost.AFIO handle::mapped_file';
+</script>
diff --git a/attic/doc/disqus_identifiers/hello_world.html b/attic/doc/disqus_identifiers/hello_world.html
new file mode 100644
index 00000000..a1440b38
--- /dev/null
+++ b/attic/doc/disqus_identifiers/hello_world.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'hello_world';
+var disqus_title = 'Boost.AFIO Hello World, asynchronously!';
+</script>
diff --git a/attic/doc/disqus_identifiers/introduction.html b/attic/doc/disqus_identifiers/introduction.html
new file mode 100644
index 00000000..cc7a7d42
--- /dev/null
+++ b/attic/doc/disqus_identifiers/introduction.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'introduction';
+var disqus_title = 'Boost.AFIO Introduction';
+</script>
diff --git a/attic/doc/disqus_identifiers/io_req.html b/attic/doc/disqus_identifiers/io_req.html
new file mode 100644
index 00000000..7e15efc9
--- /dev/null
+++ b/attic/doc/disqus_identifiers/io_req.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'io_req';
+var disqus_title = 'Boost.AFIO io_req';
+</script>
diff --git a/attic/doc/disqus_identifiers/io_req_constt_.html b/attic/doc/disqus_identifiers/io_req_constt_.html
new file mode 100644
index 00000000..fe03917d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/io_req_constt_.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'io_req_constt_';
+var disqus_title = 'Boost.AFIO io_req&lt; const T &gt;';
+</script>
diff --git a/attic/doc/disqus_identifiers/io_req_constvoid_.html b/attic/doc/disqus_identifiers/io_req_constvoid_.html
new file mode 100644
index 00000000..47407ae9
--- /dev/null
+++ b/attic/doc/disqus_identifiers/io_req_constvoid_.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'io_req_constvoid_';
+var disqus_title = 'Boost.AFIO io_req&lt; const void &gt;';
+</script>
diff --git a/attic/doc/disqus_identifiers/io_req_void_.html b/attic/doc/disqus_identifiers/io_req_void_.html
new file mode 100644
index 00000000..00f200d3
--- /dev/null
+++ b/attic/doc/disqus_identifiers/io_req_void_.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'io_req_void_';
+var disqus_title = 'Boost.AFIO io_req&lt; void &gt;';
+</script>
diff --git a/attic/doc/disqus_identifiers/is_future.html b/attic/doc/disqus_identifiers/is_future.html
new file mode 100644
index 00000000..480024da
--- /dev/null
+++ b/attic/doc/disqus_identifiers/is_future.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'is_future';
+var disqus_title = 'Boost.AFIO is_future';
+</script>
diff --git a/attic/doc/disqus_identifiers/is_future_-future_-t-_.html b/attic/doc/disqus_identifiers/is_future_-future_-t-_.html
new file mode 100644
index 00000000..3653e1c8
--- /dev/null
+++ b/attic/doc/disqus_identifiers/is_future_-future_-t-_.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'is_future_-future_-t-_';
+var disqus_title = 'Boost.AFIO &gt; is_future&lt; future&lt; T &gt; &gt;';
+</script>
diff --git a/attic/doc/disqus_identifiers/link.html b/attic/doc/disqus_identifiers/link.html
new file mode 100644
index 00000000..c1c169f1
--- /dev/null
+++ b/attic/doc/disqus_identifiers/link.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'link';
+var disqus_title = 'Boost.AFIO link';
+</script>
diff --git a/attic/doc/disqus_identifiers/lock_req.html b/attic/doc/disqus_identifiers/lock_req.html
new file mode 100644
index 00000000..cb97f9db
--- /dev/null
+++ b/attic/doc/disqus_identifiers/lock_req.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'lock_req';
+var disqus_title = 'Boost.AFIO lock_req';
+</script>
diff --git a/attic/doc/disqus_identifiers/make_dispatcher.html b/attic/doc/disqus_identifiers/make_dispatcher.html
new file mode 100644
index 00000000..62ddcb44
--- /dev/null
+++ b/attic/doc/disqus_identifiers/make_dispatcher.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'make_dispatcher';
+var disqus_title = 'Boost.AFIO make_dispatcher';
+</script>
diff --git a/attic/doc/disqus_identifiers/make_io_req_3_length_deducing.html b/attic/doc/disqus_identifiers/make_io_req_3_length_deducing.html
new file mode 100644
index 00000000..b00e3326
--- /dev/null
+++ b/attic/doc/disqus_identifiers/make_io_req_3_length_deducing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'make_io_req_3_length_deducing';
+var disqus_title = 'Boost.AFIO make_io_req (length deducing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/make_io_req_4_length_specifying.html b/attic/doc/disqus_identifiers/make_io_req_4_length_specifying.html
new file mode 100644
index 00000000..ad6b8e79
--- /dev/null
+++ b/attic/doc/disqus_identifiers/make_io_req_4_length_specifying.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'make_io_req_4_length_specifying';
+var disqus_title = 'Boost.AFIO make_io_req (length specifying)';
+</script>
diff --git a/attic/doc/disqus_identifiers/metadata_flags.html b/attic/doc/disqus_identifiers/metadata_flags.html
new file mode 100644
index 00000000..dafb37b9
--- /dev/null
+++ b/attic/doc/disqus_identifiers/metadata_flags.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'metadata_flags';
+var disqus_title = 'Boost.AFIO metadata_flags';
+</script>
diff --git a/attic/doc/disqus_identifiers/normalise_path.html b/attic/doc/disqus_identifiers/normalise_path.html
new file mode 100644
index 00000000..baaefbda
--- /dev/null
+++ b/attic/doc/disqus_identifiers/normalise_path.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'normalise_path';
+var disqus_title = 'Boost.AFIO normalise_path';
+</script>
diff --git a/attic/doc/disqus_identifiers/open_states.html b/attic/doc/disqus_identifiers/open_states.html
new file mode 100644
index 00000000..f841a7f2
--- /dev/null
+++ b/attic/doc/disqus_identifiers/open_states.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'open_states';
+var disqus_title = 'Boost.AFIO open_states';
+</script>
diff --git a/attic/doc/disqus_identifiers/overview.html b/attic/doc/disqus_identifiers/overview.html
new file mode 100644
index 00000000..b9d818d8
--- /dev/null
+++ b/attic/doc/disqus_identifiers/overview.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'overview';
+var disqus_title = 'Boost.AFIO Single page cheat sheet';
+</script>
diff --git a/attic/doc/disqus_identifiers/page_sizes.html b/attic/doc/disqus_identifiers/page_sizes.html
new file mode 100644
index 00000000..5844ce0a
--- /dev/null
+++ b/attic/doc/disqus_identifiers/page_sizes.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'page_sizes';
+var disqus_title = 'Boost.AFIO page_sizes';
+</script>
diff --git a/attic/doc/disqus_identifiers/path.html b/attic/doc/disqus_identifiers/path.html
new file mode 100644
index 00000000..45103272
--- /dev/null
+++ b/attic/doc/disqus_identifiers/path.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'path';
+var disqus_title = 'Boost.AFIO path';
+</script>
diff --git a/attic/doc/disqus_identifiers/path_direct.html b/attic/doc/disqus_identifiers/path_direct.html
new file mode 100644
index 00000000..ff27a444
--- /dev/null
+++ b/attic/doc/disqus_identifiers/path_direct.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'path_direct';
+var disqus_title = 'Boost.AFIO path::direct';
+</script>
diff --git a/attic/doc/disqus_identifiers/path_hash.html b/attic/doc/disqus_identifiers/path_hash.html
new file mode 100644
index 00000000..e8b57dbb
--- /dev/null
+++ b/attic/doc/disqus_identifiers/path_hash.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'path_hash';
+var disqus_title = 'Boost.AFIO path_hash';
+</script>
diff --git a/attic/doc/disqus_identifiers/path_make_absolute.html b/attic/doc/disqus_identifiers/path_make_absolute.html
new file mode 100644
index 00000000..5dbed920
--- /dev/null
+++ b/attic/doc/disqus_identifiers/path_make_absolute.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'path_make_absolute';
+var disqus_title = 'Boost.AFIO path::make_absolute';
+</script>
diff --git a/attic/doc/disqus_identifiers/path_req.html b/attic/doc/disqus_identifiers/path_req.html
new file mode 100644
index 00000000..eba6159d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/path_req.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'path_req';
+var disqus_title = 'Boost.AFIO path_req';
+</script>
diff --git a/attic/doc/disqus_identifiers/path_req_absolute.html b/attic/doc/disqus_identifiers/path_req_absolute.html
new file mode 100644
index 00000000..6874d4f2
--- /dev/null
+++ b/attic/doc/disqus_identifiers/path_req_absolute.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'path_req_absolute';
+var disqus_title = 'Boost.AFIO path_req::absolute';
+</script>
diff --git a/attic/doc/disqus_identifiers/path_req_relative.html b/attic/doc/disqus_identifiers/path_req_relative.html
new file mode 100644
index 00000000..3bab0433
--- /dev/null
+++ b/attic/doc/disqus_identifiers/path_req_relative.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'path_req_relative';
+var disqus_title = 'Boost.AFIO path_req::relative';
+</script>
diff --git a/attic/doc/disqus_identifiers/process_threadpool.html b/attic/doc/disqus_identifiers/process_threadpool.html
new file mode 100644
index 00000000..0a47d8cb
--- /dev/null
+++ b/attic/doc/disqus_identifiers/process_threadpool.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'process_threadpool';
+var disqus_title = 'Boost.AFIO process_threadpool';
+</script>
diff --git a/attic/doc/disqus_identifiers/random_fill.html b/attic/doc/disqus_identifiers/random_fill.html
new file mode 100644
index 00000000..cb222f67
--- /dev/null
+++ b/attic/doc/disqus_identifiers/random_fill.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'random_fill';
+var disqus_title = 'Boost.AFIO random_fill';
+</script>
diff --git a/attic/doc/disqus_identifiers/random_string.html b/attic/doc/disqus_identifiers/random_string.html
new file mode 100644
index 00000000..5ec46573
--- /dev/null
+++ b/attic/doc/disqus_identifiers/random_string.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'random_string';
+var disqus_title = 'Boost.AFIO random_string';
+</script>
diff --git a/attic/doc/disqus_identifiers/read_1_batch.html b/attic/doc/disqus_identifiers/read_1_batch.html
new file mode 100644
index 00000000..513ad269
--- /dev/null
+++ b/attic/doc/disqus_identifiers/read_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'read_1_batch';
+var disqus_title = 'Boost.AFIO read (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/read_3_length_deducing_throwing.html b/attic/doc/disqus_identifiers/read_3_length_deducing_throwing.html
new file mode 100644
index 00000000..ed90b51d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/read_3_length_deducing_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'read_3_length_deducing_throwing';
+var disqus_title = 'Boost.AFIO read (length deducing throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/read_4_length_deducing_non_throwing.html b/attic/doc/disqus_identifiers/read_4_length_deducing_non_throwing.html
new file mode 100644
index 00000000..95d394a2
--- /dev/null
+++ b/attic/doc/disqus_identifiers/read_4_length_deducing_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'read_4_length_deducing_non_throwing';
+var disqus_title = 'Boost.AFIO read (length deducing non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/read_4_length_specifying_throwing.html b/attic/doc/disqus_identifiers/read_4_length_specifying_throwing.html
new file mode 100644
index 00000000..58acd552
--- /dev/null
+++ b/attic/doc/disqus_identifiers/read_4_length_specifying_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'read_4_length_specifying_throwing';
+var disqus_title = 'Boost.AFIO read (length specifying throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/read_5_length_specifying_non_throwing.html b/attic/doc/disqus_identifiers/read_5_length_specifying_non_throwing.html
new file mode 100644
index 00000000..ae41f5ba
--- /dev/null
+++ b/attic/doc/disqus_identifiers/read_5_length_specifying_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'read_5_length_specifying_non_throwing';
+var disqus_title = 'Boost.AFIO read (length specifying non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmdir_1_batch.html b/attic/doc/disqus_identifiers/rmdir_1_batch.html
new file mode 100644
index 00000000..7358e4a6
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmdir_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmdir_1_batch';
+var disqus_title = 'Boost.AFIO rmdir (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmdir_2_absolute_throwing.html b/attic/doc/disqus_identifiers/rmdir_2_absolute_throwing.html
new file mode 100644
index 00000000..2851d094
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmdir_2_absolute_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmdir_2_absolute_throwing';
+var disqus_title = 'Boost.AFIO rmdir (absolute throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmdir_3_absolute_non_throwing.html b/attic/doc/disqus_identifiers/rmdir_3_absolute_non_throwing.html
new file mode 100644
index 00000000..2733111d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmdir_3_absolute_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmdir_3_absolute_non_throwing';
+var disqus_title = 'Boost.AFIO rmdir (absolute non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmdir_3_relative_throwing.html b/attic/doc/disqus_identifiers/rmdir_3_relative_throwing.html
new file mode 100644
index 00000000..9d4f8ca7
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmdir_3_relative_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmdir_3_relative_throwing';
+var disqus_title = 'Boost.AFIO rmdir (relative throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmdir_4_relative_non_throwing.html b/attic/doc/disqus_identifiers/rmdir_4_relative_non_throwing.html
new file mode 100644
index 00000000..2df7bdf9
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmdir_4_relative_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmdir_4_relative_non_throwing';
+var disqus_title = 'Boost.AFIO rmdir (relative non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmfile_1_batch.html b/attic/doc/disqus_identifiers/rmfile_1_batch.html
new file mode 100644
index 00000000..9b7fa611
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmfile_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmfile_1_batch';
+var disqus_title = 'Boost.AFIO rmfile (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmfile_2_absolute_throwing.html b/attic/doc/disqus_identifiers/rmfile_2_absolute_throwing.html
new file mode 100644
index 00000000..adc16a28
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmfile_2_absolute_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmfile_2_absolute_throwing';
+var disqus_title = 'Boost.AFIO rmfile (absolute throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmfile_3_absolute_non_throwing.html b/attic/doc/disqus_identifiers/rmfile_3_absolute_non_throwing.html
new file mode 100644
index 00000000..1d3b9e8a
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmfile_3_absolute_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmfile_3_absolute_non_throwing';
+var disqus_title = 'Boost.AFIO rmfile (absolute non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmfile_3_relative_throwing.html b/attic/doc/disqus_identifiers/rmfile_3_relative_throwing.html
new file mode 100644
index 00000000..6ca0d375
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmfile_3_relative_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmfile_3_relative_throwing';
+var disqus_title = 'Boost.AFIO rmfile (relative throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmfile_4_relative_non_throwing.html b/attic/doc/disqus_identifiers/rmfile_4_relative_non_throwing.html
new file mode 100644
index 00000000..00b54653
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmfile_4_relative_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmfile_4_relative_non_throwing';
+var disqus_title = 'Boost.AFIO rmfile (relative non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmsymlink_1_batch.html b/attic/doc/disqus_identifiers/rmsymlink_1_batch.html
new file mode 100644
index 00000000..538bf72d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmsymlink_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmsymlink_1_batch';
+var disqus_title = 'Boost.AFIO rmsymlink (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmsymlink_2_absolute_throwing.html b/attic/doc/disqus_identifiers/rmsymlink_2_absolute_throwing.html
new file mode 100644
index 00000000..e1ca2a8b
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmsymlink_2_absolute_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmsymlink_2_absolute_throwing';
+var disqus_title = 'Boost.AFIO rmsymlink (absolute throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmsymlink_3_absolute_non_throwing.html b/attic/doc/disqus_identifiers/rmsymlink_3_absolute_non_throwing.html
new file mode 100644
index 00000000..6af15ff3
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmsymlink_3_absolute_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmsymlink_3_absolute_non_throwing';
+var disqus_title = 'Boost.AFIO rmsymlink (absolute non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmsymlink_3_relative_throwing.html b/attic/doc/disqus_identifiers/rmsymlink_3_relative_throwing.html
new file mode 100644
index 00000000..1bcd46b5
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmsymlink_3_relative_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmsymlink_3_relative_throwing';
+var disqus_title = 'Boost.AFIO rmsymlink (relative throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/rmsymlink_4_relative_non_throwing.html b/attic/doc/disqus_identifiers/rmsymlink_4_relative_non_throwing.html
new file mode 100644
index 00000000..f9b138a4
--- /dev/null
+++ b/attic/doc/disqus_identifiers/rmsymlink_4_relative_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'rmsymlink_4_relative_non_throwing';
+var disqus_title = 'Boost.AFIO rmsymlink (relative non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/so_what.html b/attic/doc/disqus_identifiers/so_what.html
new file mode 100644
index 00000000..e21d7d43
--- /dev/null
+++ b/attic/doc/disqus_identifiers/so_what.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'so_what';
+var disqus_title = 'Boost.AFIO What benefit does asynchronous file i/o bring me? A demonstration of AFIO's power';
+</script>
diff --git a/attic/doc/disqus_identifiers/stat_t.html b/attic/doc/disqus_identifiers/stat_t.html
new file mode 100644
index 00000000..df51c7df
--- /dev/null
+++ b/attic/doc/disqus_identifiers/stat_t.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'stat_t';
+var disqus_title = 'Boost.AFIO stat_t';
+</script>
diff --git a/attic/doc/disqus_identifiers/statfs_2_batch.html b/attic/doc/disqus_identifiers/statfs_2_batch.html
new file mode 100644
index 00000000..01ece709
--- /dev/null
+++ b/attic/doc/disqus_identifiers/statfs_2_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'statfs_2_batch';
+var disqus_title = 'Boost.AFIO statfs (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/statfs_2_throwing.html b/attic/doc/disqus_identifiers/statfs_2_throwing.html
new file mode 100644
index 00000000..2fabc77d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/statfs_2_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'statfs_2_throwing';
+var disqus_title = 'Boost.AFIO statfs (throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/statfs_3_nonthrowing.html b/attic/doc/disqus_identifiers/statfs_3_nonthrowing.html
new file mode 100644
index 00000000..1f28a9e1
--- /dev/null
+++ b/attic/doc/disqus_identifiers/statfs_3_nonthrowing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'statfs_3_nonthrowing';
+var disqus_title = 'Boost.AFIO statfs (nonthrowing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/statfs_t.html b/attic/doc/disqus_identifiers/statfs_t.html
new file mode 100644
index 00000000..1f3b4903
--- /dev/null
+++ b/attic/doc/disqus_identifiers/statfs_t.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'statfs_t';
+var disqus_title = 'Boost.AFIO statfs_t';
+</script>
diff --git a/attic/doc/disqus_identifiers/statfs_t_f_flags_t.html b/attic/doc/disqus_identifiers/statfs_t_f_flags_t.html
new file mode 100644
index 00000000..62d73567
--- /dev/null
+++ b/attic/doc/disqus_identifiers/statfs_t_f_flags_t.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'statfs_t_f_flags_t';
+var disqus_title = 'Boost.AFIO statfs_t::f_flags_t';
+</script>
diff --git a/attic/doc/disqus_identifiers/std_thread_pool.html b/attic/doc/disqus_identifiers/std_thread_pool.html
new file mode 100644
index 00000000..0dd3883e
--- /dev/null
+++ b/attic/doc/disqus_identifiers/std_thread_pool.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'std_thread_pool';
+var disqus_title = 'Boost.AFIO std_thread_pool';
+</script>
diff --git a/attic/doc/disqus_identifiers/std_thread_pool_worker.html b/attic/doc/disqus_identifiers/std_thread_pool_worker.html
new file mode 100644
index 00000000..c57c71cc
--- /dev/null
+++ b/attic/doc/disqus_identifiers/std_thread_pool_worker.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'std_thread_pool_worker';
+var disqus_title = 'Boost.AFIO std_thread_pool::worker';
+</script>
diff --git a/attic/doc/disqus_identifiers/symlink_2_batch.html b/attic/doc/disqus_identifiers/symlink_2_batch.html
new file mode 100644
index 00000000..b2ead36a
--- /dev/null
+++ b/attic/doc/disqus_identifiers/symlink_2_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'symlink_2_batch';
+var disqus_title = 'Boost.AFIO symlink (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/symlink_3_absolute_throwing.html b/attic/doc/disqus_identifiers/symlink_3_absolute_throwing.html
new file mode 100644
index 00000000..1bb7d36c
--- /dev/null
+++ b/attic/doc/disqus_identifiers/symlink_3_absolute_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'symlink_3_absolute_throwing';
+var disqus_title = 'Boost.AFIO symlink (absolute throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/symlink_4_absolute_non_throwing.html b/attic/doc/disqus_identifiers/symlink_4_absolute_non_throwing.html
new file mode 100644
index 00000000..849bf2e7
--- /dev/null
+++ b/attic/doc/disqus_identifiers/symlink_4_absolute_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'symlink_4_absolute_non_throwing';
+var disqus_title = 'Boost.AFIO symlink (absolute non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/symlink_4_relative_throwing.html b/attic/doc/disqus_identifiers/symlink_4_relative_throwing.html
new file mode 100644
index 00000000..3808178e
--- /dev/null
+++ b/attic/doc/disqus_identifiers/symlink_4_relative_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'symlink_4_relative_throwing';
+var disqus_title = 'Boost.AFIO symlink (relative throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/symlink_5_relative_non_throwing.html b/attic/doc/disqus_identifiers/symlink_5_relative_non_throwing.html
new file mode 100644
index 00000000..01226a6c
--- /dev/null
+++ b/attic/doc/disqus_identifiers/symlink_5_relative_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'symlink_5_relative_non_throwing';
+var disqus_title = 'Boost.AFIO symlink (relative non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/sync_1_batch.html b/attic/doc/disqus_identifiers/sync_1_batch.html
new file mode 100644
index 00000000..aab33809
--- /dev/null
+++ b/attic/doc/disqus_identifiers/sync_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'sync_1_batch';
+var disqus_title = 'Boost.AFIO sync (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/sync_1_throwing.html b/attic/doc/disqus_identifiers/sync_1_throwing.html
new file mode 100644
index 00000000..d1859b97
--- /dev/null
+++ b/attic/doc/disqus_identifiers/sync_1_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'sync_1_throwing';
+var disqus_title = 'Boost.AFIO sync (throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/sync_2_non_throwing.html b/attic/doc/disqus_identifiers/sync_2_non_throwing.html
new file mode 100644
index 00000000..77b73b5a
--- /dev/null
+++ b/attic/doc/disqus_identifiers/sync_2_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'sync_2_non_throwing';
+var disqus_title = 'Boost.AFIO sync (non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/target.html b/attic/doc/disqus_identifiers/target.html
new file mode 100644
index 00000000..366ddba2
--- /dev/null
+++ b/attic/doc/disqus_identifiers/target.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'target';
+var disqus_title = 'Boost.AFIO target';
+</script>
diff --git a/attic/doc/disqus_identifiers/thread_source.html b/attic/doc/disqus_identifiers/thread_source.html
new file mode 100644
index 00000000..b0a765c7
--- /dev/null
+++ b/attic/doc/disqus_identifiers/thread_source.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'thread_source';
+var disqus_title = 'Boost.AFIO thread_source';
+</script>
diff --git a/attic/doc/disqus_identifiers/to_asio_buffers_1_asio_const_buffer.html b/attic/doc/disqus_identifiers/to_asio_buffers_1_asio_const_buffer.html
new file mode 100644
index 00000000..6d429eab
--- /dev/null
+++ b/attic/doc/disqus_identifiers/to_asio_buffers_1_asio_const_buffer.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'to_asio_buffers_1_asio_const_buffer';
+var disqus_title = 'Boost.AFIO to_asio_buffers (asio const_buffer)';
+</script>
diff --git a/attic/doc/disqus_identifiers/to_asio_buffers_1_asio_mutable_buffer.html b/attic/doc/disqus_identifiers/to_asio_buffers_1_asio_mutable_buffer.html
new file mode 100644
index 00000000..4ce97d77
--- /dev/null
+++ b/attic/doc/disqus_identifiers/to_asio_buffers_1_asio_mutable_buffer.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'to_asio_buffers_1_asio_mutable_buffer';
+var disqus_title = 'Boost.AFIO to_asio_buffers (asio mutable_buffer)';
+</script>
diff --git a/attic/doc/disqus_identifiers/to_asio_buffers_1_c_arrays.html b/attic/doc/disqus_identifiers/to_asio_buffers_1_c_arrays.html
new file mode 100644
index 00000000..35618177
--- /dev/null
+++ b/attic/doc/disqus_identifiers/to_asio_buffers_1_c_arrays.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'to_asio_buffers_1_c_arrays';
+var disqus_title = 'Boost.AFIO to_asio_buffers (C arrays)';
+</script>
diff --git a/attic/doc/disqus_identifiers/to_asio_buffers_1_const_c_arrays.html b/attic/doc/disqus_identifiers/to_asio_buffers_1_const_c_arrays.html
new file mode 100644
index 00000000..44513b8e
--- /dev/null
+++ b/attic/doc/disqus_identifiers/to_asio_buffers_1_const_c_arrays.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'to_asio_buffers_1_const_c_arrays';
+var disqus_title = 'Boost.AFIO to_asio_buffers (const C arrays)';
+</script>
diff --git a/attic/doc/disqus_identifiers/to_asio_buffers_1_const_trivial_and_container_types.html b/attic/doc/disqus_identifiers/to_asio_buffers_1_const_trivial_and_container_types.html
new file mode 100644
index 00000000..dab9b115
--- /dev/null
+++ b/attic/doc/disqus_identifiers/to_asio_buffers_1_const_trivial_and_container_types.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'to_asio_buffers_1_const_trivial_and_container_types';
+var disqus_title = 'Boost.AFIO to_asio_buffers (const trivial and container types)';
+</script>
diff --git a/attic/doc/disqus_identifiers/to_asio_buffers_1_trivial_and_container_types.html b/attic/doc/disqus_identifiers/to_asio_buffers_1_trivial_and_container_types.html
new file mode 100644
index 00000000..c94e64fa
--- /dev/null
+++ b/attic/doc/disqus_identifiers/to_asio_buffers_1_trivial_and_container_types.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'to_asio_buffers_1_trivial_and_container_types';
+var disqus_title = 'Boost.AFIO to_asio_buffers (trivial and container types)';
+</script>
diff --git a/attic/doc/disqus_identifiers/to_asio_buffers_2_buffer.html b/attic/doc/disqus_identifiers/to_asio_buffers_2_buffer.html
new file mode 100644
index 00000000..605880a9
--- /dev/null
+++ b/attic/doc/disqus_identifiers/to_asio_buffers_2_buffer.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'to_asio_buffers_2_buffer';
+var disqus_title = 'Boost.AFIO to_asio_buffers (buffer)';
+</script>
diff --git a/attic/doc/disqus_identifiers/to_asio_buffers_2_buffer_of_t.html b/attic/doc/disqus_identifiers/to_asio_buffers_2_buffer_of_t.html
new file mode 100644
index 00000000..98e7ea16
--- /dev/null
+++ b/attic/doc/disqus_identifiers/to_asio_buffers_2_buffer_of_t.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'to_asio_buffers_2_buffer_of_t';
+var disqus_title = 'Boost.AFIO to_asio_buffers (buffer of T)';
+</script>
diff --git a/attic/doc/disqus_identifiers/to_asio_buffers_2_const_buffer_of_t.html b/attic/doc/disqus_identifiers/to_asio_buffers_2_const_buffer_of_t.html
new file mode 100644
index 00000000..15c1c7a5
--- /dev/null
+++ b/attic/doc/disqus_identifiers/to_asio_buffers_2_const_buffer_of_t.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'to_asio_buffers_2_const_buffer_of_t';
+var disqus_title = 'Boost.AFIO to_asio_buffers (const buffer of T)';
+</script>
diff --git a/attic/doc/disqus_identifiers/to_hex_string.html b/attic/doc/disqus_identifiers/to_hex_string.html
new file mode 100644
index 00000000..fb9a740a
--- /dev/null
+++ b/attic/doc/disqus_identifiers/to_hex_string.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'to_hex_string';
+var disqus_title = 'Boost.AFIO to_hex_string';
+</script>
diff --git a/attic/doc/disqus_identifiers/truncate_2_batch.html b/attic/doc/disqus_identifiers/truncate_2_batch.html
new file mode 100644
index 00000000..511723ad
--- /dev/null
+++ b/attic/doc/disqus_identifiers/truncate_2_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'truncate_2_batch';
+var disqus_title = 'Boost.AFIO truncate (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/truncate_2_throwing.html b/attic/doc/disqus_identifiers/truncate_2_throwing.html
new file mode 100644
index 00000000..ef6129d7
--- /dev/null
+++ b/attic/doc/disqus_identifiers/truncate_2_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'truncate_2_throwing';
+var disqus_title = 'Boost.AFIO truncate (throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/truncate_3_non_throwing.html b/attic/doc/disqus_identifiers/truncate_3_non_throwing.html
new file mode 100644
index 00000000..e95d0d40
--- /dev/null
+++ b/attic/doc/disqus_identifiers/truncate_3_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'truncate_3_non_throwing';
+var disqus_title = 'Boost.AFIO truncate (non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/type.html b/attic/doc/disqus_identifiers/type.html
new file mode 100644
index 00000000..d85d8474
--- /dev/null
+++ b/attic/doc/disqus_identifiers/type.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'type';
+var disqus_title = 'Boost.AFIO Type';
+</script>
diff --git a/attic/doc/disqus_identifiers/unlink.html b/attic/doc/disqus_identifiers/unlink.html
new file mode 100644
index 00000000..a4ddfbf7
--- /dev/null
+++ b/attic/doc/disqus_identifiers/unlink.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'unlink';
+var disqus_title = 'Boost.AFIO unlink';
+</script>
diff --git a/attic/doc/disqus_identifiers/utils_page_allocator.html b/attic/doc/disqus_identifiers/utils_page_allocator.html
new file mode 100644
index 00000000..18455b3d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/utils_page_allocator.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'utils_page_allocator';
+var disqus_title = 'Boost.AFIO utils::page_allocator';
+</script>
diff --git a/attic/doc/disqus_identifiers/utils_page_allocator_-void-_.html b/attic/doc/disqus_identifiers/utils_page_allocator_-void-_.html
new file mode 100644
index 00000000..df8e7d4d
--- /dev/null
+++ b/attic/doc/disqus_identifiers/utils_page_allocator_-void-_.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'utils_page_allocator_-void-_';
+var disqus_title = 'Boost.AFIO rebind utils::page_allocator&lt; void &gt;::rebind';
+</script>
diff --git a/attic/doc/disqus_identifiers/utils_page_allocator_rebind.html b/attic/doc/disqus_identifiers/utils_page_allocator_rebind.html
new file mode 100644
index 00000000..8a4cbfdd
--- /dev/null
+++ b/attic/doc/disqus_identifiers/utils_page_allocator_rebind.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'utils_page_allocator_rebind';
+var disqus_title = 'Boost.AFIO utils::page_allocator::rebind';
+</script>
diff --git a/attic/doc/disqus_identifiers/utils_secded_ecc.html b/attic/doc/disqus_identifiers/utils_secded_ecc.html
new file mode 100644
index 00000000..b612cff9
--- /dev/null
+++ b/attic/doc/disqus_identifiers/utils_secded_ecc.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'utils_secded_ecc';
+var disqus_title = 'Boost.AFIO utils::secded_ecc';
+</script>
diff --git a/attic/doc/disqus_identifiers/verify_status.html b/attic/doc/disqus_identifiers/verify_status.html
new file mode 100644
index 00000000..6efb84c6
--- /dev/null
+++ b/attic/doc/disqus_identifiers/verify_status.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'verify_status';
+var disqus_title = 'Boost.AFIO verify_status';
+</script>
diff --git a/attic/doc/disqus_identifiers/write_1_batch.html b/attic/doc/disqus_identifiers/write_1_batch.html
new file mode 100644
index 00000000..278a7a65
--- /dev/null
+++ b/attic/doc/disqus_identifiers/write_1_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'write_1_batch';
+var disqus_title = 'Boost.AFIO write (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/write_3_length_deducing_throwing.html b/attic/doc/disqus_identifiers/write_3_length_deducing_throwing.html
new file mode 100644
index 00000000..e8fc3912
--- /dev/null
+++ b/attic/doc/disqus_identifiers/write_3_length_deducing_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'write_3_length_deducing_throwing';
+var disqus_title = 'Boost.AFIO write (length deducing throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/write_4_length_deducing_non_throwing.html b/attic/doc/disqus_identifiers/write_4_length_deducing_non_throwing.html
new file mode 100644
index 00000000..7fdfec58
--- /dev/null
+++ b/attic/doc/disqus_identifiers/write_4_length_deducing_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'write_4_length_deducing_non_throwing';
+var disqus_title = 'Boost.AFIO write (length deducing non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/write_4_length_specifying_throwing.html b/attic/doc/disqus_identifiers/write_4_length_specifying_throwing.html
new file mode 100644
index 00000000..448aa312
--- /dev/null
+++ b/attic/doc/disqus_identifiers/write_4_length_specifying_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'write_4_length_specifying_throwing';
+var disqus_title = 'Boost.AFIO write (length specifying throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/write_5_length_specifying_non_throwing.html b/attic/doc/disqus_identifiers/write_5_length_specifying_non_throwing.html
new file mode 100644
index 00000000..a88e7e11
--- /dev/null
+++ b/attic/doc/disqus_identifiers/write_5_length_specifying_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'write_5_length_specifying_non_throwing';
+var disqus_title = 'Boost.AFIO write (length specifying non throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/zero_2_batch.html b/attic/doc/disqus_identifiers/zero_2_batch.html
new file mode 100644
index 00000000..90e2ee14
--- /dev/null
+++ b/attic/doc/disqus_identifiers/zero_2_batch.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'zero_2_batch';
+var disqus_title = 'Boost.AFIO zero (batch)';
+</script>
diff --git a/attic/doc/disqus_identifiers/zero_2_throwing.html b/attic/doc/disqus_identifiers/zero_2_throwing.html
new file mode 100644
index 00000000..61b6d700
--- /dev/null
+++ b/attic/doc/disqus_identifiers/zero_2_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'zero_2_throwing';
+var disqus_title = 'Boost.AFIO zero (throwing)';
+</script>
diff --git a/attic/doc/disqus_identifiers/zero_3_non_throwing.html b/attic/doc/disqus_identifiers/zero_3_non_throwing.html
new file mode 100644
index 00000000..faa6d6ae
--- /dev/null
+++ b/attic/doc/disqus_identifiers/zero_3_non_throwing.html
@@ -0,0 +1,4 @@
+<script type="text/javascript">
+var disqus_identifier = 'zero_3_non_throwing';
+var disqus_title = 'Boost.AFIO zero (non throwing)';
+</script>
diff --git a/attic/doc/doxy/Doxyfile b/attic/doc/doxy/Doxyfile
new file mode 100644
index 00000000..5a9ce944
--- /dev/null
+++ b/attic/doc/doxy/Doxyfile
@@ -0,0 +1,248 @@
+# Doxyfile 1.5.5
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING = UTF-8
+PROJECT_NAME = "Boost.AFIO"
+PROJECT_NUMBER =
+OUTPUT_DIRECTORY = ./doxygen_output
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF =
+ALWAYS_DETAILED_SEC = YES
+INLINE_INHERITED_MEMB = YES
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH =
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+QT_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS = YES
+SEPARATE_MEMBER_PAGES = NO
+TAB_SIZE = 8
+
+# The aliases are shortcuts for Doxygen documentation.
+# Within Boost.Geometry they are used in the top section, so for both
+# Doxygen documentation and QuickBook documentation.
+# They avoid repetitions and make the documentation more structured.
+# There are also qbk expressions, which might qbk defines and templates.
+
+# There are sections for
+# \brief* for various brief descriptions
+# \tparam* for various template parameters
+# \param* for various parameters
+# \return* for various return cases
+ALIASES = qbk{1}="\xmlonly <qbk>\1</qbk> \endxmlonly" \
+ qbk{2}="\xmlonly <qbk.\1>\2</qbk.\1> \endxmlonly" \
+ raceguarantees{1}="\xmlonly <qbk>[heading Race Guarantees][raceguarantees \1 ]</qbk> \endxmlonly" \
+ complexity{1}="\xmlonly <qbk>[heading Complexity]\1</qbk> \endxmlonly" \
+ exceptionmodel{1}="\xmlonly <qbk>[heading Exception Model]\1</qbk> \endxmlonly" \
+ exceptionmodelstd="\xmlonly <qbk>[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.</qbk> \endxmlonly" \
+ exceptionmodelfree="\xmlonly <qbk>[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.</qbk> \endxmlonly" \
+ direct_io_note="Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations)." \
+ ntkernelnamespacenote="Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by `afio::path` in NT kernel namespace paths. `normalise_path()` can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage." \
+ deprecate{1}="\xmlonly <qbk>[caution \1]</qbk> \endxmlonly" \
+ constr="Default constructor." \
+ cconstr="Copy constructor." \
+ mconstr="Move constructor." \
+ cassign="Copy assignment." \
+ massign="Move assignment." \
+ io_req1="\brief Constructs an instance. \
+ \param _precondition An optional precondition for this operation \
+ \param v A pointer to memory or reference to object into which to read or write \
+ \param _where The offset at which to transfer" \
+ io_req2="\brief Constructs an instance. \
+ \param _precondition An optional precondition for this operation \
+ \param _buffers A sequence of mutable/const Boost.ASIO buffers to read into/write from \
+ \param _where The offset at which to transfer" \
+ io_req3="\brief Constructs an instance. \
+ \param _precondition An optional precondition for this operation \
+ \param _buffer A mutable/const Boost.ASIO buffer to read into/write from \
+ \param _where The offset at which to transfer" \
+ qexample{1}="\xmlonly <qbk>[heading Example][\1]</qbk> \endxmlonly"
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+OPTIMIZE_FOR_FORTRAN = NO
+OPTIMIZE_OUTPUT_VHDL = NO
+BUILTIN_STL_SUPPORT = YES
+CPP_CLI_SUPPORT = NO
+SIP_SUPPORT = NO
+DISTRIBUTE_GROUP_DOC = NO
+SUBGROUPING = YES
+TYPEDEF_HIDES_STRUCT = NO
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = YES
+EXTRACT_LOCAL_CLASSES = NO
+EXTRACT_LOCAL_METHODS = NO
+EXTRACT_ANON_NSPACES = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = NO
+HIDE_SCOPE_NAMES = NO
+SHOW_INCLUDE_FILES = NO
+INLINE_INFO = NO
+SORT_MEMBER_DOCS = NO
+SORT_BRIEF_DOCS = NO
+SORT_GROUP_NAMES = YES
+SORT_BY_SCOPE_NAME = YES
+GENERATE_TODOLIST = NO
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = NO
+FILE_VERSION_FILTER =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = YES
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = NO
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = ../../include/boost/afio/v2/afio.hpp \
+ ./doxygen_input/pages \
+ ./doxygen_input/groups
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS = *.hpp
+RECURSIVE = NO
+EXCLUDE = ./doxygen_extension_examples.hpp
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS =
+EXCLUDE_SYMBOLS =
+EXAMPLE_PATH = ../../example
+EXAMPLE_PATTERNS = *.cpp
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH = doxygen_input/images
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = YES
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION = NO
+REFERENCES_LINK_SOURCE = NO
+USE_HTAGS = NO
+VERBATIM_HEADERS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+COLS_IN_ALPHA_INDEX = 3
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER = doxygen_input/doxygen_header.html
+HTML_FOOTER = doxygen_input/doxygen_footer.html
+HTML_STYLESHEET =
+GENERATE_HTMLHELP = NO
+GENERATE_DOCSET = NO
+DOCSET_FEEDNAME = "Generated documentation, by Doxygen"
+DOCSET_BUNDLE_ID = org.doxygen.Project
+HTML_DYNAMIC_SECTIONS = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+BINARY_TOC = NO
+TOC_EXPAND = NO
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+TREEVIEW_WIDTH = 250
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = YES
+XML_OUTPUT = xml
+XML_SCHEMA =
+XML_DTD =
+XML_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options turned off
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+GENERATE_MAN = NO
+GENERATE_RTF = NO
+GENERATE_AUTOGEN_DEF = NO
+GENERATE_PERLMOD = NO
+
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = YES
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED = \
+ BOOST_CONCEPT_REQUIRES(x)= \
+ BOOST_CONCEPT_ASSERT(x)= \
+ BOOST_STATIC_ASSERT(x)= \
+ BOOST_AFIO_DECL= \
+ DOXYGEN_SHOULD_SKIP_THIS \
+ DOXYGEN_NO_CLASS_ENUMS \
+ BOOST_NOEXCEPT_OR_NOTHROW=noexcept \
+ BOOST_CONSTEXPR_OR_CONST=constexpr \
+ BOOST_CONSTEXPR=constexpr \
+ BOOST_CXX14_CONSTEXPR=constexpr
+EXPAND_AS_DEFINED = BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD \
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC \
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC \
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC \
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC \
+ BOOST_AFIO_V2_NAMESPACE \
+ BOOST_AFIO_V2_NAMESPACE_BEGIN \
+ BOOST_AFIO_V2_NAMESPACE_END
+SKIP_FUNCTION_MACROS = YES
+
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to the search engine
+#---------------------------------------------------------------------------
+SEARCHENGINE = NO
+
+ALIASES += docs_adopt="This function enables you to adopt third party custom `__afio_handle__` derivatives as ops into the scheduler. Think of it as if you were calling file(), except the op returns the supplied handle and otherwise does nothing."
+ALIASES += docs_dir="Note that if there is already a handle open to the directory requested, that will be returned instead of a new handle unless file_flags::unique_directory_handle is specified. For such handles where available_to_directory_cache() is true, they cannot be explicitly closed either, you must let the reference count reach zero for that to happen.\n\nRelated types: `__afio_path_req__`"
+ALIASES += docs_rmdir="Make sure you read the docs for `__afio_handle__::unlink()` for important caveats.\n\nNote that on operating systems with an unstable `__afio_handle__::path(true)` you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname.\n\nRelated types: `__afio_path_req__`"
+ALIASES += docs_file="Be aware that any files created are by default sparse if supported on the local filing system. On Windows opening any file for writing converts it to sparse. Use file_flags::no_sparse to prevent this on those filing systems which permit it.\n\nRelated types: `__afio_path_req__`"
+ALIASES += docs_rmfile="Note that you can delete files before they are closed on Windows just as with POSIX if and only if all open handles to that file were opened with permission for the file to be deleted (AFIO always sets this). The actual file data will be deleted when the last handle is closed on the system.\n\nMake sure you read the docs for `__afio_handle__::unlink()` for important caveats.\n\nNote that on operating systems with an unstable `__afio_handle__::path(true)` you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname.\n\nRelated types: `__afio_path_req__`"
+ALIASES += docs_symlink="Note that if creating and you don't specify a target, the target for the symlink is the precondition. On Windows directories are symlinked using a reparse point instead of a symlink due to the default lack of the <tt>SeCreateSymbolicLinkPrivilege</tt> for non-Administrative users. On Windows you can open symlinks as a file and so a valid handle is output, whereas on POSIX except for Linux you cannot do this and an invalid handle is output.\n\nRelated types: `__afio_path_req__`"
+ALIASES += docs_rmsymlink="Make sure you read the docs for `__afio_handle__::unlink()` for important caveats.\n\nNote that on operating systems with an unstable `__afio_handle__::path(true)` you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname.\n\nRelated types: `__afio_path_req__`"
+ALIASES += docs_sync="It goes without saying that this call can take very significant amounts of time to complete!"
+ALIASES += docs_zero="Most extent based filing systems provide an optimised way of zeroing parts of a file by deallocating the storage backing those regions, and marking those regions as unwritten instead of actually writing zero bytes to storage. They appear as zeroes to anything reading those ranges, and have the big advantage of not consuming any actual physical storage. On Windows, extent deallocation writes zeros for ordinary files and only actually deallocates physical storage if the file is sparse or compressed (note that AFIO by default creates sparse files where possible, and converts any file opened for writing to a sparse file). For your information, deallocation on NTFS is on a 64Kb granularity, but the zeros are written at a byte granularity. On Linux, an attempt is made to use FALLOC_FL_PUNCH_HOLE which if it fails then a write of zeros corresponding to the same ranges is made instead. On FreeBSD, long runs of zeros are automatically detected and eliminated on physical storage, and so zeros are simply written. On OS X, there is no formal hole punching API that we are aware of, and so zeros are simply written."
+ALIASES += docs_close="Note this is ignored for handles where available_to_directory_cache() is true as those cannot be explicitly closed. Note that failure to explicitly schedule closing a file handle using this call means it will be [*synchronously] closed on last reference count by `__afio_handle__`. This can consume considerable time, especially if SyncOnClose is enabled."
+ALIASES += docs_read="Related types: `__afio_io_req__`"
+ALIASES += docs_write="Related types: `__afio_io_req__`"
+ALIASES += docs_truncate=""
+ALIASES += docs_enumerate="By default dir() returns shared handles i.e. dir("foo") and dir("foo") will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file_flags::unique_directory_handle flag.\n\nNote that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time.\n\nRelated types: `__afio_enumerate_req__`, `__afio_directory_entry__`, `__afio_stat_t__`"
+ALIASES += docs_extents="In a sparsely allocated file, it can be useful to know which extents contain non-zero data. Note that this call is racy (i.e. the extents are enumerated one by one on some platforms, this means they may be out of date with respect to one another) when other threads or processes are concurrently calling zero() or write() - this is a host OS API limitation."
+ALIASES += docs_statfs="Related types: `__afio_statfs_t__`"
diff --git a/attic/doc/doxy/doxygen_enhance.py b/attic/doc/doxy/doxygen_enhance.py
new file mode 100644
index 00000000..af892be9
--- /dev/null
+++ b/attic/doc/doxy/doxygen_enhance.py
@@ -0,0 +1,33 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+# ===========================================================================
+# Copyright (c) 2010 Barend Gehrels, Amsterdam, the Netherlands.
+#
+# Use, modification and distribution is subject to the Boost Software License,
+# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)9
+# ============================================================================
+
+import sys
+
+args = sys.argv[1:]
+if len(args) != 1:
+ raise SystemExit("Usage: doxygen_enhance <html filename>")
+
+
+# 1) set variable for doxygen_contents to be posted
+file_in = open(args[0], 'r')
+doxygen_contents = file_in.read()
+
+doxygen_contents = doxygen_contents.replace('<span>Modules</span>', '<span>Function overview</span>')
+doxygen_contents = doxygen_contents.replace('<span>Related&nbsp;Pages</span>', '<span>Main pages</span>')
+doxygen_contents = doxygen_contents.replace('<span>Main&nbsp;Page</span>', '<span>Introduction</span>')
+doxygen_contents = doxygen_contents.replace('<li><a href="namespaces.html"><span>Namespaces</span></a></li>', '')
+
+doxygen_contents = doxygen_contents.replace('<h1>Modules</h1>Here is a list of all modules:<ul>', '<h1>Function overview</h1>Here is a list of all functions:<ul>')
+doxygen_contents = doxygen_contents.replace('<h1>Related Pages</h1>Here is a list of all related documentation pages:<ul>', '<h1>Main pages</h1>The following pages give backgrounds on Boost.Geometry:<ul>')
+
+
+file_out = file (args[0], 'w')
+file_out.write(doxygen_contents)
+file_out.close()
diff --git a/attic/doc/doxy/doxygen_input/doxygen_footer.html b/attic/doc/doxy/doxygen_input/doxygen_footer.html
new file mode 100644
index 00000000..049eed89
--- /dev/null
+++ b/attic/doc/doxy/doxygen_input/doxygen_footer.html
@@ -0,0 +1,22 @@
+<hr size="1">
+<table width="100%">
+<tbody>
+<tr>
+<td align="left"><small>
+<p>January, 2014</p>
+</small></td>
+<td align="right">
+<small>
+Copyright &copy; 2013-2014 Niall Douglas, Cork, Ireland<br>
+Copyright &copy; 2013 Paul Kirth, California<br>
+</small>
+</td>
+</tr>
+</tbody>
+</table>
+
+<address style="text-align: right;"><small>
+Documentation is generated by&nbsp;<a href="http://www.doxygen.org/index.html">Doxygen</a>
+</small></address>
+</body>
+</html> \ No newline at end of file
diff --git a/attic/doc/doxy/doxygen_input/doxygen_header.html b/attic/doc/doxy/doxygen_input/doxygen_header.html
new file mode 100644
index 00000000..eb6e908c
--- /dev/null
+++ b/attic/doc/doxy/doxygen_input/doxygen_header.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+<title>Boost.AFIO</title>
+<link href="doxygen.css" rel="stylesheet" type="text/css">
+<link href="tabs.css" rel="stylesheet" type="text/css">
+</head>
+
+<table cellpadding="2" width="100%">
+<tbody>
+<tr>
+<td valign="top">
+<img alt="Boost.AFIO" src="images/ggl-logo-big.png" height="80" width="200">
+&nbsp;&nbsp;
+</td>
+<td valign="top" align="right">
+<a href="http://www.boost.org">
+<img alt="Boost C++ Libraries" src="images/accepted_by_boost.png" height="80" width="230" border="0">
+</a>
+</td>
+</tr>
+</tbody>
+</table>
diff --git a/attic/doc/doxy/doxygen_input/groups/groups.hpp b/attic/doc/doxy/doxygen_input/groups/groups.hpp
new file mode 100644
index 00000000..12f5ed1e
--- /dev/null
+++ b/attic/doc/doxy/doxygen_input/groups/groups.hpp
@@ -0,0 +1,48 @@
+// Boost.AFIO
+
+// Use, modification and distribution is subject to the Boost Software License,
+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+// File defining groups for Doxygen.
+// Note that group descriptions are (currently) not used.
+
+/*!
+\defgroup macros Macros
+\defgroup process_threadpool Process Thread pool
+\defgroup normalise_path Normalise path
+\defgroup when_all_futures when_all_p()
+\defgroup file_flags file_flags
+\defgroup metadata_flags metadata_flags
+\defgroup async_op_flags async_op_flags
+\defgroup fs_metadata_flags fs_metadata
+\defgroup dispatcher__filter x
+\defgroup dispatcher__completion x
+\defgroup dispatcher__call x
+\defgroup dispatcher__filedirops x
+\defgroup dispatcher__barrier x
+\defgroup dispatcher__enumerate x
+\defgroup dispatcher__extents x
+\defgroup dispatcher__statfs x
+\defgroup dispatcher__misc x
+\defgroup dispatcher__depends x
+\defgroup async_file_io_dispatcher dispatcher
+\defgroup io_req io_req
+\defgroup make_io_req make_io_req
+\defgroup utils Utilities
+\defgroup async_io_handle__ops Handle Ops
+\defgroup dir Opening/Creating Directories
+\defgroup rmdir Deleting Directories
+\defgroup file Opening/Creating Files
+\defgroup rmfile Deleting Files
+\defgroup symlink Opening/Creating Symlinks
+\defgroup rmsymlink Deleting Symlinks
+\defgroup sync Fsync
+\defgroup zero Zeroing and Deallocation
+\defgroup close Closing files
+\defgroup read Reading data
+\defgroup write Writing data
+\defgroup truncate Resizing files
+\defgroup enumerate Enumerating directory contents
+\defgroup extents Enumerating file extents
+\defgroup statfs Fetch metadata of storage volume
+*/
diff --git a/attic/doc/doxy/doxygen_input/pages/doxygen_mainpage.hpp b/attic/doc/doxy/doxygen_input/pages/doxygen_mainpage.hpp
new file mode 100644
index 00000000..e406719a
--- /dev/null
+++ b/attic/doc/doxy/doxygen_input/pages/doxygen_mainpage.hpp
@@ -0,0 +1,26 @@
+// Boost.AFIO
+//
+// Use, modification and distribution is subject to the Boost Software License,
+// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef _DOXYGEN_MAINPAGE_HPP
+#define _DOXYGEN_MAINPAGE_HPP
+
+
+// -> introduction.qbk
+// -> quickstart.qbk
+
+/*!
+\mainpage Boost.AFIO
+
+Welcome to the Boost.AFIO documentation. You may find the more formal documentation at
+http://boostgsoc13.github.io/boost.afio/ of use. It also includes a full reference manual.
+
+*/
+
+
+
+
+
+#endif // _DOXYGEN_MAINPAGE_HPP
diff --git a/attic/doc/doxy/make_documentation.bat b/attic/doc/doxy/make_documentation.bat
new file mode 100644
index 00000000..bdb6d8fe
--- /dev/null
+++ b/attic/doc/doxy/make_documentation.bat
@@ -0,0 +1,17 @@
+:: ===========================================================================
+:: Copyright (c) 2010 Barend Gehrels, Amsterdam, the Netherlands.
+::
+:: Use, modification and distribution is subject to the Boost Software License,
+:: Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+:: http://www.boost.org/LICENSE_1_0.txt)9
+:: ============================================================================
+
+@echo off
+
+doxygen
+
+cd doxygen_output\html
+
+for %%f in (*.html) do ..\..\doxygen_enhance.py %%f
+
+cd ..\..
diff --git a/attic/doc/doxygen_xml2qbk.patch b/attic/doc/doxygen_xml2qbk.patch
new file mode 100644
index 00000000..2f983604
--- /dev/null
+++ b/attic/doc/doxygen_xml2qbk.patch
@@ -0,0 +1,113 @@
+ .../tools/doxygen_xml2qbk/doxygen_elements.hpp | 2 +-
+ .../tools/doxygen_xml2qbk/doxygen_xml_parser.hpp | 43 +++++++++++++++++++---
+ .../tools/doxygen_xml2qbk/quickbook_output.hpp | 29 +++++++++++++--
+ 3 files changed, 64 insertions(+), 10 deletions(-)
+
+diff --git a/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/doxygen_elements.hpp b/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/doxygen_elements.hpp
+index 04d50dc..43fc98b 100644
+--- a/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/doxygen_elements.hpp
++++ b/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/doxygen_elements.hpp
+@@ -164,7 +164,7 @@ struct class_or_struct : public element
+ std::vector<function> functions;
+
+ std::vector<base_element> typedefs;
+- std::vector<base_element> variables;
++ std::vector<parameter> variables;
+
+ std::vector<base_class> base_classes;
+ };
+diff --git a/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/doxygen_xml_parser.hpp b/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/doxygen_xml_parser.hpp
+index 0a98f42..119a573 100644
+--- a/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/doxygen_xml_parser.hpp
++++ b/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/doxygen_xml_parser.hpp
+@@ -691,13 +691,44 @@ static void parse(rapidxml::xml_node<>* node, configuration const& config, docum
+ }
+ else if (kind == "variable")
+ {
+- if (boost::equals(get_attribute(node, "static"), "yes")
+- && boost::equals(get_attribute(node, "mutable"), "no")
+- && boost::equals(get_attribute(node, "prot"), "public"))
++ if (boost::equals(get_attribute(node, "prot"), "public"))
+ {
+- std::string name = parse_named_node(node->first_node(), "name");
+- doc.cos.variables.push_back(base_element(name));
+- doc.cos.variables.back().id = id;
++ parameter p;
++ p.id = id;
++ for(rapidxml::xml_node<>* var_node = node->first_node(); var_node; var_node=var_node->next_sibling())
++ {
++ if(boost::equals(var_node->name(), "name"))
++ {
++ p.name = var_node->value();
++ }
++ else if(boost::equals(var_node->name(), "type"))
++ {
++ get_contents(var_node->first_node(), p.fulltype);
++ p.type = p.fulltype;
++ //boost::replace_all(p.type, " const", "");
++ //boost::trim(p.type);
++ //boost::replace_all(p.type, "&", "");
++ //boost::replace_all(p.type, "*", "");
++ boost::trim(p.type);
++
++ // If alt output is used retrieve type with QBK links
++ if ( configuration::alt == config.output_style )
++ {
++ p.fulltype_without_links = p.fulltype;
++ p.fulltype.clear();
++ parse_para(var_node->first_node(), config, p.fulltype, p.skip);
++ }
++ }
++ else if(boost::equals(var_node->name(), "briefdescription"))
++ {
++ parse_para(var_node->first_node(), config, p.brief_description, p.skip);
++ }
++ else if(p.brief_description.empty() && boost::equals(var_node->name(), "detaileddescription"))
++ {
++ parse_para(var_node->first_node(), config, p.brief_description, p.skip);
++ }
++ }
++ doc.cos.variables.push_back(p);
+ }
+ }
+
+diff --git a/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/quickbook_output.hpp b/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/quickbook_output.hpp
+index 34295b7..0cb6f24 100644
+--- a/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/quickbook_output.hpp
++++ b/libs/geometry/doc/src/docutils/tools/doxygen_xml2qbk/quickbook_output.hpp
+@@ -544,9 +544,32 @@ void quickbook_output(class_or_struct const& cos, configuration const& config, s
+ out << std::endl;
+ }
+
+- out << "{" << std::endl
+- << " // ..." << std::endl
+- << "};" << std::endl
++ out << "{" << std::endl;
++ if (! cos.variables.empty())
++ {
++ size_t maxlength = 0;
++ BOOST_FOREACH(parameter const& p, cos.variables)
++ {
++ if (! p.skip)
++ {
++ size_t length = 6 + p.fulltype.size() + p.name.size();
++ if (length > maxlength) maxlength = length;
++ }
++ }
++ BOOST_FOREACH(parameter const& p, cos.variables)
++ {
++ if (! p.skip)
++ {
++ size_t length = 4 + p.fulltype.size() + p.name.size();
++ out << " " << p.fulltype << " " << p.name << ";";
++ while(length++ < maxlength) out << " ";
++ out << "// " << p.brief_description << std::endl;
++ }
++ }
++ }
++ else
++ out << " // ..." << std::endl;
++ out << "};" << std::endl
+ << "``" << std::endl << std::endl;
+ quickbook_markup(cos.qbk_markup, markup_after, markup_synopsis, out);
+
diff --git a/attic/doc/generated/class_current_dispatcher_guard.qbk b/attic/doc/generated/class_current_dispatcher_guard.qbk
new file mode 100644
index 00000000..64556646
--- /dev/null
+++ b/attic/doc/generated/class_current_dispatcher_guard.qbk
@@ -0,0 +1,81 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1current__dispatcher__guard.xml]
+[section:current_dispatcher_guard current_dispatcher_guard]
+'''<?dbhtml-include href="disqus_identifiers/current_dispatcher_guard.html"?>'''
+
+'''<indexterm><primary>current_dispatcher_guard</primary></indexterm>'''
+
+
+[heading Description]
+RAII holder for the current async file i/o dispatcher.
+
+[heading Synopsis]
+``class current_dispatcher_guard
+{
+ // ...
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``current_dispatcher_guard(dispatcher_ptr _new)``
+
+] [] [[* dispatcher_ptr]: ['_new]:
+
+
+
+]]
+[[``~current_dispatcher_guard()``
+
+] [] [
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``void release()``
+
+] [Restore the former async file i/o dispatcher now. ] [
+
+][
+
+]
+]
+[[``void dismiss()``
+
+] [Don't restore the former async file i/o dispatcher. ] [
+
+][
+
+]
+]
+[[``void reset(dispatcher_ptr p)``
+
+] [Set a different former async file i/o dispatcher on destruction. ] [[* dispatcher_ptr]: ['p]:
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_directory_entry.qbk b/attic/doc/generated/class_directory_entry.qbk
new file mode 100644
index 00000000..d6eb5a80
--- /dev/null
+++ b/attic/doc/generated/class_directory_entry.qbk
@@ -0,0 +1,405 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1directory__entry.xml]
+[section:directory_entry directory_entry]
+'''<?dbhtml-include href="disqus_identifiers/directory_entry.html"?>'''
+
+'''<indexterm><primary>directory_entry</primary></indexterm>'''
+The abstract base class for an entry in a directory with lazily filled metadata.
+
+[heading Description]
+Note that [^`directory_entry_hash`] will hash one of these for you, and a [^`std::hash<``directory_entry``>`] specialisation is defined for you so you ought to be able to use directory\u005fentry directly in an [^`unordered_map<>`].
+
+See [^`__afio_stat_t__`] for explanations of the fields.
+
+[heading Synopsis]
+``class directory_entry
+{
+ // ...
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``directory_entry()``
+
+] [Default constructor. ] [
+
+]]
+[[``directory_entry(path::string_type _leafname, stat_t __stat, metadata_flags _have_metadata)``
+
+] [Default constructor. ] [[* path::string_type]: ['_leafname]:
+
+[* stat_t]: ['__stat]:
+
+[* metadata_flags]: ['_have_metadata]:
+
+
+
+]]
+[[``directory_entry(const directory_entry & )``
+
+] [] [[* const directory_entry &]: [']:
+
+
+
+]]
+[[``directory_entry(directory_entry && o)``
+
+] [] [[* directory_entry &&]: ['o]:
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``directory_entry & operator=(const directory_entry & )``
+
+] [] [[* const directory_entry &]: [']:
+
+
+
+][
+
+]
+]
+[[``directory_entry & operator=(directory_entry && o)``
+
+] [] [[* directory_entry &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[``bool operator==(const directory_entry & rhs)``
+
+] [] [[* const directory_entry &]: ['rhs]:
+
+
+
+][
+
+]
+]
+[[``bool operator!=(const directory_entry & rhs)``
+
+] [] [[* const directory_entry &]: ['rhs]:
+
+
+
+][
+
+]
+]
+[[``bool operator<(const directory_entry & rhs)``
+
+] [] [[* const directory_entry &]: ['rhs]:
+
+
+
+][
+
+]
+]
+[[``bool operator<=(const directory_entry & rhs)``
+
+] [] [[* const directory_entry &]: ['rhs]:
+
+
+
+][
+
+]
+]
+[[``bool operator>(const directory_entry & rhs)``
+
+] [] [[* const directory_entry &]: ['rhs]:
+
+
+
+][
+
+]
+]
+[[``bool operator>=(const directory_entry & rhs)``
+
+] [] [[* const directory_entry &]: ['rhs]:
+
+
+
+][
+
+]
+]
+[[``path::string_type name()``
+
+] [] [
+
+][
+The name of the directory entry. May be empty if the file is deleted.
+
+]
+]
+[[``metadata_flags metadata_ready()``
+
+] [] [
+
+][
+A bitfield of what metadata is ready right now
+
+]
+]
+[[``metadata_flags fetch_metadata(handle_ptr dirh, metadata_flags wanted)``
+
+] [Fetches the specified metadata, returning that newly available. This is a blocking call if wanted metadata is not yet ready. Note that if the call blocks and the leafname no longer exists or the directory handle is null, an exception is thrown. ] [[* handle_ptr]: ['dirh]: An open handle to the entry's containing directory. You can get this from an op ref using dirop.get_handle().
+
+[* metadata_flags]: ['wanted]: A bitfield of the metadata to fetch. This does not replace existing metadata.
+
+
+
+][
+The metadata now available in this directory entry.
+
+]
+]
+[[``stat_t fetch_lstat(handle_ptr dirh, metadata_flags wanted = directory_entry::metadata_fastpath())``
+
+] [Returns a copy of the internal [^`stat_t`] structure. This is a blocking call if wanted metadata is not yet ready. Note that if the call blocks and the leafname no longer exists or the directory handle is null, an exception is thrown. ] [[* handle_ptr]: ['dirh]: An open handle to the entry's containing directory. You can get this from an op ref using dirop.get_handle().
+
+[* metadata_flags]: ['wanted]: A bitfield of the metadata to fetch. This does not replace existing metadata.
+
+
+
+][
+A copy of the internal [^`stat_t`] structure.
+
+]
+]
+[[``fieldtype st_dev(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_dev. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_ino(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_ino. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_type(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_type. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_perms(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_perms. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_nlink(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_nlink. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_uid(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_uid. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_gid(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_gid. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_rdev(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_rdev. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_atim(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_atim. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_mtim(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_mtim. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_ctim(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_ctim. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_size(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_size. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_allocated(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_allocated. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_blocks(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_blocks. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_blksize(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_blksize. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_flags(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_flags. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_gen(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_gen. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``fieldtype st_birthtim(handle_ptr dirh = handle_ptr())``
+
+] [Returns st_birthtim. ] [[* handle_ptr]: ['dirh]: An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+
+
+
+][
+
+]
+]
+[[``metadata_flags metadata_supported()``
+
+] [A bitfield of what metadata is available on this platform. This doesn't mean all is available for every filing system. ] [
+
+][
+
+]
+]
+[[``metadata_flags metadata_fastpath()``
+
+] [A bitfield of what metadata is fast on this platform. This doesn't mean all is available for every filing system. ] [
+
+][
+
+]
+]
+[[``size_t compatibility_maximum()``
+
+] [The maximum number of entries which is "usual" to fetch at once i.e. what your libc does. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[include generated/struct_directory_entry_hash.qbk]
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_dispatcher.qbk b/attic/doc/generated/class_dispatcher.qbk
new file mode 100644
index 00000000..5dc5395f
--- /dev/null
+++ b/attic/doc/generated/class_dispatcher.qbk
@@ -0,0 +1,341 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1dispatcher.xml]
+[section:dispatcher dispatcher]
+'''<?dbhtml-include href="disqus_identifiers/dispatcher.html"?>'''
+
+'''<indexterm><primary>dispatcher</primary></indexterm>'''
+Abstract base class for dispatching file i/o asynchronously.
+
+[heading Description]
+This is a reference counted instance with platform-specific implementation in object code. Construct an instance using the [^`boost::afio::make_dispatcher()`] function.
+
+[heading Synopsis]
+``class dispatcher
+ : public std::enable_shared_from_this< dispatcher >
+{
+ // ...
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``~dispatcher()``
+
+] [Destroys the dispatcher, blocking inefficiently if any ops are still in flight. ] [
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``void testing_flags(detail::unit_testing_flags flags)``
+
+] [] [[* detail::unit_testing_flags]: ['flags]:
+
+
+
+][
+
+]
+]
+[[``std::shared_ptr< thread_source > threadsource()``
+
+] [Returns the thread source used by this dispatcher. ] [
+
+][
+
+]
+]
+[[``file_flags fileflags(file_flags flags)``
+
+] [Returns file flags as would be used after forcing and masking bits passed during construction. ] [[* file_flags]: ['flags]:
+
+
+
+][
+
+]
+]
+[[``size_t wait_queue_depth()``
+
+] [Returns the current wait queue depth of this dispatcher. ] [
+
+][
+
+]
+]
+[[``size_t fd_count()``
+
+] [Returns the number of open items in this dispatcher. ] [
+
+][
+
+]
+]
+[[``std::vector< future<> > dir(const std::vector< path_req > & reqs)``
+
+] [Schedule a batch of asynchronous directory creations and opens after optional preconditions. ] [[* const std::vector< path_req > &]: ['reqs]: A batch of [^`path_req`] structures.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``std::vector< future<> > rmdir(const std::vector< path_req > & reqs)``
+
+] [Schedule a batch of asynchronous directory deletions after optional preconditions. ] [[* const std::vector< path_req > &]: ['reqs]: A batch of [^`path_req`] structures.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``std::vector< future<> > file(const std::vector< path_req > & reqs)``
+
+] [Schedule a batch of asynchronous file creations and opens after optional preconditions. ] [[* const std::vector< path_req > &]: ['reqs]: A batch of [^`path_req`] structures.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``std::vector< future<> > rmfile(const std::vector< path_req > & reqs)``
+
+] [Schedule a batch of asynchronous file deletions after optional preconditions. ] [[* const std::vector< path_req > &]: ['reqs]: A batch of [^`path_req`] structures.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``std::vector< future<> > symlink(const std::vector< path_req > & reqs, const std::vector< future<>> & targets = std::vector< future<>>())``
+
+] [Schedule a batch of asynchronous symlink creations and opens after a precondition. ] [[* const std::vector< path_req > &]: ['reqs]: A batch of [^`path_req`] structures.
+
+[* const std::vector< future<>> &]: ['targets]: An optional batch of targets if creating symlinks.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``std::vector< future<> > rmsymlink(const std::vector< path_req > & reqs)``
+
+] [Schedule a batch of asynchronous symlink deletions after optional preconditions. ] [[* const std::vector< path_req > &]: ['reqs]: A batch of [^`path_req`] structures.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``std::vector< future<> > sync(const std::vector< future<>> & ops)``
+
+] [Schedule a batch of asynchronous content synchronisations with physical storage after preceding operations. ] [[* const std::vector< future<>> &]: ['ops]: A batch of op handles.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``std::vector< future<> > zero(const std::vector< future<>> & ops, const std::vector< std::vector< std::pair< off_t, off_t >>> & ranges)``
+
+] [Schedule a batch of asynchronous zeroing and deallocations of physical storage ("hole punching") after preceding operations. ] [[* const std::vector< future<>> &]: ['ops]: A batch of op handles.
+
+[* const std::vector< std::vector< std::pair< off_t, off_t >>> &]: ['ranges]: A batch of vectors of extents to zero and deallocate.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``std::vector< future<> > close(const std::vector< future<>> & ops)``
+
+] [Schedule a batch of asynchronous file or directory handle closes after preceding operations. ] [[* const std::vector< future<>> &]: ['ops]: A batch of op handles.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``template<class T>
+std::vector< future<> > read(const std::vector< io_req< T >> & ops)``
+
+] [Schedule a batch of asynchronous data reads after preceding operations, where offset and total data read must not exceed the present file size. ] [[* const std::vector< io_req< T >> &]: ['ops]: A batch of io_req<T> structures.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``template<class T>
+std::vector< future<> > write(const std::vector< io_req< const T >> & ops)``
+
+] [Schedule a batch of asynchronous data writes after preceding operations, where offset and total data written must not exceed the present file size. ] [[* const std::vector< io_req< const T >> &]: ['ops]: A batch of io_req<const T> structures.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``std::vector< future<> > truncate(const std::vector< future<>> & ops, const std::vector< off_t > & sizes)``
+
+] [Schedule a batch of asynchronous file length truncations after preceding operations. ] [[* const std::vector< future<>> &]: ['ops]: A batch of op handles.
+
+[* const std::vector< off_t > &]: ['sizes]: A batch of new lengths.
+
+
+
+][
+A batch of op handles.
+
+]
+]
+[[``std::vector< future< std::pair< std::vector< directory_entry >, bool > > > enumerate(const std::vector< enumerate_req > & reqs)``
+
+] [Schedule a batch of asynchronous directory enumerations after preceding operations. ] [[* const std::vector< enumerate_req > &]: ['reqs]: A batch of enumeration requests.
+
+
+
+][
+A batch of stl_future vectors of directory entries with boolean returning false if done.
+
+]
+]
+[[``std::vector< future< std::vector< std::pair< off_t, off_t > > > > extents(const std::vector< future<>> & ops)``
+
+] [Schedule a batch of asynchronous extent enumerations after preceding operations. ] [[* const std::vector< future<>> &]: ['ops]: A batch of op handles.
+
+
+
+][
+A batch of stl_future vectors of extents.
+
+]
+]
+[[``std::vector< future< statfs_t > > statfs(const std::vector< future<>> & ops, const std::vector< fs_metadata_flags > & reqs)``
+
+] [Schedule a batch of asynchronous volume enumerations after preceding operations. ] [[* const std::vector< future<>> &]: ['ops]: A batch of op handles.
+
+[* const std::vector< fs_metadata_flags > &]: ['reqs]: A batch of metadata requests.
+
+
+
+][
+A batch of stl_future volume metadatas.
+
+]
+]
+[[``future depends(future<> precondition, future<> op)``
+
+] [Schedule the return of an op handle after another op handle completes. This is useful when you need to supply one op handle to a function but it must not begin until another op handle has finished. ] [[* future<>]: ['precondition]: The op handle which must complete for op to be passed through.
+
+[* future<>]: ['op]: The op handle to return.
+
+
+
+][
+The op handle op.
+
+]
+]
+[[``void complete_async_op(size_t id, handle_ptr h, exception_ptr e = exception_ptr())``
+
+] [Completes an operation with a handle or an error, usually used when an operation was previously deferred. ] [[* size_t]: ['id]:
+
+[* handle_ptr]: ['h]:
+
+[* exception_ptr]: ['e]:
+
+
+
+][
+
+]
+]
+[[``void complete_async_op(size_t id, exception_ptr e)``
+
+] [Completes an operation with an error, usually used when an operation was previously deferred. ] [[* size_t]: ['id]:
+
+[* exception_ptr]: ['e]:
+
+
+
+][
+
+]
+]
+[[``template<class T>
+std::vector< future<> > read(const std::vector< io_req< T >> & ops)``
+
+] [] [[* const std::vector< io_req< T >> &]: ['ops]:
+
+
+
+][
+
+]
+]
+[[``template<class T>
+std::vector< future<> > write(const std::vector< io_req< T >> & ops)``
+
+] [] [[* const std::vector< io_req< T >> &]: ['ops]:
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[/ link afio.reference.functions.async_file_io_dispatcher `async_file_io_dispatcher()`]
+[/ include generated/group_dispatcher__filter.qbk]
+[/ include generated/group_dispatcher__completion.qbk]
+[/ include generated/group_dispatcher__call.qbk]
+[include generated/group_dispatcher__filedirops.qbk]
+[include generated/group_dispatcher__enumerate.qbk]
+[include generated/group_dispatcher__extents.qbk]
+[include generated/group_dispatcher__statfs.qbk]
+[/ include generated/group_dispatcher__depends.qbk]
+[/ include generated/group_dispatcher__barrier.qbk]
+[include generated/group_dispatcher__misc.qbk]
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_enqueued_task.qbk b/attic/doc/generated/class_enqueued_task.qbk
new file mode 100644
index 00000000..880c7fcf
--- /dev/null
+++ b/attic/doc/generated/class_enqueued_task.qbk
@@ -0,0 +1,37 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1enqueued__task.xml]
+[section:enqueued_task enqueued_task]
+'''<?dbhtml-include href="disqus_identifiers/enqueued_task.html"?>'''
+
+'''<indexterm><primary>enqueued_task</primary></indexterm>'''
+Effectively our own custom std::packaged_task<>, with copy semantics and letting us early set value to significantly improve performance.
+
+[heading Synopsis]
+``template<class R>
+class enqueued_task
+{
+ // ...
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Description]]
+[[class R] [The return type of the callable which must be without parameters. Unlike [^`std::packaged_task<>`], this custom variant is copyable though each copy always refers to the same internal state. Early stl_future value setting is possible, with any subsequent value setting including that by the function being executed being ignored. Note that this behaviour opens the potential to lose exception state - if you set the stl_future value early and then an exception is later thrown, the exception is swallowed. ]]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_enqueued_task_3_01_r_07_08_4.qbk b/attic/doc/generated/class_enqueued_task_3_01_r_07_08_4.qbk
new file mode 100644
index 00000000..43dedb0a
--- /dev/null
+++ b/attic/doc/generated/class_enqueued_task_3_01_r_07_08_4.qbk
@@ -0,0 +1,144 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1enqueued__task_3_01_r_07_08_4.xml]
+[section:enqueued_task_r___ enqueued_task< R()>]
+'''<?dbhtml-include href="disqus_identifiers/enqueued_task_r___.html"?>'''
+
+'''<indexterm><primary>enqueued_task&lt; R()&gt;</primary></indexterm>'''
+
+
+[heading Synopsis]
+``template<class R>
+class enqueued_task< R()>
+ : public detail::enqueued_task_impl< R >
+{
+ // ...
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Description]]
+[[class R] []]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool valid()``
+
+] [Returns true if valid. ] [
+
+][
+
+]
+]
+[[``void swap(enqueued_task_impl & o)``
+
+] [Swaps contents with another instance. ] [[* enqueued_task_impl &]: ['o]:
+
+
+
+][
+
+]
+]
+[[``void reset()``
+
+] [Resets the contents. ] [
+
+][
+
+]
+]
+[[``void set_task(std::function< R()> _task)``
+
+] [Sets the task. ] [[* std::function< R()>]: ['_task]:
+
+
+
+][
+
+]
+]
+[[``const shared_future< R > & get_future()``
+
+] [Returns the shared stl_future corresponding to the stl_future return value of the task. ] [
+
+][
+
+]
+]
+[[``template<class T>
+void set_future_value(T v)``
+
+] [Sets the shared stl_future corresponding to the stl_future return value of the task. ] [[* T]: ['v]:
+
+
+
+][
+
+]
+]
+[[``void set_future_value()``
+
+] [] [
+
+][
+
+]
+]
+[[``void set_future_exception(exception_ptr e)``
+
+] [Sets the shared stl_future corresponding to the stl_future return value of the task. ] [[* exception_ptr]: ['e]:
+
+
+
+][
+
+]
+]
+[[``void disable_auto_set_future(bool v = true)``
+
+] [Disables the task setting the shared stl_future return value. ] [[* bool]: ['v]:
+
+
+
+][
+
+]
+]
+[[`` enqueued_task(std::function< R()> _task = std::function< R()>())``
+
+] [Default constructor. ] [[* std::function< R()>]: ['_task]:
+
+
+
+][
+
+]
+]
+[[``void operator()()``
+
+] [Invokes the callable, setting the shared stl_future to the value it returns. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_enqueued_task_3_01void_07_08_4.qbk b/attic/doc/generated/class_enqueued_task_3_01void_07_08_4.qbk
new file mode 100644
index 00000000..cefcb808
--- /dev/null
+++ b/attic/doc/generated/class_enqueued_task_3_01void_07_08_4.qbk
@@ -0,0 +1,136 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1enqueued__task_3_01void_07_08_4.xml]
+[section:enqueued_task_void___ enqueued_task< void()>]
+'''<?dbhtml-include href="disqus_identifiers/enqueued_task_void___.html"?>'''
+
+'''<indexterm><primary>enqueued_task&lt; void()&gt;</primary></indexterm>'''
+
+
+[heading Synopsis]
+``class enqueued_task< void()>
+ : public detail::enqueued_task_impl< void >
+{
+ // ...
+};
+``
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool valid()``
+
+] [Returns true if valid. ] [
+
+][
+
+]
+]
+[[``void swap(enqueued_task_impl & o)``
+
+] [Swaps contents with another instance. ] [[* enqueued_task_impl &]: ['o]:
+
+
+
+][
+
+]
+]
+[[``void reset()``
+
+] [Resets the contents. ] [
+
+][
+
+]
+]
+[[``void set_task(std::function< void()> _task)``
+
+] [Sets the task. ] [[* std::function< void()>]: ['_task]:
+
+
+
+][
+
+]
+]
+[[``const shared_future< void > & get_future()``
+
+] [Returns the shared stl_future corresponding to the stl_future return value of the task. ] [
+
+][
+
+]
+]
+[[``void set_future_value(T v)``
+
+] [Sets the shared stl_future corresponding to the stl_future return value of the task. ] [[* T]: ['v]:
+
+
+
+][
+
+]
+]
+[[``void set_future_value()``
+
+] [] [
+
+][
+
+]
+]
+[[``void set_future_exception(exception_ptr e)``
+
+] [Sets the shared stl_future corresponding to the stl_future return value of the task. ] [[* exception_ptr]: ['e]:
+
+
+
+][
+
+]
+]
+[[``void disable_auto_set_future(bool v = true)``
+
+] [Disables the task setting the shared stl_future return value. ] [[* bool]: ['v]:
+
+
+
+][
+
+]
+]
+[[`` enqueued_task(std::function< void()> _task = std::function< void()>())``
+
+] [Default constructor. ] [[* std::function< void()>]: ['_task]:
+
+
+
+][
+
+]
+]
+[[``void operator()()``
+
+] [Invokes the callable, setting the stl_future to the value it returns. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_future.qbk b/attic/doc/generated/class_future.qbk
new file mode 100644
index 00000000..2a770f7f
--- /dev/null
+++ b/attic/doc/generated/class_future.qbk
@@ -0,0 +1,114 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1future.xml]
+[section:future future]
+'''<?dbhtml-include href="disqus_identifiers/future.html"?>'''
+
+'''<indexterm><primary>future</primary></indexterm>'''
+The future status of a scheduled asynchronous operation.
+
+[heading Description]
+Other than the fact that [^`get()`] returns a [^`T`] and [^`get_handle()`] returns a handle, the errored and excepted state for both is identical and non-consuming for both.
+
+Finally, note that there is a freely available type slice from [^`future<T>`] to [^`future<void>`] which moves/copies only the [^`future<void>`] part of the [^`future<T>`], leaving the [^`T`] behind. This is because the AFIO engine resides behind a stable ABI layer which cannot know anything about arbitrary types, and therefore it accepts only [^`future<void>`]. Equally, this means you can supply a [^`future<T>`] as a precondition to another op safe in the knowledge that any [^`T`] part will remain untouched for later consumption.
+
+[heading Synopsis]
+``template<class T, >
+class future
+{
+ // ...
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Default] [Description]]
+[[class T] [void] []]
+[[] [] [Any returned result. Note this is defaulted to [^`void`] for you, so usually you write [^`future<>`]. As of v1.4 of the AFIO engine, the legacy [^`async_io_op`] struct has been replaced with this custom future type based on the lightweight future-promise factory toolkit in forthcoming Boost.Monad. This custom future type consists of two pieces of future data each with different semantics:A [^`handle_ptr`] retrieved using [^`get_handle()`], [^`operator*`] and [^`operator->`] - this is the shared i/o handle returned by the asynchronous operation. This has non-consuming semantics i.e. you can call [^`get_handle()`] as many times as you like. Note that for legacy compatibility reasons, calling [^`get_handle()`] on an invalid instance returns a null shared pointer instead of an exception throw.If T is non-void, any type [^`T`] - this is any additional data returned by an asynchronous operation above and beyond the i/o handle (e.g. [^`enumerate()`] returns a [^`std::pair<std::vector<``directory_entry``>, bool>`]. This has ['consuming] semantics, so calling [^`get()`] returns the result exactly once.]]
+]
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``future()``
+
+] [] [
+
+]]
+[[``future(dispatcher * parent, size_t id, shared_future< handle_ptr > handle,
+ stl_future< T > result, bool check_handle = true, bool validate = true)``
+
+] [] [[* dispatcher *]: ['parent]: The dispatcher this op belongs to.
+
+[* size_t]: ['id]: The unique non-zero id of this op.
+
+[* shared_future< handle_ptr >]: ['handle]: A shared_future to shared state between all instances of this reference.
+
+[* stl_future< T >]: ['result]: A future to any result from the operation.
+
+[* bool]: ['check_handle]: Whether to have validation additionally check if a handle is not null
+
+[* bool]: ['validate]: Whether to check the inputs and shared state for valid (and not errored) values
+
+
+
+]]
+[[``future(future< void > && o, stl_future< T > && result)``
+
+] [] [[* future< void > &&]: ['o]: The future<void>
+
+[* stl_future< T > &&]: ['result]: The future<T> to add
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool valid(bool just_handle = false)``
+
+] [True if this future is valid. ] [[* bool]: ['just_handle]:
+
+
+
+][
+
+]
+]
+[[``T get()``
+
+] [Waits for the future to become ready, returning any value or rethrowing any exception found. Throws a [^`future_errc::no_state`] if this future is invalid. ] [
+
+][
+
+]
+]
+[[``template<class U>
+auto then(U && f)``
+
+] [Schedules a callable to be invoked after this future becomes ready. If this future is null, use the current async file i/o dispatcher. ] [[* U &&]: ['f]:
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_future_3_01void_01_4.qbk b/attic/doc/generated/class_future_3_01void_01_4.qbk
new file mode 100644
index 00000000..66a81055
--- /dev/null
+++ b/attic/doc/generated/class_future_3_01void_01_4.qbk
@@ -0,0 +1,336 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1future_3_01void_01_4.xml]
+[section:future_void_ future< void >]
+'''<?dbhtml-include href="disqus_identifiers/future_void_.html"?>'''
+
+'''<indexterm><primary>future&lt; void &gt;</primary></indexterm>'''
+
+
+[heading Description]
+future
+
+[heading Synopsis]
+``class future< void >
+{
+ // ...
+};
+``
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[`` future(future< void > && o, stl_future< void > && result)``
+
+] [] [[* future< void > &&]: ['o]:
+
+[* stl_future< void > &&]: ['result]:
+
+
+
+][
+
+]
+]
+[[`` future()``
+
+] [Default constructor. ] [
+
+][
+
+]
+]
+[[`` future(const future & o)``
+
+] [Copy constructor. ] [[* const future &]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` future(future && o)``
+
+] [Move constructor. ] [[* future &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` future(dispatcher * parent, size_t id, shared_future< handle_ptr > handle,
+ bool check_handle = true, bool validate = true)``
+
+] [] [[* dispatcher *]: ['parent]: The dispatcher this op belongs to.
+
+[* size_t]: ['id]: The unique non-zero id of this op.
+
+[* shared_future< handle_ptr >]: ['handle]: A shared_ptr to shared state between all instances of this reference.
+
+[* bool]: ['check_handle]: Whether to have validation additionally check if a handle is not null
+
+[* bool]: ['validate]: Whether to check the inputs and shared state for valid (and not errored) values
+
+
+
+][
+
+]
+]
+[[`` future(handle_ptr _handle, bool check_handle = true, bool validate = true)``
+
+] [] [[* handle_ptr]: ['_handle]: A shared_ptr to shared state between all instances of this reference.
+
+[* bool]: ['check_handle]: Whether to have validation additionally check if a handle is not null
+
+[* bool]: ['validate]: Whether to check the inputs and shared state for valid (and not errored) values
+
+
+
+][
+
+]
+]
+[[`` future(dispatcher * parent, size_t id)``
+
+] [] [[* dispatcher *]: ['parent]: The dispatcher this op belongs to.
+
+[* size_t]: ['id]: The unique non-zero id of this op.
+
+
+
+][
+
+]
+]
+[[``future & operator=(const future & o)``
+
+] [Copy assignment. ] [[* const future &]: ['o]:
+
+
+
+][
+
+]
+]
+[[``future & operator=(future && o)``
+
+] [Move assignment. ] [[* future &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[``bool valid()``
+
+] [True if this future is valid. ] [
+
+][
+
+]
+]
+[[`` operator bool()``
+
+] [Same as [^`true_(tribool(*this))`]] [
+
+][
+
+]
+]
+[[``bool is_ready()``
+
+] [True if monad is not empty. ] [
+
+][
+
+]
+]
+[[``bool has_value()``
+
+] [True if monad contains a value_type. ] [
+
+][
+
+]
+]
+[[``bool has_error()``
+
+] [True if monad contains an error_type. ] [
+
+][
+
+]
+]
+[[``bool has_exception(bool only_exception = false)``
+
+] [True if monad contains an exception_type or error_type (any error_type is returned as an exception_ptr by get_exception()). This needs to be true for both for compatibility with Boost.Thread's future. If you really want to test only for has exception only, pass true as the argument. ] [[* bool]: ['only_exception]:
+
+
+
+][
+
+]
+]
+[[``dispatcher * parent()``
+
+] [The parent dispatcher of this future. ] [
+
+][
+
+]
+]
+[[``size_t id()``
+
+] [] [
+
+][
+
+]
+]
+[[``handle_ptr get_handle(bool return_null_if_errored = false)``
+
+] [Retrieves the handle or exception from the shared state, rethrowing any exception. Returns a null shared pointer if this future is invalid. ] [[* bool]: ['return_null_if_errored]:
+
+
+
+][
+
+]
+]
+[[``handle_ptr get_handle(error_type & ec)``
+
+] [Retrieves the handle or exception from the shared state, rethrowing any exception but setting _ec if there is an error. Returns a null shared pointer if this future is invalid. ] [[* error_type &]: ['ec]:
+
+
+
+][
+
+]
+]
+[[``const handle & operator*()``
+
+] [Dereferences the handle from the shared state. Same as *h.get_handle(). ] [
+
+][
+
+]
+]
+[[``handle & operator*()``
+
+] [Dereferences the handle from the shared state. Same as *h.get_handle(). ] [
+
+][
+
+]
+]
+[[``const handle * operator->()``
+
+] [Dereferences the handle from the shared state. Same as h.get_handle()->get(). ] [
+
+][
+
+]
+]
+[[``handle * operator->()``
+
+] [Dereferences the handle from the shared state. Same as h.get_handle()->get(). ] [
+
+][
+
+]
+]
+[[``void get()``
+
+] [Waits for the future to become ready, rethrowing any exception found. Throws a [^`future_errc::no_state`] if this future is invalid. ] [
+
+][
+
+]
+]
+[[``error_type get_error()``
+
+] [Waits for the future to become ready, returning any error state found. ] [
+
+][
+
+]
+]
+[[``exception_type get_exception()``
+
+] [Waits for the future to become ready, returning any error state found. ] [
+
+][
+
+]
+]
+[[``void wait()``
+
+] [Waits for the future to become ready. Throws a [^`future_errc::no_state`] if this future is invalid. ] [
+
+][
+
+]
+]
+[[``template<class Rep, class Period>
+future_status wait_for(const chrono::duration< Rep, Period > & duration)``
+
+] [Waits for the future to become ready for a period. Throws a [^`future_errc::no_state`] if this future is invalid. ] [[* const chrono::duration< Rep, Period > &]: ['duration]:
+
+
+
+][
+
+]
+]
+[[``template<class Clock, class Duration>
+future_status wait_until(const chrono::time_point< Clock, Duration > & deadline)``
+
+] [Waits for the future to become ready until a deadline. Throws a [^`future_errc::no_state`] if this future is invalid. ] [[* const chrono::time_point< Clock, Duration > &]: ['deadline]:
+
+
+
+][
+
+]
+]
+[[``template<class U>
+auto then(U && f)``
+
+] [Schedules a callable to be invoked after this future becomes ready. If this future is null, use the current async file i/o dispatcher. ] [[* U &&]: ['f]:
+
+
+
+][
+
+]
+]
+[[``bool validate(bool check_handle = true)``
+
+] [Validates contents. ] [[* bool]: ['check_handle]:
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_handle.qbk b/attic/doc/generated/class_handle.qbk
new file mode 100644
index 00000000..6a5a8e41
--- /dev/null
+++ b/attic/doc/generated/class_handle.qbk
@@ -0,0 +1,276 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1handle.xml]
+[section:handle handle]
+'''<?dbhtml-include href="disqus_identifiers/handle.html"?>'''
+[section:open_states open_states]
+'''<?dbhtml-include href="disqus_identifiers/open_states.html"?>'''
+
+'''<indexterm><primary>open_states</primary></indexterm>'''
+'''<indexterm><primary>closed</primary></indexterm>'''
+'''<indexterm><primary>open</primary></indexterm>'''
+'''<indexterm><primary>opendir</primary></indexterm>'''
+In which way this handle is opened or not.
+
+[heading Synopsis]
+``enum open_states {closed, open, opendir};``
+
+[heading Values]
+
+[table
+[[Value] [Description] ]
+[[closed] [This handle is closed. ]]
+[[open] [This handle is open as a normal handle. ]]
+[[opendir] [This handle is open as a cached directory handle, and therefore closing it explicitly has no effect. ]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+
+'''<indexterm><primary>handle</primary></indexterm>'''
+The abstract base class encapsulating a platform-specific file handle.
+
+[heading Description]
+Note that failure to explicitly schedule closing a file handle in the dispatcher means it will be synchronously closed on last reference count by handle. This can consume considerable time, especially if SyncOnClose is enabled.
+
+[heading Synopsis]
+``class handle
+ : public std::enable_shared_from_this< handle >
+{
+ // ...
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``~handle()``
+
+] [] [
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``dispatcher * parent()``
+
+] [Returns the parent of this io handle. ] [
+
+][
+
+]
+]
+[[``handle_ptr container()``
+
+] [Returns a handle to the directory containing this handle. Only works if [^`file_flags::hold_parent_open`] was specified when this handle was opened. ] [
+
+][
+
+]
+]
+[[``open_states is_open()``
+
+] [Returns if this handle is opened or not. ] [
+
+][
+
+]
+]
+[[``void * native_handle()``
+
+] [Returns the native handle of this io handle. On POSIX, you can cast this to a fd using [^`(int)(size_t) ``native_handle()`]. On Windows it's a simple [^`(HANDLE) ``native_handle()`]. ] [
+
+][
+
+]
+]
+[[``const chrono::system_clock::time_point & opened()``
+
+] [Returns when this handle was opened. ] [
+
+][
+
+]
+]
+[[``boost::afio::path path(bool refresh = false)``
+
+] [Returns the path of this i/o handle right now if the handle is open and ['refresh] is true, else last known good. May be null if the file has been deleted. ] [[* bool]: ['refresh]: Whether to ask the OS for the current path of this handle.
+
+
+
+][
+The path of this i/o handle right now.
+
+]
+]
+[[``boost::afio::path path()``
+
+] [Returns the last known good path of this i/o handle. May be null if the file has been deleted. ] [
+
+][
+
+]
+]
+[[``file_flags flags()``
+
+] [Returns the final flags used when this handle was opened. ] [
+
+][
+
+]
+]
+[[``bool opened_as_file()``
+
+] [True if this handle was opened as a file. ] [
+
+][
+
+]
+]
+[[``bool opened_as_dir()``
+
+] [True if this handle was opened as a directory. ] [
+
+][
+
+]
+]
+[[``bool opened_as_symlink()``
+
+] [True if this handle was opened as a symlink. ] [
+
+][
+
+]
+]
+[[``bool available_to_directory_cache()``
+
+] [True if this handle is used by the directory handle cache (not UniqueDirectoryHandle and is open for write and not open for write) ] [
+
+][
+
+]
+]
+[[``off_t read_count()``
+
+] [Returns how many bytes have been read since this handle was opened. ] [
+
+][
+
+]
+]
+[[``off_t write_count()``
+
+] [Returns how many bytes have been written since this handle was opened. ] [
+
+][
+
+]
+]
+[[``off_t write_count_since_fsync()``
+
+] [Returns how many bytes have been written since this handle was last fsynced. ] [
+
+][
+
+]
+]
+[[``directory_entry direntry(metadata_flags wanted = directory_entry::metadata_fastpath())``
+
+] [Returns a mostly filled directory_entry for the file or directory referenced by this handle. Use [^`metadata_flags::All`] if you want it as complete as your platform allows, even at the cost of severe performance loss. ] [[* metadata_flags]: ['wanted]: The metadata wanted.
+
+
+
+][
+A directory entry for this handle.
+
+]
+]
+[[``stat_t lstat(metadata_flags wanted = directory_entry::metadata_fastpath())``
+
+] [Returns a mostly filled stat_t structure for the file or directory referenced by this handle. Use [^`metadata_flags::All`] if you want it as complete as your platform allows, even at the cost of severe performance loss. Calls direntry(), so same race guarantees as that call. ] [[* metadata_flags]: ['wanted]:
+
+
+
+][
+
+]
+]
+[[``boost::afio::path target()``
+
+] [Returns the target path of this handle if it is a symbolic link. ] [
+
+][
+The path the symbolic link points to. May not exist or even be valid.
+
+]
+]
+[[``mapped_file_ptr map_file(size_t length = (size_t)-1, off_t offset = 0, bool read_only = false)``
+
+] [Maps the file into memory, returning a null pointer if couldn't map (e.g. address space exhaustion). Do NOT mix this with [^`file_flags::os_direct`]! ] [[* size_t]: ['length]:
+
+[* off_t]: ['offset]:
+
+[* bool]: ['read_only]:
+
+
+
+][
+
+]
+]
+[[``void link(const path_req & req)``
+
+] [Hard links the file to a new location on the same volume. ] [[* const path_req &]: ['req]: The absolute or relative (in which case precondition specifies a directory) path to create a hard link at.
+
+
+
+][
+
+]
+]
+[[``void unlink()``
+
+] [Unlinks the file from its present location as determined by path(true), which could be any hard link on those operating systems with an unstable path(true). Other links may remain to the same file. ] [
+
+][
+
+]
+]
+[[``void atomic_relink(const path_req & req)``
+
+] [Links the file to a new location and unlinks the file from its present location as determined by path(true), ['atomically overwriting any file entry at the new location]. Very useful for preparing file content elsewhere and once ready, atomically making it visible at some named location to other processes. Note that operating systems with an unstable path(true) may relink any hard link to the file to the new location. ] [[* const path_req &]: ['req]: The absolute or relative (in which case precondition specifies a directory) path to relink to.
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[include generated/struct_handle_1_1mapped_file.qbk]
+[include generated/group_async_io_handle__ops.qbk]
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_path.qbk b/attic/doc/generated/class_path.qbk
new file mode 100644
index 00000000..473bc102
--- /dev/null
+++ b/attic/doc/generated/class_path.qbk
@@ -0,0 +1,387 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1path.xml]
+[section:path path]
+'''<?dbhtml-include href="disqus_identifiers/path.html"?>'''
+
+'''<indexterm><primary>path</primary></indexterm>'''
+An AFIO filesystem path, a thin wrapper of filesystem::path used to mark when a filesystem path has been prepared for AFIO usage. Note that on Windows this exclusively refers to a case sensitive NT kernel path, not a Win32 path (Win32 paths are converted in the constructor).
+
+[heading Synopsis]
+``class path
+ : protected path
+{
+ // ...
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``path()``
+
+] [Default constructor. ] [
+
+]]
+[[``path(const path & p)``
+
+] [Copy constructor. ] [[* const path &]: ['p]:
+
+
+
+]]
+[[``path(const filesystem::path & p)``
+
+] [Converts a filesystem::path to AFIO format. ] [[* const filesystem::path &]: ['p]:
+
+
+
+]]
+[[``path(const char * p)``
+
+] [Converts a filesystem::path to AFIO format. ] [[* const char *]: ['p]:
+
+
+
+]]
+[[``path(const string_type & p)``
+
+] [Converts a filesystem::path to AFIO format. ] [[* const string_type &]: ['p]:
+
+
+
+]]
+[[``path(path && p)``
+
+] [Move constructor. ] [[* path &&]: ['p]:
+
+
+
+]]
+[[``path(filesystem::path && p)``
+
+] [Converts a filesystem::path to AFIO format. ] [[* filesystem::path &&]: ['p]:
+
+
+
+]]
+[[``path(string_type && p)``
+
+] [Converts a filesystem::path to AFIO format. ] [[* string_type &&]: ['p]:
+
+
+
+]]
+[[``template<class InputIterator>
+path(InputIterator begin, InputIterator end)``
+
+] [Converts source to AFIO path format. ] [[* InputIterator]: ['begin]:
+
+[* InputIterator]: ['end]:
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``path & operator=(const path & p)``
+
+] [Copy assignment. ] [[* const path &]: ['p]:
+
+
+
+][
+
+]
+]
+[[``path & operator=(path && p)``
+
+] [Move assignment. ] [[* path &&]: ['p]:
+
+
+
+][
+
+]
+]
+[[``template<class Source>
+path & assign(Source const & source)``
+
+] [Converts source to AFIO path format. ] [[* Source const &]: ['source]:
+
+
+
+][
+
+]
+]
+[[``template<class InputIterator>
+path & assign(InputIterator begin, InputIterator end)``
+
+] [] [[* InputIterator]: ['begin]:
+
+[* InputIterator]: ['end]:
+
+
+
+][
+
+]
+]
+[[``path & operator/=(const path & p)``
+
+] [] [[* const path &]: ['p]:
+
+
+
+][
+
+]
+]
+[[``template<class Source>
+path & operator/=(Source const & source)``
+
+] [] [[* Source const &]: ['source]:
+
+
+
+][
+
+]
+]
+[[``template<class Source>
+path & append(Source const & source)``
+
+] [] [[* Source const &]: ['source]:
+
+
+
+][
+
+]
+]
+[[``template<class InputIterator>
+path & append(InputIterator begin, InputIterator end)``
+
+] [] [[* InputIterator]: ['begin]:
+
+[* InputIterator]: ['end]:
+
+
+
+][
+
+]
+]
+[[``path & operator+=(const path & x)``
+
+] [] [[* const path &]: ['x]:
+
+
+
+][
+
+]
+]
+[[``path & operator+=(const string_type & x)``
+
+] [] [[* const string_type &]: ['x]:
+
+
+
+][
+
+]
+]
+[[``path & operator+=(const value_type * x)``
+
+] [] [[* const value_type *]: ['x]:
+
+
+
+][
+
+]
+]
+[[``path & operator+=(value_type x)``
+
+] [] [[* value_type]: ['x]:
+
+
+
+][
+
+]
+]
+[[``template<class Source>
+path & operator+=(Source const & x)``
+
+] [] [[* Source const &]: ['x]:
+
+
+
+][
+
+]
+]
+[[``template<class Source>
+path & concat(Source const & x)``
+
+] [] [[* Source const &]: ['x]:
+
+
+
+][
+
+]
+]
+[[``template<class InputIterator>
+path & concat(InputIterator begin, InputIterator end)``
+
+] [] [[* InputIterator]: ['begin]:
+
+[* InputIterator]: ['end]:
+
+
+
+][
+
+]
+]
+[[``path & make_preferred()``
+
+] [] [
+
+][
+
+]
+]
+[[``path & remove_filename()``
+
+] [] [
+
+][
+
+]
+]
+[[``path & replace_extension(const path & new_extension = path())``
+
+] [] [[* const path &]: ['new_extension]:
+
+
+
+][
+
+]
+]
+[[``path root_name()``
+
+] [] [
+
+][
+
+]
+]
+[[``path root_directory()``
+
+] [] [
+
+][
+
+]
+]
+[[``path root_path()``
+
+] [] [
+
+][
+
+]
+]
+[[``path relative_path()``
+
+] [] [
+
+][
+
+]
+]
+[[``path parent_path()``
+
+] [] [
+
+][
+
+]
+]
+[[``path filename()``
+
+] [] [
+
+][
+
+]
+]
+[[``path stem()``
+
+] [] [
+
+][
+
+]
+]
+[[``path extension()``
+
+] [] [
+
+][
+
+]
+]
+[[``iterator begin()``
+
+] [] [
+
+][
+
+]
+]
+[[``iterator end()``
+
+] [] [
+
+][
+
+]
+]
+[[``filesystem::path filesystem_path()``
+
+] [Return a normalised filesystem::path from an AFIO path. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[include generated/struct_path_1_1make_absolute.qbk]
+[include generated/group_normalise_path.qbk]
+[include generated/struct_path_hash.qbk]
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_std_thread_pool.qbk b/attic/doc/generated/class_std_thread_pool.qbk
new file mode 100644
index 00000000..a7c940b9
--- /dev/null
+++ b/attic/doc/generated/class_std_thread_pool.qbk
@@ -0,0 +1,105 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1std__thread__pool.xml]
+[section:std_thread_pool std_thread_pool]
+'''<?dbhtml-include href="disqus_identifiers/std_thread_pool.html"?>'''
+
+'''<indexterm><primary>std_thread_pool</primary></indexterm>'''
+A very simple thread pool based on std::thread or boost::thread.
+
+[heading Description]
+This instantiates a [^`asio::io_service`] and a latchable [^`asio::io_service::work`] to keep any threads working until the instance is destructed.
+
+[heading Synopsis]
+``class std_thread_pool
+ : public thread_source
+{
+ // ...
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``std_thread_pool(size_t no)``
+
+] [Constructs a thread pool of ['no] workers. ] [[* size_t]: ['no]: The number of worker threads to create
+
+
+
+]]
+[[``~std_thread_pool()``
+
+] [] [
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``asio::io_service & io_service()``
+
+] [Returns the underlying io_service. ] [
+
+][
+
+]
+]
+[[``template<class R>
+void enqueue(enqueued_task< R > task)``
+
+] [Sends a task to the thread pool for execution. ] [[* enqueued_task< R >]: ['task]:
+
+
+
+][
+
+]
+]
+[[``template<class F>
+shared_future< typename std::result_of< F()>::type > enqueue(F f)``
+
+] [Sends some callable entity to the thread pool for execution. ] [[* F]: ['f]: Any instance of a callable type
+
+
+
+][
+An enqueued task for the enqueued callable
+
+]
+]
+[[``void add_workers(size_t no)``
+
+] [Adds more workers to the thread pool. ] [[* size_t]: ['no]: The number of worker threads to add
+
+
+
+][
+
+]
+]
+[[``void destroy()``
+
+] [Destroys the thread pool, waiting for worker threads to exit beforehand. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_std_thread_pool_1_1worker.qbk b/attic/doc/generated/class_std_thread_pool_1_1worker.qbk
new file mode 100644
index 00000000..f720e495
--- /dev/null
+++ b/attic/doc/generated/class_std_thread_pool_1_1worker.qbk
@@ -0,0 +1,55 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1std__thread__pool_1_1worker.xml]
+[section:std_thread_pool_worker std_thread_pool::worker]
+'''<?dbhtml-include href="disqus_identifiers/std_thread_pool_worker.html"?>'''
+
+'''<indexterm><primary>std_thread_pool</primary></indexterm><indexterm><primary>worker</primary></indexterm>'''
+
+
+[heading Synopsis]
+``class std_thread_pool::worker
+{
+ // ...
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``worker(std_thread_pool * p)``
+
+] [] [[* std_thread_pool *]: ['p]:
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``void operator()()``
+
+] [] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_thread_source.qbk b/attic/doc/generated/class_thread_source.qbk
new file mode 100644
index 00000000..621b0696
--- /dev/null
+++ b/attic/doc/generated/class_thread_source.qbk
@@ -0,0 +1,70 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1thread__source.xml]
+[section:thread_source thread_source]
+'''<?dbhtml-include href="disqus_identifiers/thread_source.html"?>'''
+
+'''<indexterm><primary>thread_source</primary></indexterm>'''
+Abstract base class for a source of thread workers.
+
+[heading Description]
+Note that in Boost 1.54, and possibly later versions, [^`asio::io_service`] on Windows appears to dislike being destructed during static data deinit, hence why this inherits from [^`std::enable_shared_from_this<>`] in order that it may be reference count deleted before static data deinit occurs.
+
+[heading Synopsis]
+``class thread_source
+ : public std::enable_shared_from_this< thread_source >
+{
+ // ...
+};
+``
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``asio::io_service & io_service()``
+
+] [Returns the underlying io_service. ] [
+
+][
+
+]
+]
+[[``template<class R>
+void enqueue(enqueued_task< R > task)``
+
+] [Sends a task to the thread pool for execution. ] [[* enqueued_task< R >]: ['task]:
+
+
+
+][
+
+]
+]
+[[``template<class F>
+shared_future< typename std::result_of< F()>::type > enqueue(F f)``
+
+] [Sends some callable entity to the thread pool for execution. ] [[* F]: ['f]: Any instance of a callable type
+
+
+
+][
+An enqueued task for the enqueued callable
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_utils_1_1page_allocator.qbk b/attic/doc/generated/class_utils_1_1page_allocator.qbk
new file mode 100644
index 00000000..38a4614f
--- /dev/null
+++ b/attic/doc/generated/class_utils_1_1page_allocator.qbk
@@ -0,0 +1,141 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1utils_1_1page__allocator.xml]
+[section:utils_page_allocator utils::page_allocator]
+'''<?dbhtml-include href="disqus_identifiers/utils_page_allocator.html"?>'''
+
+'''<indexterm><primary>utils</primary></indexterm><indexterm><primary>page_allocator</primary></indexterm>'''
+An STL allocator which allocates large TLB page memory.If the operating system is configured to allow it, this type of memory is particularly efficient for doing large scale file i/o. This is because the kernel must normally convert the scatter gather buffers you pass into extended scatter gather buffers as the memory you see as contiguous may not, and probably isn't, actually be contiguous in physical memory. Regions returned by this allocator ['may] be allocated contiguously in physical memory and therefore the kernel can pass through your scatter gather buffers unmodified.
+
+[heading Description]
+A particularly useful combination with this allocator is with the page\u005fsizes() member function of [*afio_dispatcher]. This will return which pages sizes are possible, and which page sizes are enabled for this user. If writing a file copy routine for example, using this allocator with the largest page size as the copy chunk makes a great deal of sense.
+
+Be aware that as soon as the allocation exceeds a large page size, most systems allocate in multiples of the large page size, so if the large page size were 2Mb and you allocate 2Mb + 1 byte, 4Mb is actually consumed.
+
+[heading Synopsis]
+``template<typename T>
+class utils::page_allocator
+{
+ // ...
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Description]]
+[[typename T] []]
+]
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``page_allocator()``
+
+] [] [
+
+]]
+[[``template<class U>
+page_allocator(const page_allocator< U > & )``
+
+] [] [[* const page_allocator< U > &]: [']:
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``size_type max_size()``
+
+] [] [
+
+][
+
+]
+]
+[[``pointer address(reference x)``
+
+] [] [[* reference]: ['x]:
+
+
+
+][
+
+]
+]
+[[``const_pointer address(const_reference x)``
+
+] [] [[* const_reference]: ['x]:
+
+
+
+][
+
+]
+]
+[[``pointer allocate(size_type n, const void * hint = 0)``
+
+] [] [[* size_type]: ['n]:
+
+[* const void *]: ['hint]:
+
+
+
+][
+
+]
+]
+[[``void deallocate(pointer p, size_type n)``
+
+] [] [[* pointer]: ['p]:
+
+[* size_type]: ['n]:
+
+
+
+][
+
+]
+]
+[[``template<class U, class... Args>
+void construct(U * p, Args &&... args)``
+
+] [] [[* U *]: ['p]:
+
+[* Args &&...]: ['args]:
+
+
+
+][
+
+]
+]
+[[``template<class U>
+void destroy(U * p)``
+
+] [] [[* U *]: ['p]:
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_utils_1_1page_allocator_3_01void_01_4.qbk b/attic/doc/generated/class_utils_1_1page_allocator_3_01void_01_4.qbk
new file mode 100644
index 00000000..4619e36f
--- /dev/null
+++ b/attic/doc/generated/class_utils_1_1page_allocator_3_01void_01_4.qbk
@@ -0,0 +1,30 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1utils_1_1page__allocator_3_01void_01_4.xml]
+[section:utils_page_allocator< void > utils::page_allocator< void >]
+'''<?dbhtml-include href="disqus_identifiers/utils_page_allocator_-void-_.html"?>'''
+
+'''<indexterm><primary>utils</primary></indexterm><indexterm><primary>page_allocator< void ></primary></indexterm>'''
+
+
+[heading Synopsis]
+``class utils::page_allocator< void >
+{
+ // ...
+};
+``
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/class_utils_1_1secded_ecc.qbk b/attic/doc/generated/class_utils_1_1secded_ecc.qbk
new file mode 100644
index 00000000..04d7ecf3
--- /dev/null
+++ b/attic/doc/generated/class_utils_1_1secded_ecc.qbk
@@ -0,0 +1,175 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\classboost_1_1afio_1_1utils_1_1secded__ecc.xml]
+[section:utils_secded_ecc utils::secded_ecc]
+'''<?dbhtml-include href="disqus_identifiers/utils_secded_ecc.html"?>'''
+[section:verify_status verify_status]
+'''<?dbhtml-include href="disqus_identifiers/verify_status.html"?>'''
+
+'''<indexterm><primary>verify_status</primary></indexterm>'''
+'''<indexterm><primary>corrupt</primary></indexterm>'''
+'''<indexterm><primary>okay</primary></indexterm>'''
+'''<indexterm><primary>healed</primary></indexterm>'''
+The outcomes from verify()
+
+[heading Synopsis]
+``enum verify_status {corrupt = 0, okay = 1, healed = 2};``
+
+[heading Values]
+
+[table
+[[Value] [Description] ]
+[[corrupt] [The buffer had more than a single bit corrupted or the ECC was invalid. ]]
+[[okay] [The buffer had no errors. ]]
+[[healed] [The buffer was healed. ]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+
+'''<indexterm><primary>utils</primary></indexterm><indexterm><primary>secded_ecc</primary></indexterm>'''
+Calculates the single error correcting double error detecting (SECDED) Hamming Error Correcting Code for a ['blocksize] block of bytes. For example, a secdec_ecc<8> would be the very common 72,64 Hamming code used in ECC RAM, or secdec_ecc<4096> would be for a 32784,32768 Hamming code.
+
+[heading Description]
+Did you know that some non-ECC RAM systems can see 1e-12 flips/bit/hour, which is 3.3 bits flipped in a 16Gb RAM system per 24 hours). See Schroeder, Pinheiro and Weber (2009) 'DRAM Errors in the Wild: A Large-Scale Field Study'.
+
+After construction during which lookup tables are built, no state is modified and therefore this class is safe for static storage (indeed if C++ 14 is available, the constructor is constexpr). The maximum number of bits in a code is a good four billion, I did try limiting it to 65536 for performance but it wasn't worth it, and one might want > 8Kb blocks maybe. As with all SECDED ECC, undefined behaviour occurs when more than two bits of error are present or the ECC supplied is incorrect. You should combine this SECDED with a robust hash which can tell you definitively if a buffer is error free or not rather than relying on this to correctly do so.
+
+The main intended use case for this routine is calculating the ECC on data being written to disc, and hence that is where performance has been maximised. It is not expected that this routine will be frequently called on data being read from disc i.e. only when its hash doesn't match its contents which should be very rare, and then a single bit heal using this routine is attempted before trying again with the hash. Care was taken that really enormous SECDEDs are fast, in fact tuning was mostly done for the 32784,32768 code which can heal one bad bit per 4Kb page as the main thing we have in mind is achieving reliable filing system code on computers without ECC RAM and in which sustained large quantities of random disc i/o produce a worrying number of flipped bits in a 24 hour period (anywhere between 0 and 3 on my hardware here, average is about 0.8).
+
+Performance of the fixed block size routine where you supply whole chunks of ['blocksize] is therefore [*particularly] excellent as I spent a lot of time tuning it for Ivy Bridge and later out of order architectures: an amazing 22 cycles per byte for the 32784,32768 code, which is a testament to modern out of order CPUs (remember SECDED inherently must work a bit at a time, so that's just 2.75 amortised CPU cycles per bit which includes a table load, a bit test, and a conditional XOR) i.e. it's pushing about 1.5 ops per clock cycle. On my 3.9Ghz i7-3770K here, I see about 170Mb/sec per CPU core.
+
+The variable length routine is necessarily much slower as it must work in single bytes, and achieves 72 cycles per byte, or 9 cycles per bit (64Mb/sec per CPU core).
+
+[heading Synopsis]
+``template<size_t blocksize>
+class utils::secded_ecc
+{
+ // ...
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Description]]
+[[size_t blocksize] []]
+]
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``secded_ecc()``
+
+] [Constructs an instance, configuring the necessary lookup tables. ] [
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``constexpr result_type result_bits_valid()``
+
+] [The number of bits valid in result_type. ] [
+
+][
+
+]
+]
+[[``result_type operator()(result_type ecc, const char * buffer)``
+
+] [Accumulate ECC from fixed size buffer. ] [[* result_type]: ['ecc]:
+
+[* const char *]: ['buffer]:
+
+
+
+][
+
+]
+]
+[[``result_type operator()(const char * buffer)``
+
+] [] [[* const char *]: ['buffer]:
+
+
+
+][
+
+]
+]
+[[``result_type operator()(result_type ecc, const char * buffer, size_t length)``
+
+] [Accumulate ECC from partial buffer where ['length] <= ['blocksize]. ] [[* result_type]: ['ecc]:
+
+[* const char *]: ['buffer]:
+
+[* size_t]: ['length]:
+
+
+
+][
+
+]
+]
+[[``result_type operator()(const char * buffer, size_t length)``
+
+] [] [[* const char *]: ['buffer]:
+
+[* size_t]: ['length]:
+
+
+
+][
+
+]
+]
+[[``result_type find_bad_bit(result_type good_ecc, result_type bad_ecc)``
+
+] [Given the original ECC and the new ECC for a buffer, find the bad bit. Return (result_type)-1 if not found (e.g. ECC corrupt) ] [[* result_type]: ['good_ecc]:
+
+[* result_type]: ['bad_ecc]:
+
+
+
+][
+
+]
+]
+[[``verify_status verify(char * buffer, result_type good_ecc)``
+
+] [Verifies and heals when possible a buffer, returning non zero if the buffer is error free. ] [[* char *]: ['buffer]:
+
+[* result_type]: ['good_ecc]:
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]O(N) where N is the blocksize
+[heading Exception Model]Throws constexpr exceptions in constructor only, otherwise entirely noexcept.
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_async_file_io_dispatcher.qbk b/attic/doc/generated/group_async_file_io_dispatcher.qbk
new file mode 100644
index 00000000..6ec3a4ea
--- /dev/null
+++ b/attic/doc/generated/group_async_file_io_dispatcher.qbk
@@ -0,0 +1,109 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__async__file__io__dispatcher.xml]
+[section:current_dispatcher current_dispatcher]
+'''<?dbhtml-include href="disqus_identifiers/current_dispatcher.html"?>'''
+
+'''<indexterm><primary>current_dispatcher</primary></indexterm>'''
+Retrieves the currently set async\u005ffile\u005fio\u005fdispatcher for this thread, optionally setting it to a new dispatcher.
+
+[heading Synopsis]
+``BOOST_AFIO_DECL dispatcher_ptr current_dispatcher(option< dispatcher_ptr > new_dispatcher = empty)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[option< dispatcher_ptr >] [] [new_dispatcher] [The new async_file_io_dispatcher to set. ]]
+]
+
+
+[heading Returns]
+The current async\u005ffile\u005fio\u005fdispatcher.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:make_dispatcher make_dispatcher]
+'''<?dbhtml-include href="disqus_identifiers/make_dispatcher.html"?>'''
+
+'''<indexterm><primary>make_dispatcher</primary></indexterm>'''
+Instatiates the best available async\u005ffile\u005fio\u005fdispatcher implementation for this system for the given uri.
+
+[heading Description]
+Note that the number of threads in the threadpool supplied is the maximum non-async op queue depth (e.g. file opens, closes etc.). For fast SSDs, there isn't much gain after eight-sixteen threads, so the process threadpool is set to eight by default. For slow hard drives, or worse, SANs, a queue depth of 64 or higher might deliver significant benefits.
+
+URIs currently supported by AFIO:
+
+* [*[^__fileurl__]] The dispatcher will refer to the local filesystem of this machine.
+
+
+
+[heading Synopsis]
+``BOOST_AFIO_DECL outcome<dispatcher_ptr> make_dispatcher(std::string uri = "file : / / /", file_flags flagsforce = file_flags::none, file_flags flagsmask = file_flags::none,
+ std::shared_ptr< thread_source > threadpool = process_threadpool())``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[std::string] [] [uri] [Where to open the dispatcher upon. ]]
+[[file_flags] [] [flagsforce] [The flags to bitwise OR with any opened file flags. Used to force on certain flags. ]]
+[[file_flags] [] [flagsmask] [The flags to bitwise AND with any opened file flags. Used to force off certain flags. ]]
+[[std::shared_ptr< thread_source >] [] [threadpool] [The threadpool instance to use for asynchronous dispatch.]]
+]
+
+
+[heading Returns]
+A shared\u005fptr to the best available async\u005ffile\u005fio\u005fdispatcher implementation for this system for the given uri.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Example]
+[call_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:depends depends]
+'''<?dbhtml-include href="disqus_identifiers/depends.html"?>'''
+
+'''<indexterm><primary>depends</primary></indexterm>'''
+Make ready a future after a precondition future readies.
+
+[heading Synopsis]
+``future depends(future<> precondition, future<> out)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [precondition] [The future which must signal before the returned future signals. ]]
+[[future<>] [] [out] [The future to return. ]]
+]
+
+
+[heading Returns]
+A future which returns out after precondition signals.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_async_io_handle__ops.qbk b/attic/doc/generated/group_async_io_handle__ops.qbk
new file mode 100644
index 00000000..d8af2227
--- /dev/null
+++ b/attic/doc/generated/group_async_io_handle__ops.qbk
@@ -0,0 +1,237 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__async__io__handle____ops.xml]
+[section:path path]
+'''<?dbhtml-include href="disqus_identifiers/path.html"?>'''
+
+'''<indexterm><primary>path</primary></indexterm>'''
+Returns the path of this i/o handle right now if the handle is open and ['refresh] is true, else last known good. May be null if the file has been deleted.
+
+[heading Description]
+Note the refreshed path completely dereferences any intermediate symbolic links to return a truly absolute canonical path, and therefore may look quite different to before. Some operating systems unfortunately also return any one of the hard links to the file, so if hard links is greater than one the path refreshed will randomly permute.
+
+Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``virtual path handle::path(bool refresh = false)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[bool] [] [refresh] [Whether to ask the OS for the current path of this handle.]]
+]
+
+
+[heading Returns]
+The path of this i/o handle right now.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD..Paths are only refreshed for directories, not files.]
+[raceguarantee Linux, Windows..Paths are always refreshed and ignore other hard links.]
+[raceguarantee OS X..Paths are only refreshed for directories and files with a single hard link.]
+ ]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:direntry direntry]
+'''<?dbhtml-include href="disqus_identifiers/direntry.html"?>'''
+
+'''<indexterm><primary>direntry</primary></indexterm>'''
+Returns a mostly filled directory\u005fentry for the file or directory referenced by this handle. Use [^`metadata_flags::All`] if you want it as complete as your platform allows, even at the cost of severe performance loss.
+
+[heading Description]
+Related types: [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``virtual directory_entry handle::direntry(metadata_flags wanted = directory_entry::metadata_fastpath())``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[metadata_flags] [] [wanted] [The metadata wanted.]]
+]
+
+
+[heading Returns]
+A directory entry for this handle.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD..Race free if handle open for directories and regular files only, else if handle closed or a symlink race free up to the containing directory. All metadata is fetched in a single shot.]
+[raceguarantee Linux..Race free if handle open, else if handle closed race free up to the containing directory. All metadata is fetched in a single shot.]
+[raceguarantee OS X..Race free if handle open for directories and regular files only. No guarantees if handle closed or a symlink.]
+[raceguarantee Windows..Handle must be open and is always race free. Metadata may be fetched in a single shot if at least two categories requested, or else the following categories apply: (i) ino (ii) type, atim, mtim, ctim, birthtim, sparse, compressed (iii) nlink, size, allocated, blocks.]
+ ]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:target target]
+'''<?dbhtml-include href="disqus_identifiers/target.html"?>'''
+
+'''<indexterm><primary>target</primary></indexterm>'''
+Returns the target path of this handle if it is a symbolic link.
+
+[heading Description]
+Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``virtual path handle::target()``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+]
+
+
+[heading Returns]
+The path the symbolic link points to. May not exist or even be valid.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD..Race free up to the containing directory.]
+[raceguarantee Linux, Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:link link]
+'''<?dbhtml-include href="disqus_identifiers/link.html"?>'''
+
+'''<indexterm><primary>link</primary></indexterm>'''
+Hard links the file to a new location on the same volume.
+
+[heading Description]
+If you wish to make a temporary file whose contents are ready appear at a location and error out if a file entry is already there, use link() and if success, unlink() on the former location. If you wish to always overwrite the destination, use atomic\u005frelink() instead.
+
+On Windows, the destination directory cannot have any handle opened to it with delete/rename privileges ([^`file_flags::write`]) anywhere in the system. This is an operating system limitation.
+
+Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+Related types: [^`__afio_path_req__`]
+
+[heading Synopsis]
+``virtual void handle::link(const path_req & req)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const path_req &] [] [req] [The absolute or relative (in which case precondition specifies a directory) path to create a hard link at.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD..Race free up to the containing directory for both source and target.]
+[raceguarantee Linux, Windows..Race free for source if handle open, else up to the containing directory. Race free up to the target directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:unlink unlink]
+'''<?dbhtml-include href="disqus_identifiers/unlink.html"?>'''
+
+'''<indexterm><primary>unlink</primary></indexterm>'''
+Unlinks the file from its present location as determined by path(true), which could be any hard link on those operating systems with an unstable path(true). Other links may remain to the same file.
+
+[heading Description]
+On Microsoft Windows, this routine unlinks items as follows:
+
+The reason for such complexity is that this algorithm, if it renames successfully, neatly works around a number of annoying features in Windows, specifically that when you delete a file you actually don't delete it till an unknown amount of time later. This breaks code which tries to delete a directory tree, and finds that the directories won't delete because they still contain files supposedly deleted but actually not quite yet. By renaming the items as far away as possible, this problem ought to go away - unless of course that the user does not have permissions to write into any directory other than the one being eventually deleted, in which case you will still see the strange access denied and directory not empty errors from before.
+
+Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+Related types: [^`__afio_path_req__`]
+
+[heading Synopsis]
+``virtual void handle::unlink()``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:atomic_relink atomic_relink]
+'''<?dbhtml-include href="disqus_identifiers/atomic_relink.html"?>'''
+
+'''<indexterm><primary>atomic_relink</primary></indexterm>'''
+Links the file to a new location and unlinks the file from its present location as determined by path(true), ['atomically overwriting any file entry at the new location]. Very useful for preparing file content elsewhere and once ready, atomically making it visible at some named location to other processes. Note that operating systems with an unstable path(true) may relink any hard link to the file to the new location.
+
+[heading Description]
+Note that not all filing systems guarantee the atomicity of the relink itself (i.e. the file may appear at two locations in the filing system for a period of time), though all supported platforms do guarantee the atomicity of the replaced location i.e. the location you are relinking to will always refer to some valid file to all readers, and will never be deleted or missing. Some filing systems may also fail to do the unlink if power is lost close to the relinking operation.
+
+On Windows, the destination directory cannot have any handle opened to it with delete/rename privileges ([^`file_flags::write`]) anywhere in the system. This is an operating system limitation.
+
+Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+Related types: [^`__afio_path_req__`]
+
+[heading Synopsis]
+``virtual void handle::atomic_relink(const path_req & req)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const path_req &] [] [req] [The absolute or relative (in which case precondition specifies a directory) path to relink to.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory for both source and target.]
+[raceguarantee OS X..No guarantees.]
+[raceguarantee Windows..Race free for source if handle open, else up to the containing directory. Race free up to the target directory.]
+ ]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_async_op_flags.qbk b/attic/doc/generated/group_async_op_flags.qbk
new file mode 100644
index 00000000..57d6b52c
--- /dev/null
+++ b/attic/doc/generated/group_async_op_flags.qbk
@@ -0,0 +1,38 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__async__op__flags.xml]
+[section:async_op_flags async_op_flags]
+'''<?dbhtml-include href="disqus_identifiers/async_op_flags.html"?>'''
+
+'''<indexterm><primary>async_op_flags</primary></indexterm>'''
+'''<indexterm><primary>none</primary></indexterm>'''
+'''<indexterm><primary>immediate</primary></indexterm>'''
+Bitwise async_op_flags flags.
+
+[heading Synopsis]
+``enum async_op_flags {none = 0, immediate = 1};``
+
+[heading Values]
+
+[table
+[[Value] [Description] ]
+[[none] [No flags set. ]]
+[[immediate] [Call chained completion immediately instead of scheduling for later. Make SURE your completion can not block! ]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_close.qbk b/attic/doc/generated/group_close.qbk
new file mode 100644
index 00000000..32edb99d
--- /dev/null
+++ b/attic/doc/generated/group_close.qbk
@@ -0,0 +1,142 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__close.xml]
+[section:close_1_batch close (batch)]
+'''<?dbhtml-include href="disqus_identifiers/close_1_batch.html"?>'''
+
+'''<indexterm><primary>close</primary></indexterm>'''
+Schedule a batch of asynchronous file or directory handle closes after preceding operations.
+
+[heading Description]
+Note this is ignored for handles where available\u005fto\u005fdirectory\u005fcache() is true as those cannot be explicitly closed. Note that failure to explicitly schedule closing a file handle using this call means it will be [*synchronously] closed on last reference count by [^`__afio_handle__`]. This can consume considerable time, especially if SyncOnClose is enabled.
+
+[heading Synopsis]
+``future dispatcher::close(const std::vector< future<>> & ops)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< future<>> &] [] [ops] [A batch of op handles.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if closing handles is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_close async_close]
+'''<?dbhtml-include href="disqus_identifiers/async_close.html"?>'''
+
+'''<indexterm><primary>async_close</primary></indexterm>'''
+Asynchronous file or directory handle close after a preceding operation.
+
+[heading Description]
+Note this is ignored for handles where available\u005fto\u005fdirectory\u005fcache() is true as those cannot be explicitly closed. Note that failure to explicitly schedule closing a file handle using this call means it will be [*synchronously] closed on last reference count by [^`__afio_handle__`]. This can consume considerable time, especially if SyncOnClose is enabled.
+
+[heading Synopsis]
+``future async_close(future<> _precondition)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:close_1_throwing close (throwing)]
+'''<?dbhtml-include href="disqus_identifiers/close_1_throwing.html"?>'''
+
+'''<indexterm><primary>close</primary></indexterm>'''
+Synchronous file or directory handle close after a preceding operation.
+
+[heading Description]
+Note this is ignored for handles where available\u005fto\u005fdirectory\u005fcache() is true as those cannot be explicitly closed. Note that failure to explicitly schedule closing a file handle using this call means it will be [*synchronously] closed on last reference count by [^`__afio_handle__`]. This can consume considerable time, especially if SyncOnClose is enabled.
+
+[heading Synopsis]
+``void close(future<> _precondition)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:close_2_non_throwing close (non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/close_2_non_throwing.html"?>'''
+
+'''<indexterm><primary>close</primary></indexterm>'''
+Synchronous file or directory handle close after a preceding operation.
+
+[heading Description]
+Note this is ignored for handles where available\u005fto\u005fdirectory\u005fcache() is true as those cannot be explicitly closed. Note that failure to explicitly schedule closing a file handle using this call means it will be [*synchronously] closed on last reference count by [^`__afio_handle__`]. This can consume considerable time, especially if SyncOnClose is enabled.
+
+[heading Synopsis]
+``void close(error_code & _ec, future<> _precondition)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_dir.qbk b/attic/doc/generated/group_dir.qbk
new file mode 100644
index 00000000..73aaa7f7
--- /dev/null
+++ b/attic/doc/generated/group_dir.qbk
@@ -0,0 +1,298 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dir.xml]
+[section:dir_1_batch dir (batch)]
+'''<?dbhtml-include href="disqus_identifiers/dir_1_batch.html"?>'''
+
+'''<indexterm><primary>dir</primary></indexterm>'''
+Schedule a batch of asynchronous directory creations and opens after optional preconditions.
+
+[heading Description]
+Note that if there is already a handle open to the directory requested, that will be returned instead of a new handle unless file\u005fflags::unique\u005fdirectory\u005fhandle is specified. For such handles where available\u005fto\u005fdirectory\u005fcache() is true, they cannot be explicitly closed either, you must let the reference count reach zero for that to happen. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``future dispatcher::dir(const std::vector< path_req > & reqs)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< path_req > &] [] [reqs] [A batch of [^`path_req`] structures.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if directory creation is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_dir_3_relative async_dir (relative)]
+'''<?dbhtml-include href="disqus_identifiers/async_dir_3_relative.html"?>'''
+
+'''<indexterm><primary>async_dir</primary></indexterm>'''
+Asynchronous directory creation and open after an optional precondition.
+
+[heading Description]
+Note that if there is already a handle open to the directory requested, that will be returned instead of a new handle unless file\u005fflags::unique\u005fdirectory\u005fhandle is specified. For such handles where available\u005fto\u005fdirectory\u005fcache() is true, they cannot be explicitly closed either, you must let the reference count reach zero for that to happen. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+future async_dir(future<> _precondition, T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_dir_2_absolute async_dir (absolute)]
+'''<?dbhtml-include href="disqus_identifiers/async_dir_2_absolute.html"?>'''
+
+'''<indexterm><primary>async_dir</primary></indexterm>'''
+Asynchronous directory creation and open after an optional precondition.
+
+[heading Description]
+Note that if there is already a handle open to the directory requested, that will be returned instead of a new handle unless file\u005fflags::unique\u005fdirectory\u005fhandle is specified. For such handles where available\u005fto\u005fdirectory\u005fcache() is true, they cannot be explicitly closed either, you must let the reference count reach zero for that to happen. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+future async_dir(T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:dir_3_relative_throwing dir (relative throwing)]
+'''<?dbhtml-include href="disqus_identifiers/dir_3_relative_throwing.html"?>'''
+
+'''<indexterm><primary>dir</primary></indexterm>'''
+Synchronous directory creation and open after an optional precondition.
+
+[heading Description]
+Note that if there is already a handle open to the directory requested, that will be returned instead of a new handle unless file\u005fflags::unique\u005fdirectory\u005fhandle is specified. For such handles where available\u005fto\u005fdirectory\u005fcache() is true, they cannot be explicitly closed either, you must let the reference count reach zero for that to happen. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+handle_ptr dir(future<> _precondition, T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the directory.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:dir_2_absolute_throwing dir (absolute throwing)]
+'''<?dbhtml-include href="disqus_identifiers/dir_2_absolute_throwing.html"?>'''
+
+'''<indexterm><primary>dir</primary></indexterm>'''
+Synchronous directory creation and open after an optional precondition.
+
+[heading Description]
+Note that if there is already a handle open to the directory requested, that will be returned instead of a new handle unless file\u005fflags::unique\u005fdirectory\u005fhandle is specified. For such handles where available\u005fto\u005fdirectory\u005fcache() is true, they cannot be explicitly closed either, you must let the reference count reach zero for that to happen. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+handle_ptr dir(T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the directory.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:dir_4_relative_non_throwing dir (relative non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/dir_4_relative_non_throwing.html"?>'''
+
+'''<indexterm><primary>dir</primary></indexterm>'''
+Synchronous directory creation and open after an optional precondition.
+
+[heading Description]
+Note that if there is already a handle open to the directory requested, that will be returned instead of a new handle unless file\u005fflags::unique\u005fdirectory\u005fhandle is specified. For such handles where available\u005fto\u005fdirectory\u005fcache() is true, they cannot be explicitly closed either, you must let the reference count reach zero for that to happen. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+handle_ptr dir(error_code & _ec, future<> _precondition, T _path,
+ file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the directory.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:dir_3_absolute_non_throwing dir (absolute non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/dir_3_absolute_non_throwing.html"?>'''
+
+'''<indexterm><primary>dir</primary></indexterm>'''
+Synchronous directory creation and open after an optional precondition.
+
+[heading Description]
+Note that if there is already a handle open to the directory requested, that will be returned instead of a new handle unless file\u005fflags::unique\u005fdirectory\u005fhandle is specified. For such handles where available\u005fto\u005fdirectory\u005fcache() is true, they cannot be explicitly closed either, you must let the reference count reach zero for that to happen. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+handle_ptr dir(error_code & _ec, T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the directory.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_dispatcher__barrier.qbk b/attic/doc/generated/group_dispatcher__barrier.qbk
new file mode 100644
index 00000000..6e5ee3f2
--- /dev/null
+++ b/attic/doc/generated/group_dispatcher__barrier.qbk
@@ -0,0 +1,11 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dispatcher____barrier.xml]
diff --git a/attic/doc/generated/group_dispatcher__call.qbk b/attic/doc/generated/group_dispatcher__call.qbk
new file mode 100644
index 00000000..8773a70d
--- /dev/null
+++ b/attic/doc/generated/group_dispatcher__call.qbk
@@ -0,0 +1,11 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dispatcher____call.xml]
diff --git a/attic/doc/generated/group_dispatcher__completion.qbk b/attic/doc/generated/group_dispatcher__completion.qbk
new file mode 100644
index 00000000..04cf467b
--- /dev/null
+++ b/attic/doc/generated/group_dispatcher__completion.qbk
@@ -0,0 +1,11 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dispatcher____completion.xml]
diff --git a/attic/doc/generated/group_dispatcher__depends.qbk b/attic/doc/generated/group_dispatcher__depends.qbk
new file mode 100644
index 00000000..c12823f0
--- /dev/null
+++ b/attic/doc/generated/group_dispatcher__depends.qbk
@@ -0,0 +1,43 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dispatcher____depends.xml]
+[section:depends depends]
+'''<?dbhtml-include href="disqus_identifiers/depends.html"?>'''
+
+'''<indexterm><primary>depends</primary></indexterm>'''
+Schedule the return of an op handle after another op handle completes. This is useful when you need to supply one op handle to a function but it must not begin until another op handle has finished.
+
+[heading Synopsis]
+``future dispatcher::depends(future<> precondition, future<> op)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [precondition] [The op handle which must complete for op to be passed through. ]]
+[[future<>] [] [op] [The op handle to return.]]
+]
+
+
+[heading Returns]
+The op handle op.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][filecopy_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_dispatcher__enumerate.qbk b/attic/doc/generated/group_dispatcher__enumerate.qbk
new file mode 100644
index 00000000..aa907da4
--- /dev/null
+++ b/attic/doc/generated/group_dispatcher__enumerate.qbk
@@ -0,0 +1,11 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dispatcher____enumerate.xml]
diff --git a/attic/doc/generated/group_dispatcher__extents.qbk b/attic/doc/generated/group_dispatcher__extents.qbk
new file mode 100644
index 00000000..9d0b6b64
--- /dev/null
+++ b/attic/doc/generated/group_dispatcher__extents.qbk
@@ -0,0 +1,11 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dispatcher____extents.xml]
diff --git a/attic/doc/generated/group_dispatcher__filedirops.qbk b/attic/doc/generated/group_dispatcher__filedirops.qbk
new file mode 100644
index 00000000..db5169d7
--- /dev/null
+++ b/attic/doc/generated/group_dispatcher__filedirops.qbk
@@ -0,0 +1,11 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dispatcher____filedirops.xml]
diff --git a/attic/doc/generated/group_dispatcher__filter.qbk b/attic/doc/generated/group_dispatcher__filter.qbk
new file mode 100644
index 00000000..3cc4cd16
--- /dev/null
+++ b/attic/doc/generated/group_dispatcher__filter.qbk
@@ -0,0 +1,11 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dispatcher____filter.xml]
diff --git a/attic/doc/generated/group_dispatcher__misc.qbk b/attic/doc/generated/group_dispatcher__misc.qbk
new file mode 100644
index 00000000..c187457d
--- /dev/null
+++ b/attic/doc/generated/group_dispatcher__misc.qbk
@@ -0,0 +1,68 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dispatcher____misc.xml]
+[section:complete_async_op_3_normal complete_async_op (normal)]
+'''<?dbhtml-include href="disqus_identifiers/complete_async_op_3_normal.html"?>'''
+
+'''<indexterm><primary>complete_async_op</primary></indexterm>'''
+Completes an operation with a handle or an error, usually used when an operation was previously deferred.
+
+[heading Synopsis]
+``void dispatcher::complete_async_op(size_t id, handle_ptr h, exception_ptr e = exception_ptr())``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[size_t] [] [id] []]
+[[handle_ptr] [] [h] []]
+[[exception_ptr] [] [e] []]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]O(N) where N is the number of completions dependent on this op.
+[heading Exception Model]Should not throw any exception except for out of memory.
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:complete_async_op_2_errored complete_async_op (errored)]
+'''<?dbhtml-include href="disqus_identifiers/complete_async_op_2_errored.html"?>'''
+
+'''<indexterm><primary>complete_async_op</primary></indexterm>'''
+Completes an operation with an error, usually used when an operation was previously deferred.
+
+[heading Synopsis]
+``void dispatcher::complete_async_op(size_t id, exception_ptr e)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[size_t] [] [id] []]
+[[exception_ptr] [] [e] []]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]O(N) where N is the number of completions dependent on this op.
+[heading Exception Model]Should not throw any exception except for out of memory.
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_dispatcher__statfs.qbk b/attic/doc/generated/group_dispatcher__statfs.qbk
new file mode 100644
index 00000000..3df8a8fe
--- /dev/null
+++ b/attic/doc/generated/group_dispatcher__statfs.qbk
@@ -0,0 +1,11 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__dispatcher____statfs.xml]
diff --git a/attic/doc/generated/group_enumerate.qbk b/attic/doc/generated/group_enumerate.qbk
new file mode 100644
index 00000000..4f78c141
--- /dev/null
+++ b/attic/doc/generated/group_enumerate.qbk
@@ -0,0 +1,481 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__enumerate.xml]
+[section:enumerate_1_batch enumerate (batch)]
+'''<?dbhtml-include href="disqus_identifiers/enumerate_1_batch.html"?>'''
+
+'''<indexterm><primary>enumerate</primary></indexterm>'''
+Schedule a batch of asynchronous directory enumerations after preceding operations.
+
+[heading Description]
+By default dir() returns shared handles i.e. dir( foo) and dir( foo) will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file\u005fflags::unique\u005fdirectory\u005fhandle flag. Note that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time. Related types: [^`__afio_enumerate_req__`], [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``future< std::pair< std::vector< directory_entry >, bool > > dispatcher::enumerate(const std::vector< enumerate_req > & reqs)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< enumerate_req > &] [] [reqs] [A batch of enumeration requests.]]
+]
+
+
+[heading Returns]
+A batch of stl\u005ffuture vectors of directory entries with boolean returning false if done.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+ ]
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][enumerate_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_enumerate_6_maxitems_first async_enumerate (maxitems first)]
+'''<?dbhtml-include href="disqus_identifiers/async_enumerate_6_maxitems_first.html"?>'''
+
+'''<indexterm><primary>async_enumerate</primary></indexterm>'''
+Asynchronous directory enumeration after a preceding operation.
+
+[heading Description]
+By default dir() returns shared handles i.e. dir( foo) and dir( foo) will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file\u005fflags::unique\u005fdirectory\u005fhandle flag. Note that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time. Related types: [^`__afio_enumerate_req__`], [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``future<std::pair<std::vector<directory_entry>, bool> > async_enumerate(future<> _precondition, size_t _maxitems = 2, bool _restart = true,
+ path _glob = path(), metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[size_t] [] [_maxitems] [The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. ]]
+[[bool] [] [_restart] [Restarts the enumeration for this open directory handle. ]]
+[[path] [] [_glob] [An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. ]]
+[[metadata_flags] [] [_metadata] [The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. ]]
+[[enumerate_req::filter] [] [_filtering] [Any filtering you want AFIO to do for you.]]
+]
+
+
+[heading Returns]
+A [^`future<std::pair<std::vector<``directory_entry``>, bool>>`]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][enumerate_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:enumerate_6_max_items_first_throwing enumerate (max items first throwing)]
+'''<?dbhtml-include href="disqus_identifiers/enumerate_6_max_items_first_throwing.html"?>'''
+
+'''<indexterm><primary>enumerate</primary></indexterm>'''
+Synchronous directory enumeration after a preceding operation.
+
+[heading Description]
+By default dir() returns shared handles i.e. dir( foo) and dir( foo) will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file\u005fflags::unique\u005fdirectory\u005fhandle flag. Note that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time. Related types: [^`__afio_enumerate_req__`], [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``std::pair<std::vector<directory_entry>, bool> enumerate(future<> _precondition, size_t _maxitems = 2, bool _restart = true,
+ path _glob = path(), metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[size_t] [] [_maxitems] [The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. ]]
+[[bool] [] [_restart] [Restarts the enumeration for this open directory handle. ]]
+[[path] [] [_glob] [An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. ]]
+[[metadata_flags] [] [_metadata] [The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. ]]
+[[enumerate_req::filter] [] [_filtering] [Any filtering you want AFIO to do for you.]]
+]
+
+
+[heading Returns]
+A vector of results and a bool indicating if there is more.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][enumerate_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:enumerate_7_max_items_first_non_throwing enumerate (max items first non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/enumerate_7_max_items_first_non_throwing.html"?>'''
+
+'''<indexterm><primary>enumerate</primary></indexterm>'''
+Synchronous directory enumeration after a preceding operation.
+
+[heading Description]
+By default dir() returns shared handles i.e. dir( foo) and dir( foo) will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file\u005fflags::unique\u005fdirectory\u005fhandle flag. Note that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time. Related types: [^`__afio_enumerate_req__`], [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``std::pair<std::vector<directory_entry>, bool> enumerate(error_code & _ec, future<> _precondition, size_t _maxitems = 2,
+ bool _restart = true, path _glob = path(), metadata_flags _metadata = metadata_flags::None,
+ enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[size_t] [] [_maxitems] [The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. ]]
+[[bool] [] [_restart] [Restarts the enumeration for this open directory handle. ]]
+[[path] [] [_glob] [An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. ]]
+[[metadata_flags] [] [_metadata] [The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. ]]
+[[enumerate_req::filter] [] [_filtering] [Any filtering you want AFIO to do for you.]]
+]
+
+
+[heading Returns]
+A vector of results and a bool indicating if there is more.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][enumerate_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_enumerate_6_glob_first async_enumerate (glob first)]
+'''<?dbhtml-include href="disqus_identifiers/async_enumerate_6_glob_first.html"?>'''
+
+'''<indexterm><primary>async_enumerate</primary></indexterm>'''
+Asynchronous directory enumeration after a preceding operation.
+
+[heading Description]
+By default dir() returns shared handles i.e. dir( foo) and dir( foo) will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file\u005fflags::unique\u005fdirectory\u005fhandle flag. Note that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time. Related types: [^`__afio_enumerate_req__`], [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``future<std::pair<std::vector<directory_entry>, bool> > async_enumerate(future<> _precondition, path _glob, size_t _maxitems = 2,
+ bool _restart = true, metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[path] [] [_glob] [A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. ]]
+[[size_t] [] [_maxitems] [The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. ]]
+[[bool] [] [_restart] [Restarts the enumeration for this open directory handle. ]]
+[[metadata_flags] [] [_metadata] [The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. ]]
+[[enumerate_req::filter] [] [_filtering] [Any filtering you want AFIO to do for you.]]
+]
+
+
+[heading Returns]
+A [^`future<std::pair<std::vector<``directory_entry``>, bool>>`]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][enumerate_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:enumerate_6_glob_first_throwing enumerate (glob first throwing)]
+'''<?dbhtml-include href="disqus_identifiers/enumerate_6_glob_first_throwing.html"?>'''
+
+'''<indexterm><primary>enumerate</primary></indexterm>'''
+Synchronous directory enumeration after a preceding operation.
+
+[heading Description]
+By default dir() returns shared handles i.e. dir( foo) and dir( foo) will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file\u005fflags::unique\u005fdirectory\u005fhandle flag. Note that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time. Related types: [^`__afio_enumerate_req__`], [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``std::pair<std::vector<directory_entry>, bool> enumerate(future<> _precondition, path _glob, size_t _maxitems = 2,
+ bool _restart = true, metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[path] [] [_glob] [A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. ]]
+[[size_t] [] [_maxitems] [The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. ]]
+[[bool] [] [_restart] [Restarts the enumeration for this open directory handle. ]]
+[[metadata_flags] [] [_metadata] [The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. ]]
+[[enumerate_req::filter] [] [_filtering] [Any filtering you want AFIO to do for you.]]
+]
+
+
+[heading Returns]
+A vector of results and a bool indicating if there is more.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][enumerate_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:enumerate_7_glob_first_non_throwing enumerate (glob first non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/enumerate_7_glob_first_non_throwing.html"?>'''
+
+'''<indexterm><primary>enumerate</primary></indexterm>'''
+Synchronous directory enumeration after a preceding operation.
+
+[heading Description]
+By default dir() returns shared handles i.e. dir( foo) and dir( foo) will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file\u005fflags::unique\u005fdirectory\u005fhandle flag. Note that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time. Related types: [^`__afio_enumerate_req__`], [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``std::pair<std::vector<directory_entry>, bool> enumerate(error_code & _ec, future<> _precondition, path _glob,
+ size_t _maxitems = 2, bool _restart = true, metadata_flags _metadata = metadata_flags::None,
+ enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[path] [] [_glob] [A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. ]]
+[[size_t] [] [_maxitems] [The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. ]]
+[[bool] [] [_restart] [Restarts the enumeration for this open directory handle. ]]
+[[metadata_flags] [] [_metadata] [The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. ]]
+[[enumerate_req::filter] [] [_filtering] [Any filtering you want AFIO to do for you.]]
+]
+
+
+[heading Returns]
+A vector of results and a bool indicating if there is more.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][enumerate_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_enumerate_6_metadata_first async_enumerate (metadata first)]
+'''<?dbhtml-include href="disqus_identifiers/async_enumerate_6_metadata_first.html"?>'''
+
+'''<indexterm><primary>async_enumerate</primary></indexterm>'''
+Asynchronous directory enumeration after a preceding operation.
+
+[heading Description]
+By default dir() returns shared handles i.e. dir( foo) and dir( foo) will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file\u005fflags::unique\u005fdirectory\u005fhandle flag. Note that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time. Related types: [^`__afio_enumerate_req__`], [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``future<std::pair<std::vector<directory_entry>, bool> > async_enumerate(future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2,
+ bool _restart = true, path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[metadata_flags] [] [_metadata] [The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. ]]
+[[size_t] [] [_maxitems] [The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. ]]
+[[bool] [] [_restart] [Restarts the enumeration for this open directory handle. ]]
+[[path] [] [_glob] [An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. ]]
+[[enumerate_req::filter] [] [_filtering] [Any filtering you want AFIO to do for you.]]
+]
+
+
+[heading Returns]
+A [^`future<std::pair<std::vector<``directory_entry``>, bool>>`]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][enumerate_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:enumerate_6_metadata_first_throwing enumerate (metadata first throwing)]
+'''<?dbhtml-include href="disqus_identifiers/enumerate_6_metadata_first_throwing.html"?>'''
+
+'''<indexterm><primary>enumerate</primary></indexterm>'''
+Synchronous directory enumeration after a preceding operation.
+
+[heading Description]
+By default dir() returns shared handles i.e. dir( foo) and dir( foo) will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file\u005fflags::unique\u005fdirectory\u005fhandle flag. Note that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time. Related types: [^`__afio_enumerate_req__`], [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``std::pair<std::vector<directory_entry>, bool> enumerate(future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2,
+ bool _restart = true, path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[metadata_flags] [] [_metadata] [The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. ]]
+[[size_t] [] [_maxitems] [The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. ]]
+[[bool] [] [_restart] [Restarts the enumeration for this open directory handle. ]]
+[[path] [] [_glob] [An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. ]]
+[[enumerate_req::filter] [] [_filtering] [Any filtering you want AFIO to do for you.]]
+]
+
+
+[heading Returns]
+A vector of results and a bool indicating if there is more.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][enumerate_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:enumerate_7_metadata_first_non_throwing enumerate (metadata first non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/enumerate_7_metadata_first_non_throwing.html"?>'''
+
+'''<indexterm><primary>enumerate</primary></indexterm>'''
+Synchronous directory enumeration after a preceding operation.
+
+[heading Description]
+By default dir() returns shared handles i.e. dir( foo) and dir( foo) will return the exact same handle, and therefore enumerating not all of the entries at once is a race condition. The solution is to either set maxitems to a value large enough to guarantee a directory will be enumerated in a single shot, or to open a separate directory handle using the file\u005fflags::unique\u005fdirectory\u005fhandle flag. Note that setting maxitems=1 will often cause a buffer space exhaustion, causing a second syscall with an enlarged buffer. This is because AFIO cannot know if the allocated buffer can hold all of the filename being retrieved, so it may have to retry. Put another way, setting maxitems=1 will give you the worst performance possible, whereas maxitems=2 will probably only return one item most of the time. Related types: [^`__afio_enumerate_req__`], [^`__afio_directory_entry__`], [^`__afio_stat_t__`]
+
+[heading Synopsis]
+``std::pair<std::vector<directory_entry>, bool> enumerate(error_code & _ec, future<> _precondition, metadata_flags _metadata,
+ size_t _maxitems = 2, bool _restart = true, path _glob = path(),
+ enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[metadata_flags] [] [_metadata] [The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free. ]]
+[[size_t] [] [_maxitems] [The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls. ]]
+[[bool] [] [_restart] [Restarts the enumeration for this open directory handle. ]]
+[[path] [] [_glob] [An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX. ]]
+[[enumerate_req::filter] [] [_filtering] [Any filtering you want AFIO to do for you.]]
+]
+
+
+[heading Returns]
+A vector of results and a bool indicating if there is more.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][enumerate_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_extents.qbk b/attic/doc/generated/group_extents.qbk
new file mode 100644
index 00000000..e3da8fe3
--- /dev/null
+++ b/attic/doc/generated/group_extents.qbk
@@ -0,0 +1,172 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__extents.xml]
+[section:extents_1_batch extents (batch)]
+'''<?dbhtml-include href="disqus_identifiers/extents_1_batch.html"?>'''
+
+'''<indexterm><primary>extents</primary></indexterm>'''
+Schedule a batch of asynchronous extent enumerations after preceding operations.
+
+[heading Description]
+In a sparsely allocated file, it can be useful to know which extents contain non-zero data. Note that this call is racy (i.e. the extents are enumerated one by one on some platforms, this means they may be out of date with respect to one another) when other threads or processes are concurrently calling zero() or write() - this is a host OS API limitation.
+
+[heading Synopsis]
+``future< std::vector< std::pair< off_t, off_t > > > dispatcher::extents(const std::vector< future<>> & ops)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< future<>> &] [] [ops] [A batch of op handles.]]
+]
+
+
+[heading Returns]
+A batch of stl\u005ffuture vectors of extents.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
+before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
+into single extents.]
+[raceguarantee Windows..Race free.]
+ ]
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of extents in each file.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][extents_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_extents async_extents]
+'''<?dbhtml-include href="disqus_identifiers/async_extents.html"?>'''
+
+'''<indexterm><primary>async_extents</primary></indexterm>'''
+Asynchronous extent enumeration after a preceding operation.
+
+[heading Description]
+In a sparsely allocated file, it can be useful to know which extents contain non-zero data. Note that this call is racy (i.e. the extents are enumerated one by one on some platforms, this means they may be out of date with respect to one another) when other threads or processes are concurrently calling zero() or write() - this is a host OS API limitation.
+
+[heading Synopsis]
+``future<std::vector<std::pair<off_t, off_t> > > async_extents(future<> _precondition)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use.]]
+]
+
+
+[heading Returns]
+A [^`future<std::vector<std::pair<off_t, off_t>>>`]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
+before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
+into single extents.]
+[raceguarantee Windows..Race free.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][extents_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:extents_1_throwing extents (throwing)]
+'''<?dbhtml-include href="disqus_identifiers/extents_1_throwing.html"?>'''
+
+'''<indexterm><primary>extents</primary></indexterm>'''
+Synchronous extent enumeration after a preceding operation.
+
+[heading Description]
+In a sparsely allocated file, it can be useful to know which extents contain non-zero data. Note that this call is racy (i.e. the extents are enumerated one by one on some platforms, this means they may be out of date with respect to one another) when other threads or processes are concurrently calling zero() or write() - this is a host OS API limitation.
+
+[heading Synopsis]
+``std::vector<std::pair<off_t, off_t> > extents(future<> _precondition)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use.]]
+]
+
+
+[heading Returns]
+A vector of extents
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
+before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
+into single extents.]
+[raceguarantee Windows..Race free.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][extents_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:extents_2_non_throwing extents (non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/extents_2_non_throwing.html"?>'''
+
+'''<indexterm><primary>extents</primary></indexterm>'''
+Synchronous extent enumeration after a preceding operation.
+
+[heading Description]
+In a sparsely allocated file, it can be useful to know which extents contain non-zero data. Note that this call is racy (i.e. the extents are enumerated one by one on some platforms, this means they may be out of date with respect to one another) when other threads or processes are concurrently calling zero() or write() - this is a host OS API limitation.
+
+[heading Synopsis]
+``std::vector<std::pair<off_t, off_t> > extents(error_code & _ec, future<> _precondition)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use.]]
+]
+
+
+[heading Returns]
+A vector of extents
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
+before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
+into single extents.]
+[raceguarantee Windows..Race free.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][extents_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_file.qbk b/attic/doc/generated/group_file.qbk
new file mode 100644
index 00000000..e616497c
--- /dev/null
+++ b/attic/doc/generated/group_file.qbk
@@ -0,0 +1,301 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__file.xml]
+[section:file_1_batch file (batch)]
+'''<?dbhtml-include href="disqus_identifiers/file_1_batch.html"?>'''
+
+'''<indexterm><primary>file</primary></indexterm>'''
+Schedule a batch of asynchronous file creations and opens after optional preconditions.
+
+[heading Description]
+Be aware that any files created are by default sparse if supported on the local filing system. On Windows opening any file for writing converts it to sparse. Use file\u005fflags::no\u005fsparse to prevent this on those filing systems which permit it. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``future dispatcher::file(const std::vector< path_req > & reqs)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< path_req > &] [] [reqs] [A batch of [^`path_req`] structures.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if file creation is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_file_3_relative async_file (relative)]
+'''<?dbhtml-include href="disqus_identifiers/async_file_3_relative.html"?>'''
+
+'''<indexterm><primary>async_file</primary></indexterm>'''
+Asynchronous file creation and open after an optional precondition.
+
+[heading Description]
+Be aware that any files created are by default sparse if supported on the local filing system. On Windows opening any file for writing converts it to sparse. Use file\u005fflags::no\u005fsparse to prevent this on those filing systems which permit it. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+future async_file(future<> _precondition, T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_file_2_absolute async_file (absolute)]
+'''<?dbhtml-include href="disqus_identifiers/async_file_2_absolute.html"?>'''
+
+'''<indexterm><primary>async_file</primary></indexterm>'''
+Asynchronous file creation and open after an optional precondition.
+
+[heading Description]
+Be aware that any files created are by default sparse if supported on the local filing system. On Windows opening any file for writing converts it to sparse. Use file\u005fflags::no\u005fsparse to prevent this on those filing systems which permit it. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+future async_file(T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:file_3_relative_throwing file (relative throwing)]
+'''<?dbhtml-include href="disqus_identifiers/file_3_relative_throwing.html"?>'''
+
+'''<indexterm><primary>file</primary></indexterm>'''
+Synchronous file creation and open after an optional precondition.
+
+[heading Description]
+Be aware that any files created are by default sparse if supported on the local filing system. On Windows opening any file for writing converts it to sparse. Use file\u005fflags::no\u005fsparse to prevent this on those filing systems which permit it. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+handle_ptr file(future<> _precondition, T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the file.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:file_2_absolute_throwing file (absolute throwing)]
+'''<?dbhtml-include href="disqus_identifiers/file_2_absolute_throwing.html"?>'''
+
+'''<indexterm><primary>file</primary></indexterm>'''
+Synchronous file creation and open after an optional precondition.
+
+[heading Description]
+Be aware that any files created are by default sparse if supported on the local filing system. On Windows opening any file for writing converts it to sparse. Use file\u005fflags::no\u005fsparse to prevent this on those filing systems which permit it. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+handle_ptr file(T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the file.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:file_4_relative_non_throwing file (relative non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/file_4_relative_non_throwing.html"?>'''
+
+'''<indexterm><primary>file</primary></indexterm>'''
+Synchronous file creation and open after an optional precondition.
+
+[heading Description]
+Be aware that any files created are by default sparse if supported on the local filing system. On Windows opening any file for writing converts it to sparse. Use file\u005fflags::no\u005fsparse to prevent this on those filing systems which permit it. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+handle_ptr file(error_code & _ec, future<> _precondition, T _path,
+ file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the file.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:file_3_absolute_non_throwing file (absolute non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/file_3_absolute_non_throwing.html"?>'''
+
+'''<indexterm><primary>file</primary></indexterm>'''
+Synchronous file creation and open after an optional precondition.
+
+[heading Description]
+Be aware that any files created are by default sparse if supported on the local filing system. On Windows opening any file for writing converts it to sparse. Use file\u005fflags::no\u005fsparse to prevent this on those filing systems which permit it. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+handle_ptr file(error_code & _ec, T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the file.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_file_flags.qbk b/attic/doc/generated/group_file_flags.qbk
new file mode 100644
index 00000000..ca606662
--- /dev/null
+++ b/attic/doc/generated/group_file_flags.qbk
@@ -0,0 +1,84 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__file__flags.xml]
+[section:file_flags file_flags]
+'''<?dbhtml-include href="disqus_identifiers/file_flags.html"?>'''
+
+'''<indexterm><primary>file_flags</primary></indexterm>'''
+'''<indexterm><primary>none</primary></indexterm>'''
+'''<indexterm><primary>read</primary></indexterm>'''
+'''<indexterm><primary>write</primary></indexterm>'''
+'''<indexterm><primary>read_write</primary></indexterm>'''
+'''<indexterm><primary>append</primary></indexterm>'''
+'''<indexterm><primary>truncate</primary></indexterm>'''
+'''<indexterm><primary>create</primary></indexterm>'''
+'''<indexterm><primary>create_only_if_not_exist</primary></indexterm>'''
+'''<indexterm><primary>create_compressed</primary></indexterm>'''
+'''<indexterm><primary>will_be_sequentially_accessed</primary></indexterm>'''
+'''<indexterm><primary>will_be_randomly_accessed</primary></indexterm>'''
+'''<indexterm><primary>no_sparse</primary></indexterm>'''
+'''<indexterm><primary>hold_parent_open</primary></indexterm>'''
+'''<indexterm><primary>unique_directory_handle</primary></indexterm>'''
+'''<indexterm><primary>no_race_protection</primary></indexterm>'''
+'''<indexterm><primary>temporary_file</primary></indexterm>'''
+'''<indexterm><primary>delete_on_close</primary></indexterm>'''
+'''<indexterm><primary>os_direct</primary></indexterm>'''
+'''<indexterm><primary>os_lockable</primary></indexterm>'''
+'''<indexterm><primary>always_sync</primary></indexterm>'''
+'''<indexterm><primary>sync_on_close</primary></indexterm>'''
+'''<indexterm><primary>int_hold_parent_open_nested</primary></indexterm>'''
+'''<indexterm><primary>int_file_share_delete</primary></indexterm>'''
+'''<indexterm><primary>int_opening_link</primary></indexterm>'''
+'''<indexterm><primary>int_opening_dir</primary></indexterm>'''
+Bitwise file and directory open flags.
+
+[heading Synopsis]
+``enum file_flags {none = 0, read = 1, write = 2, read_write = 3, append = 4, truncate = 8, create = 16, create_only_if_not_exist = 32, create_compressed = 64, will_be_sequentially_accessed = 128, will_be_randomly_accessed = 256, no_sparse = 512, hold_parent_open = (1<<10), unique_directory_handle = (1<<11), no_race_protection = (1<<12), temporary_file = (1<<13), delete_on_close = (1<<14), os_direct = (1<<16), os_lockable = (1<<17), always_sync = (1<<24), sync_on_close = (1<<25), int_hold_parent_open_nested = (1<<27), int_file_share_delete = (1<<28), int_opening_link = (1<<29), int_opening_dir = (1<<30)};``
+
+[heading Values]
+
+[table
+[[Value] [Description] ]
+[[none] [No flags set. ]]
+[[read] [Read access. ]]
+[[write] [Write access. ]]
+[[read_write] [Read and write access. ]]
+[[append] [Append only. ]]
+[[truncate] [Truncate existing file to zero. ]]
+[[create] [Open and create if doesn't exist. Always creates sparse files if possible. ]]
+[[create_only_if_not_exist] [Create and open only if doesn't exist. ]]
+[[create_compressed] [Create a compressed file, needs to be combined with one of the other create flags. Only succeeds if supported by the underlying filing system. ]]
+[[will_be_sequentially_accessed] [Will be ['exclusively] either read or written sequentially. If you're exclusively writing sequentially, ['strongly] consider turning on [^`os_direct`] too. ]]
+[[will_be_randomly_accessed] [Will be randomly accessed, so don't bother with read-ahead. If you're using this, ['strongly] consider turning on [^`os_direct`] too. ]]
+[[no_sparse] [Don't create sparse files. May be ignored by some filing systems (e.g. ext4). ]]
+[[hold_parent_open] [Hold a file handle open to the containing directory of each open file for fast directory enumeration and fast relative path ops. ]]
+[[unique_directory_handle] [Return a unique directory handle rather than a shared directory handle. ]]
+[[no_race_protection] [Skip taking steps to avoid destruction of data due to filing system races. Most of the performance benefit of enabling this goes away if you enable HoldParentOpen instead, so be especially careful when considering turning this on. ]]
+[[temporary_file] [On some systems causes dirty cache data to not be written to physical storage until file close. Useful for temporary files and lock files, especially on Windows when combined with [^`delete_on_close`] as this avoids an fsync of the containing directory on file close. ]]
+[[delete_on_close] [Only when combined with [^`create_only_if_not_exist`], deletes the file on close. This is especially useful on Windows with temporary and lock files where normally closing a file is an implicit fsync of its containing directory. Note on POSIX this unlinks the file on first close by AFIO, whereas on Windows the operating system unlinks the file on last close including sudden application exit. Note also that AFIO permits you to delete files which are currently open on Windows and the file entry disappears immediately just as on POSIX. ]]
+[[os_direct] [Bypass the OS file buffers (only really useful for writing large files, or a lot of random reads and writes. Note you must 4Kb align everything if this is on). Be VERY careful mixing this with memory mapped files. ]]
+[[os_lockable] []]
+[[always_sync] [Ask the OS to not complete until the data is on the physical storage. Some filing systems do much better with this than [^`sync_on_close`]. ]]
+[[sync_on_close] [Automatically initiate an asynchronous flush just before file close, and fuse both operations so both must complete for close to complete. ]]
+[[int_hold_parent_open_nested] [Internal use only. Don't use. ]]
+[[int_file_share_delete] [Internal use only. Don't use. ]]
+[[int_opening_link] [Internal use only. Don't use. ]]
+[[int_opening_dir] [Internal use only. Don't use. ]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_fs_metadata_flags.qbk b/attic/doc/generated/group_fs_metadata_flags.qbk
new file mode 100644
index 00000000..ba555fbe
--- /dev/null
+++ b/attic/doc/generated/group_fs_metadata_flags.qbk
@@ -0,0 +1,66 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__fs__metadata__flags.xml]
+[section:fs_metadata_flags fs_metadata_flags]
+'''<?dbhtml-include href="disqus_identifiers/fs_metadata_flags.html"?>'''
+
+'''<indexterm><primary>fs_metadata_flags</primary></indexterm>'''
+'''<indexterm><primary>None</primary></indexterm>'''
+'''<indexterm><primary>flags</primary></indexterm>'''
+'''<indexterm><primary>bsize</primary></indexterm>'''
+'''<indexterm><primary>iosize</primary></indexterm>'''
+'''<indexterm><primary>blocks</primary></indexterm>'''
+'''<indexterm><primary>bfree</primary></indexterm>'''
+'''<indexterm><primary>bavail</primary></indexterm>'''
+'''<indexterm><primary>files</primary></indexterm>'''
+'''<indexterm><primary>ffree</primary></indexterm>'''
+'''<indexterm><primary>namemax</primary></indexterm>'''
+'''<indexterm><primary>owner</primary></indexterm>'''
+'''<indexterm><primary>fsid</primary></indexterm>'''
+'''<indexterm><primary>fstypename</primary></indexterm>'''
+'''<indexterm><primary>mntfromname</primary></indexterm>'''
+'''<indexterm><primary>mntonname</primary></indexterm>'''
+'''<indexterm><primary>All</primary></indexterm>'''
+Bitflags for availability of metadata from [^`struct ``statfs_t`]
+
+[heading Synopsis]
+``enum fs_metadata_flags {None = 0, flags = 1<<1, bsize = 1<<2, iosize = 1<<3, blocks = 1<<4, bfree = 1<<5, bavail = 1<<6, files = 1<<7, ffree = 1<<8, namemax = 1<<9, owner = 1<<10, fsid = 1<<11, fstypename = 1<<12, mntfromname = 1<<13, mntonname = 1<<14, All = (size_t)-1};``
+
+[heading Values]
+
+[table
+[[Value] [Description] ]
+[[None] []]
+[[flags] []]
+[[bsize] []]
+[[iosize] []]
+[[blocks] []]
+[[bfree] []]
+[[bavail] []]
+[[files] []]
+[[ffree] []]
+[[namemax] []]
+[[owner] []]
+[[fsid] []]
+[[fstypename] []]
+[[mntfromname] []]
+[[mntonname] []]
+[[All] [Return the maximum possible metadata. ]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_io_req.qbk b/attic/doc/generated/group_io_req.qbk
new file mode 100644
index 00000000..27a53414
--- /dev/null
+++ b/attic/doc/generated/group_io_req.qbk
@@ -0,0 +1,11 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__io__req.xml]
diff --git a/attic/doc/generated/group_macros.qbk b/attic/doc/generated/group_macros.qbk
new file mode 100644
index 00000000..1bc006c4
--- /dev/null
+++ b/attic/doc/generated/group_macros.qbk
@@ -0,0 +1,37 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__macros.xml]
+[section:boost_afio_validate_inputs BOOST_AFIO_VALIDATE_INPUTS]
+'''<?dbhtml-include href="disqus_identifiers/boost_afio_validate_inputs.html"?>'''
+
+'''<indexterm><primary>BOOST_AFIO_VALIDATE_INPUTS</primary></indexterm>'''
+Validate inputs at the point of instantiation.
+
+[heading Description]
+Turns on the checking of inputs for validity and throwing of exception conditions at the point of instantiation rather than waiting until those inputs are sent for dispatch. This, being very useful for debugging, defaults to 1 except when [^`NDEBUG`] is defined i.e. final release builds.
+
+[heading Synopsis]
+``#define BOOST_AFIO_VALIDATE_INPUTS``
+
+[heading Parameters]
+
+[table
+[[Name] [Description] ]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_make_io_req.qbk b/attic/doc/generated/group_make_io_req.qbk
new file mode 100644
index 00000000..02e5d43e
--- /dev/null
+++ b/attic/doc/generated/group_make_io_req.qbk
@@ -0,0 +1,115 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__make__io__req.xml]
+[section:make_io_req_3_length_deducing make_io_req (length deducing)]
+'''<?dbhtml-include href="disqus_identifiers/make_io_req_3_length_deducing.html"?>'''
+
+'''<indexterm><primary>make_io_req</primary></indexterm>'''
+Convenience instantiator of a io\u005freq, letting the compiler deduce the template specialisation to use.
+
+[heading Synopsis]
+``template<class T>
+auto make_io_req(future<> _precondition, T && v, off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [An optional precondition for this operation]]
+[[T &&] [] [v] [A pointer to memory or reference to object into which to read or write]]
+[[off_t] [] [_where] [The offset at which to transfer]]
+]
+
+
+[heading Returns]
+An io\u005freq matching the supplied parameter type. Constructs an instance.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Example]
+[readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:make_io_req_3_length_deducing make_io_req (length deducing)]
+'''<?dbhtml-include href="disqus_identifiers/make_io_req_3_length_deducing.html"?>'''
+
+'''<indexterm><primary>make_io_req</primary></indexterm>'''
+Convenience instantiator of a io\u005freq, letting the compiler deduce the template specialisation to use.
+
+[heading Synopsis]
+``template<class T>
+io_req<const std::initializer_list<T> > make_io_req(future<> _precondition, const std::initializer_list< T > & v, off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [An optional precondition for this operation]]
+[[const std::initializer_list< T > &] [] [v] [A pointer to memory or reference to object into which to read or write]]
+[[off_t] [] [_where] [The offset at which to transfer]]
+]
+
+
+[heading Returns]
+An io\u005freq matching the supplied parameter type. Constructs an instance.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Example]
+[readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:make_io_req_4_length_specifying make_io_req (length specifying)]
+'''<?dbhtml-include href="disqus_identifiers/make_io_req_4_length_specifying.html"?>'''
+
+'''<indexterm><primary>make_io_req</primary></indexterm>'''
+Convenience instantiator of a io\u005freq, letting the compiler deduce the template specialisation to use.
+
+[heading Synopsis]
+``template<class T>
+auto make_io_req(future<> _precondition, T && v, size_t _length,
+ off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [An optional precondition for this operation]]
+[[T &&] [] [v] [A pointer to memory or reference to object into which to read or write]]
+[[size_t] [] [_length] [The number of bytes to transfer]]
+[[off_t] [] [_where] [The offset at which to transfer ]]
+]
+
+
+[heading Returns]
+An io\u005freq matching the supplied parameter type. Constructs an instance.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Example]
+[readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_metadata_flags.qbk b/attic/doc/generated/group_metadata_flags.qbk
new file mode 100644
index 00000000..9cd6315f
--- /dev/null
+++ b/attic/doc/generated/group_metadata_flags.qbk
@@ -0,0 +1,80 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__metadata__flags.xml]
+[section:metadata_flags metadata_flags]
+'''<?dbhtml-include href="disqus_identifiers/metadata_flags.html"?>'''
+
+'''<indexterm><primary>metadata_flags</primary></indexterm>'''
+'''<indexterm><primary>None</primary></indexterm>'''
+'''<indexterm><primary>dev</primary></indexterm>'''
+'''<indexterm><primary>ino</primary></indexterm>'''
+'''<indexterm><primary>type</primary></indexterm>'''
+'''<indexterm><primary>perms</primary></indexterm>'''
+'''<indexterm><primary>nlink</primary></indexterm>'''
+'''<indexterm><primary>uid</primary></indexterm>'''
+'''<indexterm><primary>gid</primary></indexterm>'''
+'''<indexterm><primary>rdev</primary></indexterm>'''
+'''<indexterm><primary>atim</primary></indexterm>'''
+'''<indexterm><primary>mtim</primary></indexterm>'''
+'''<indexterm><primary>ctim</primary></indexterm>'''
+'''<indexterm><primary>size</primary></indexterm>'''
+'''<indexterm><primary>allocated</primary></indexterm>'''
+'''<indexterm><primary>blocks</primary></indexterm>'''
+'''<indexterm><primary>blksize</primary></indexterm>'''
+'''<indexterm><primary>flags</primary></indexterm>'''
+'''<indexterm><primary>gen</primary></indexterm>'''
+'''<indexterm><primary>birthtim</primary></indexterm>'''
+'''<indexterm><primary>sparse</primary></indexterm>'''
+'''<indexterm><primary>compressed</primary></indexterm>'''
+'''<indexterm><primary>reparse_point</primary></indexterm>'''
+'''<indexterm><primary>All</primary></indexterm>'''
+Bitflags for availability of metadata from [^`struct ``stat_t`]See [*afio_stat_t] for explanation of meaning.
+
+[heading Synopsis]
+``enum metadata_flags {None = 0, dev = 1<<0, ino = 1<<1, type = 1<<2, perms = 1<<3, nlink = 1<<4, uid = 1<<5, gid = 1<<6, rdev = 1<<7, atim = 1<<8, mtim = 1<<9, ctim = 1<<10, size = 1<<11, allocated = 1<<12, blocks = 1<<13, blksize = 1<<14, flags = 1<<15, gen = 1<<16, birthtim = 1<<17, sparse = 1<<24, compressed = 1<<25, reparse_point = 1<<26, All = (size_t)-1};``
+
+[heading Values]
+
+[table
+[[Value] [Description] ]
+[[None] []]
+[[dev] []]
+[[ino] []]
+[[type] []]
+[[perms] []]
+[[nlink] []]
+[[uid] []]
+[[gid] []]
+[[rdev] []]
+[[atim] []]
+[[mtim] []]
+[[ctim] []]
+[[size] []]
+[[allocated] []]
+[[blocks] []]
+[[blksize] []]
+[[flags] []]
+[[gen] []]
+[[birthtim] []]
+[[sparse] []]
+[[compressed] []]
+[[reparse_point] []]
+[[All] [Return the maximum possible metadata. ]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_normalise_path.qbk b/attic/doc/generated/group_normalise_path.qbk
new file mode 100644
index 00000000..12adb576
--- /dev/null
+++ b/attic/doc/generated/group_normalise_path.qbk
@@ -0,0 +1,41 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__normalise__path.xml]
+[section:normalise_path normalise_path]
+'''<?dbhtml-include href="disqus_identifiers/normalise_path.html"?>'''
+
+'''<indexterm><primary>normalise_path</primary></indexterm>'''
+Return a normalised filesystem::path from an AFIO path.
+
+[heading Description]
+On POSIX this passes through its input unchanged.
+
+On Windows AFIO exclusively uses NT kernel paths which are not necessarily trivially convertible to Win32 paths. As an example, the Win32 path [^`C:\\Foo`] might be [^`\\??\\C:\\Foo`] or even [^`\\Device\\HarddiskVolume1\\Foo`]. This function will convert any NT kernel path into something which can be fed to normal Win32 APIs - a drive letter if available, else a GUID volume path, and with an extended path prefix if the path is sufficiently long. It also scans the path for characters illegal under Win32 or paths which begin with a space or end with a period, and will extended path prefix such paths as well.
+
+[heading Synopsis]
+``filesystem::path normalise_path(path p, path_normalise type = path_normalise::dos)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[path] [] [p] [Path to be normalised ]]
+[[path_normalise] [] [type] [A path_normalise enum ]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_process_threadpool.qbk b/attic/doc/generated/group_process_threadpool.qbk
new file mode 100644
index 00000000..44e56b77
--- /dev/null
+++ b/attic/doc/generated/group_process_threadpool.qbk
@@ -0,0 +1,37 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__process__threadpool.xml]
+[section:process_threadpool process_threadpool]
+'''<?dbhtml-include href="disqus_identifiers/process_threadpool.html"?>'''
+
+'''<indexterm><primary>process_threadpool</primary></indexterm>'''
+Returns the process threadpool.
+
+[heading Description]
+On first use, this instantiates a default std\u005fthread\u005fpool running [^`BOOST_AFIO_MAX_NON_ASYNC_QUEUE_DEPTH`] threads which will remain until its shared count reaches zero.
+
+[heading Synopsis]
+``BOOST_AFIO_DECL std::shared_ptr<std_thread_pool> process_threadpool()``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_read.qbk b/attic/doc/generated/group_read.qbk
new file mode 100644
index 00000000..379e0605
--- /dev/null
+++ b/attic/doc/generated/group_read.qbk
@@ -0,0 +1,272 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__read.xml]
+[section:read_1_batch read (batch)]
+'''<?dbhtml-include href="disqus_identifiers/read_1_batch.html"?>'''
+
+'''<indexterm><primary>read</primary></indexterm>'''
+Schedule a batch of asynchronous data reads after preceding operations, where offset and total data read must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+virtual std::vector<future<> > dispatcher::read(const std::vector< io_req< T >> & ops)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[const std::vector< io_req< T >> &] [] [ops] [A batch of io_req<T> structures.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if reading data is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_read_3_length_deducing async_read (length deducing)]
+'''<?dbhtml-include href="disqus_identifiers/async_read_3_length_deducing.html"?>'''
+
+'''<indexterm><primary>async_read</primary></indexterm>'''
+Asynchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+future async_read(future<> _precondition, T && v, off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_read_4_length_specifying async_read (length specifying)]
+'''<?dbhtml-include href="disqus_identifiers/async_read_4_length_specifying.html"?>'''
+
+'''<indexterm><primary>async_read</primary></indexterm>'''
+Asynchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+future async_read(future<> _precondition, T && v, size_t _length,
+ off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[size_t] [] [_length] [The length of the item ]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:read_3_length_deducing_throwing read (length deducing throwing)]
+'''<?dbhtml-include href="disqus_identifiers/read_3_length_deducing_throwing.html"?>'''
+
+'''<indexterm><primary>read</primary></indexterm>'''
+Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+void read(future<> _precondition, T && v, off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:read_4_length_specifying_throwing read (length specifying throwing)]
+'''<?dbhtml-include href="disqus_identifiers/read_4_length_specifying_throwing.html"?>'''
+
+'''<indexterm><primary>read</primary></indexterm>'''
+Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+void read(future<> _precondition, T && v, size_t _length,
+ off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[size_t] [] [_length] [The length of the item ]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:read_4_length_deducing_non_throwing read (length deducing non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/read_4_length_deducing_non_throwing.html"?>'''
+
+'''<indexterm><primary>read</primary></indexterm>'''
+Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+void read(error_code & _ec, future<> _precondition, T && v,
+ off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:read_5_length_specifying_non_throwing read (length specifying non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/read_5_length_specifying_non_throwing.html"?>'''
+
+'''<indexterm><primary>read</primary></indexterm>'''
+Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+void read(error_code & _ec, future<> _precondition, T && v,
+ size_t _length, off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[size_t] [] [_length] [The length of the item ]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_rmdir.qbk b/attic/doc/generated/group_rmdir.qbk
new file mode 100644
index 00000000..4944d1a3
--- /dev/null
+++ b/attic/doc/generated/group_rmdir.qbk
@@ -0,0 +1,293 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__rmdir.xml]
+[section:rmdir_1_batch rmdir (batch)]
+'''<?dbhtml-include href="disqus_identifiers/rmdir_1_batch.html"?>'''
+
+'''<indexterm><primary>rmdir</primary></indexterm>'''
+Schedule a batch of asynchronous directory deletions after optional preconditions.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``future dispatcher::rmdir(const std::vector< path_req > & reqs)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< path_req > &] [] [reqs] [A batch of [^`path_req`] structures.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if directory deletion is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_rmdir_3_relative async_rmdir (relative)]
+'''<?dbhtml-include href="disqus_identifiers/async_rmdir_3_relative.html"?>'''
+
+'''<indexterm><primary>async_rmdir</primary></indexterm>'''
+Asynchronous directory deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+future async_rmdir(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_rmdir_2_absolute async_rmdir (absolute)]
+'''<?dbhtml-include href="disqus_identifiers/async_rmdir_2_absolute.html"?>'''
+
+'''<indexterm><primary>async_rmdir</primary></indexterm>'''
+Asynchronous directory deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+future async_rmdir(T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmdir_3_relative_throwing rmdir (relative throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmdir_3_relative_throwing.html"?>'''
+
+'''<indexterm><primary>rmdir</primary></indexterm>'''
+Synchronous directory deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+void rmdir(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmdir_2_absolute_throwing rmdir (absolute throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmdir_2_absolute_throwing.html"?>'''
+
+'''<indexterm><primary>rmdir</primary></indexterm>'''
+Synchronous directory deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+void rmdir(T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmdir_4_relative_non_throwing rmdir (relative non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmdir_4_relative_non_throwing.html"?>'''
+
+'''<indexterm><primary>rmdir</primary></indexterm>'''
+Synchronous directory deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+void rmdir(error_code & _ec, future<> _precondition, T _path = path(),
+ file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmdir_3_absolute_non_throwing rmdir (absolute non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmdir_3_absolute_non_throwing.html"?>'''
+
+'''<indexterm><primary>rmdir</primary></indexterm>'''
+Synchronous directory deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+void rmdir(error_code & _ec, T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_rmfile.qbk b/attic/doc/generated/group_rmfile.qbk
new file mode 100644
index 00000000..1abcd92e
--- /dev/null
+++ b/attic/doc/generated/group_rmfile.qbk
@@ -0,0 +1,293 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__rmfile.xml]
+[section:rmfile_1_batch rmfile (batch)]
+'''<?dbhtml-include href="disqus_identifiers/rmfile_1_batch.html"?>'''
+
+'''<indexterm><primary>rmfile</primary></indexterm>'''
+Schedule a batch of asynchronous file deletions after optional preconditions.
+
+[heading Description]
+Note that you can delete files before they are closed on Windows just as with POSIX if and only if all open handles to that file were opened with permission for the file to be deleted (AFIO always sets this). The actual file data will be deleted when the last handle is closed on the system. Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``future dispatcher::rmfile(const std::vector< path_req > & reqs)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< path_req > &] [] [reqs] [A batch of [^`path_req`] structures.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if file deletion is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_rmfile_3_relative async_rmfile (relative)]
+'''<?dbhtml-include href="disqus_identifiers/async_rmfile_3_relative.html"?>'''
+
+'''<indexterm><primary>async_rmfile</primary></indexterm>'''
+Asynchronous file deletion after an optional precondition.
+
+[heading Description]
+Note that you can delete files before they are closed on Windows just as with POSIX if and only if all open handles to that file were opened with permission for the file to be deleted (AFIO always sets this). The actual file data will be deleted when the last handle is closed on the system. Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+future async_rmfile(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_rmfile_2_absolute async_rmfile (absolute)]
+'''<?dbhtml-include href="disqus_identifiers/async_rmfile_2_absolute.html"?>'''
+
+'''<indexterm><primary>async_rmfile</primary></indexterm>'''
+Asynchronous file deletion after an optional precondition.
+
+[heading Description]
+Note that you can delete files before they are closed on Windows just as with POSIX if and only if all open handles to that file were opened with permission for the file to be deleted (AFIO always sets this). The actual file data will be deleted when the last handle is closed on the system. Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+future async_rmfile(T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmfile_3_relative_throwing rmfile (relative throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmfile_3_relative_throwing.html"?>'''
+
+'''<indexterm><primary>rmfile</primary></indexterm>'''
+Synchronous file deletion after an optional precondition.
+
+[heading Description]
+Note that you can delete files before they are closed on Windows just as with POSIX if and only if all open handles to that file were opened with permission for the file to be deleted (AFIO always sets this). The actual file data will be deleted when the last handle is closed on the system. Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+void rmfile(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmfile_2_absolute_throwing rmfile (absolute throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmfile_2_absolute_throwing.html"?>'''
+
+'''<indexterm><primary>rmfile</primary></indexterm>'''
+Synchronous file deletion after an optional precondition.
+
+[heading Description]
+Note that you can delete files before they are closed on Windows just as with POSIX if and only if all open handles to that file were opened with permission for the file to be deleted (AFIO always sets this). The actual file data will be deleted when the last handle is closed on the system. Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+void rmfile(T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmfile_4_relative_non_throwing rmfile (relative non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmfile_4_relative_non_throwing.html"?>'''
+
+'''<indexterm><primary>rmfile</primary></indexterm>'''
+Synchronous file deletion after an optional precondition.
+
+[heading Description]
+Note that you can delete files before they are closed on Windows just as with POSIX if and only if all open handles to that file were opened with permission for the file to be deleted (AFIO always sets this). The actual file data will be deleted when the last handle is closed on the system. Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+void rmfile(error_code & _ec, future<> _precondition, T _path = path(),
+ file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmfile_3_absolute_non_throwing rmfile (absolute non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmfile_3_absolute_non_throwing.html"?>'''
+
+'''<indexterm><primary>rmfile</primary></indexterm>'''
+Synchronous file deletion after an optional precondition.
+
+[heading Description]
+Note that you can delete files before they are closed on Windows just as with POSIX if and only if all open handles to that file were opened with permission for the file to be deleted (AFIO always sets this). The actual file data will be deleted when the last handle is closed on the system. Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+void rmfile(error_code & _ec, T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_rmsymlink.qbk b/attic/doc/generated/group_rmsymlink.qbk
new file mode 100644
index 00000000..496a106a
--- /dev/null
+++ b/attic/doc/generated/group_rmsymlink.qbk
@@ -0,0 +1,293 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__rmsymlink.xml]
+[section:rmsymlink_1_batch rmsymlink (batch)]
+'''<?dbhtml-include href="disqus_identifiers/rmsymlink_1_batch.html"?>'''
+
+'''<indexterm><primary>rmsymlink</primary></indexterm>'''
+Schedule a batch of asynchronous symlink deletions after optional preconditions.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`]
+
+[heading Synopsis]
+``future dispatcher::rmsymlink(const std::vector< path_req > & reqs)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< path_req > &] [] [reqs] [A batch of [^`path_req`] structures.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if symlink deletion is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_rmsymlink_3_relative async_rmsymlink (relative)]
+'''<?dbhtml-include href="disqus_identifiers/async_rmsymlink_3_relative.html"?>'''
+
+'''<indexterm><primary>async_rmsymlink</primary></indexterm>'''
+Asynchronous symlink deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+future async_rmsymlink(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_rmsymlink_2_absolute async_rmsymlink (absolute)]
+'''<?dbhtml-include href="disqus_identifiers/async_rmsymlink_2_absolute.html"?>'''
+
+'''<indexterm><primary>async_rmsymlink</primary></indexterm>'''
+Asynchronous symlink deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+future async_rmsymlink(T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmsymlink_3_relative_throwing rmsymlink (relative throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmsymlink_3_relative_throwing.html"?>'''
+
+'''<indexterm><primary>rmsymlink</primary></indexterm>'''
+Synchronous symlink deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+void rmsymlink(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmsymlink_2_absolute_throwing rmsymlink (absolute throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmsymlink_2_absolute_throwing.html"?>'''
+
+'''<indexterm><primary>rmsymlink</primary></indexterm>'''
+Synchronous symlink deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+void rmsymlink(T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmsymlink_4_relative_non_throwing rmsymlink (relative non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmsymlink_4_relative_non_throwing.html"?>'''
+
+'''<indexterm><primary>rmsymlink</primary></indexterm>'''
+Synchronous symlink deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+void rmsymlink(error_code & _ec, future<> _precondition, T _path = path(),
+ file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:rmsymlink_3_absolute_non_throwing rmsymlink (absolute non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/rmsymlink_3_absolute_non_throwing.html"?>'''
+
+'''<indexterm><primary>rmsymlink</primary></indexterm>'''
+Synchronous symlink deletion after an optional precondition.
+
+[heading Description]
+Make sure you read the docs for [^`__afio_handle__::unlink()`] for important caveats. Note that on operating systems with an unstable [^`__afio_handle__::path(true)`] you need to be cautious of deleting files by handle as any hard link to that file may be deleted instead of the one you intended. To work around this, portable code should delete by directory handle as the precondition and known leafname. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+void rmsymlink(error_code & _ec, T _path, file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to be used. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[T] [] [_path] [The filing system path to be used. ]]
+[[file_flags] [] [_flags] [The flags to be used.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_statfs.qbk b/attic/doc/generated/group_statfs.qbk
new file mode 100644
index 00000000..5ca3ce46
--- /dev/null
+++ b/attic/doc/generated/group_statfs.qbk
@@ -0,0 +1,180 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__statfs.xml]
+[section:statfs_2_batch statfs (batch)]
+'''<?dbhtml-include href="disqus_identifiers/statfs_2_batch.html"?>'''
+
+'''<indexterm><primary>statfs</primary></indexterm>'''
+Schedule a batch of asynchronous volume enumerations after preceding operations.
+
+[heading Description]
+Related types: [^`__afio_statfs_t__`]
+
+[heading Synopsis]
+``future< statfs_t > dispatcher::statfs(const std::vector< future<>> & ops, const std::vector< fs_metadata_flags > & reqs)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< future<>> &] [] [ops] [A batch of op handles. ]]
+[[const std::vector< fs_metadata_flags > &] [] [reqs] [A batch of metadata requests.]]
+]
+
+
+[heading Returns]
+A batch of stl\u005ffuture volume metadatas.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, OS X..Race free.]
+[raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
+flags.rdonly, flags.noexec, flags.nosuid.]
+[raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
+is fetched separately.]
+ ]
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of entries in each directory.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][statfs_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_statfs async_statfs]
+'''<?dbhtml-include href="disqus_identifiers/async_statfs.html"?>'''
+
+'''<indexterm><primary>async_statfs</primary></indexterm>'''
+Asynchronous volume enumeration after a preceding operation.
+
+[heading Description]
+Related types: [^`__afio_statfs_t__`]
+
+[heading Synopsis]
+``future<statfs_t> async_statfs(future<> _precondition, fs_metadata_flags req)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[fs_metadata_flags] [] [req] [A metadata request.]]
+]
+
+
+[heading Returns]
+A [^`future<``statfs_t``>`]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, OS X..Race free.]
+[raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
+flags.rdonly, flags.noexec, flags.nosuid.]
+[raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
+is fetched separately.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][statfs_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:statfs_2_throwing statfs (throwing)]
+'''<?dbhtml-include href="disqus_identifiers/statfs_2_throwing.html"?>'''
+
+'''<indexterm><primary>statfs</primary></indexterm>'''
+Synchronous volume enumeration after a preceding operation.
+
+[heading Description]
+Related types: [^`__afio_statfs_t__`]
+
+[heading Synopsis]
+``statfs_t statfs(future<> _precondition, fs_metadata_flags req)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[fs_metadata_flags] [] [req] [A metadata request.]]
+]
+
+
+[heading Returns]
+The volume metadata requested.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, OS X..Race free.]
+[raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
+flags.rdonly, flags.noexec, flags.nosuid.]
+[raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
+is fetched separately.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][statfs_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:statfs_3_nonthrowing statfs (nonthrowing)]
+'''<?dbhtml-include href="disqus_identifiers/statfs_3_nonthrowing.html"?>'''
+
+'''<indexterm><primary>statfs</primary></indexterm>'''
+Synchronous volume enumeration after a preceding operation.
+
+[heading Description]
+Related types: [^`__afio_statfs_t__`]
+
+[heading Synopsis]
+``statfs_t statfs(error_code & _ec, future<> _precondition, fs_metadata_flags req)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[fs_metadata_flags] [] [req] [A metadata request.]]
+]
+
+
+[heading Returns]
+The volume metadata requested.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, OS X..Race free.]
+[raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
+flags.rdonly, flags.noexec, flags.nosuid.]
+[raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
+is fetched separately.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][statfs_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_symlink.qbk b/attic/doc/generated/group_symlink.qbk
new file mode 100644
index 00000000..20120d2e
--- /dev/null
+++ b/attic/doc/generated/group_symlink.qbk
@@ -0,0 +1,311 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__symlink.xml]
+[section:symlink_2_batch symlink (batch)]
+'''<?dbhtml-include href="disqus_identifiers/symlink_2_batch.html"?>'''
+
+'''<indexterm><primary>symlink</primary></indexterm>'''
+Schedule a batch of asynchronous symlink creations and opens after a precondition.
+
+[heading Description]
+Note that if creating and you don't specify a target, the target for the symlink is the precondition. On Windows directories are symlinked using a reparse point instead of a symlink due to the default lack of the [^`SeCreateSymbolicLinkPrivilege`] for non-Administrative users. On Windows you can open symlinks as a file and so a valid handle is output, whereas on POSIX except for Linux you cannot do this and an invalid handle is output. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``future dispatcher::symlink(const std::vector< path_req > & reqs, const std::vector< future<>> & targets = std::vector< future<>>())``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< path_req > &] [] [reqs] [A batch of [^`path_req`] structures. ]]
+[[const std::vector< future<>> &] [] [targets] [An optional batch of targets if creating symlinks.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if symlink creation is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_symlink_4_relative async_symlink (relative)]
+'''<?dbhtml-include href="disqus_identifiers/async_symlink_4_relative.html"?>'''
+
+'''<indexterm><primary>async_symlink</primary></indexterm>'''
+Asynchronous symlink creation and open after a precondition.
+
+[heading Description]
+Note that if creating and you don't specify a target, the target for the symlink is the precondition. On Windows directories are symlinked using a reparse point instead of a symlink due to the default lack of the [^`SeCreateSymbolicLinkPrivilege`] for non-Administrative users. On Windows you can open symlinks as a file and so a valid handle is output, whereas on POSIX except for Linux you cannot do this and an invalid handle is output. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+future async_symlink(future<> _precondition, T _path, future<> _target = future<>(),
+ file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[future<>] [] [_target] [The item to link to if creating. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_symlink_3_absolute async_symlink (absolute)]
+'''<?dbhtml-include href="disqus_identifiers/async_symlink_3_absolute.html"?>'''
+
+'''<indexterm><primary>async_symlink</primary></indexterm>'''
+Asynchronous symlink creation and open after a precondition.
+
+[heading Description]
+Note that if creating and you don't specify a target, the target for the symlink is the precondition. On Windows directories are symlinked using a reparse point instead of a symlink due to the default lack of the [^`SeCreateSymbolicLinkPrivilege`] for non-Administrative users. On Windows you can open symlinks as a file and so a valid handle is output, whereas on POSIX except for Linux you cannot do this and an invalid handle is output. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+future async_symlink(T _path, future<> _target = future<>(), file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[future<>] [] [_target] [The item to link to if creating. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:symlink_4_relative_throwing symlink (relative throwing)]
+'''<?dbhtml-include href="disqus_identifiers/symlink_4_relative_throwing.html"?>'''
+
+'''<indexterm><primary>symlink</primary></indexterm>'''
+Synchronous symlink creation and open after a precondition..
+
+[heading Description]
+Note that if creating and you don't specify a target, the target for the symlink is the precondition. On Windows directories are symlinked using a reparse point instead of a symlink due to the default lack of the [^`SeCreateSymbolicLinkPrivilege`] for non-Administrative users. On Windows you can open symlinks as a file and so a valid handle is output, whereas on POSIX except for Linux you cannot do this and an invalid handle is output. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+handle_ptr symlink(future<> _precondition, T _path, future<> _target = future<>(),
+ file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[future<>] [] [_target] [The item to link to if creating. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the symlink.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:symlink_3_absolute_throwing symlink (absolute throwing)]
+'''<?dbhtml-include href="disqus_identifiers/symlink_3_absolute_throwing.html"?>'''
+
+'''<indexterm><primary>symlink</primary></indexterm>'''
+Synchronous symlink creation and open after a precondition.
+
+[heading Description]
+Note that if creating and you don't specify a target, the target for the symlink is the precondition. On Windows directories are symlinked using a reparse point instead of a symlink due to the default lack of the [^`SeCreateSymbolicLinkPrivilege`] for non-Administrative users. On Windows you can open symlinks as a file and so a valid handle is output, whereas on POSIX except for Linux you cannot do this and an invalid handle is output. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+handle_ptr symlink(T _path, future<> _target = future<>(), file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[future<>] [] [_target] [The item to link to if creating. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the symlink.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:symlink_5_relative_non_throwing symlink (relative non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/symlink_5_relative_non_throwing.html"?>'''
+
+'''<indexterm><primary>symlink</primary></indexterm>'''
+Synchronous symlink creation and open after a precondition.
+
+[heading Description]
+Note that if creating and you don't specify a target, the target for the symlink is the precondition. On Windows directories are symlinked using a reparse point instead of a symlink due to the default lack of the [^`SeCreateSymbolicLinkPrivilege`] for non-Administrative users. On Windows you can open symlinks as a file and so a valid handle is output, whereas on POSIX except for Linux you cannot do this and an invalid handle is output. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T>
+handle_ptr symlink(error_code & _ec, future<> _precondition, T _path,
+ future<> _target = future<>(), file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[future<>] [] [_target] [The item to link to if creating. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the symlink.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
+[raceguarantee OS X..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:symlink_4_absolute_non_throwing symlink (absolute non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/symlink_4_absolute_non_throwing.html"?>'''
+
+'''<indexterm><primary>symlink</primary></indexterm>'''
+Synchronous symlink creation and open after a precondition.
+
+[heading Description]
+Note that if creating and you don't specify a target, the target for the symlink is the precondition. On Windows directories are symlinked using a reparse point instead of a symlink due to the default lack of the [^`SeCreateSymbolicLinkPrivilege`] for non-Administrative users. On Windows you can open symlinks as a file and so a valid handle is output, whereas on POSIX except for Linux you cannot do this and an invalid handle is output. Related types: [^`__afio_path_req__`] Be aware that on Windows AFIO operates exclusively in the NT kernel namespace, NOT the Win32 namespace, and therefore paths you supply are converted by [^`afio::path`] in NT kernel namespace paths. [^`normalise_path()`] can convert an afio path back to a Filesystem TS path for you, but note that this may not be the path that was supplied originally. NT kernel namespace paths have a 32,767 character limit and an almost POSIX like lack of restrictions on naming or historical behaviour quirks, but as a result AFIO does not support DOS short names or any of the other historical Win32 filing system baggage.
+
+[heading Synopsis]
+``template<class T, typename>
+handle_ptr symlink(error_code & _ec, T _path, future<> _target = future<>(),
+ file_flags _flags = file_flags::none)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [The type of path to use. ] [ - ] [Must be specified]]
+[[] [] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[T] [] [_path] [The filing system path to use. ]]
+[[future<>] [] [_target] [The item to link to if creating. ]]
+[[file_flags] [] [_flags] [The flags to use.]]
+]
+
+
+[heading Returns]
+A handle to the symlink.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Race Guarantees][raceguarantees
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+ ]
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][filedir_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_sync.qbk b/attic/doc/generated/group_sync.qbk
new file mode 100644
index 00000000..3c48febf
--- /dev/null
+++ b/attic/doc/generated/group_sync.qbk
@@ -0,0 +1,142 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__sync.xml]
+[section:sync_1_batch sync (batch)]
+'''<?dbhtml-include href="disqus_identifiers/sync_1_batch.html"?>'''
+
+'''<indexterm><primary>sync</primary></indexterm>'''
+Schedule a batch of asynchronous content synchronisations with physical storage after preceding operations.
+
+[heading Description]
+It goes without saying that this call can take very significant amounts of time to complete!
+
+[heading Synopsis]
+``future dispatcher::sync(const std::vector< future<>> & ops)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< future<>> &] [] [ops] [A batch of op handles.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if content synchronisation is constant time (which is extremely unlikely).
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_sync async_sync]
+'''<?dbhtml-include href="disqus_identifiers/async_sync.html"?>'''
+
+'''<indexterm><primary>async_sync</primary></indexterm>'''
+Asynchronous content synchronisation with physical storage after a preceding operation.
+
+[heading Description]
+It goes without saying that this call can take very significant amounts of time to complete!
+
+[heading Synopsis]
+``future async_sync(future<> _precondition)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:sync_1_throwing sync (throwing)]
+'''<?dbhtml-include href="disqus_identifiers/sync_1_throwing.html"?>'''
+
+'''<indexterm><primary>sync</primary></indexterm>'''
+Synchronous content synchronisation with physical storage after a preceding operation.
+
+[heading Description]
+It goes without saying that this call can take very significant amounts of time to complete!
+
+[heading Synopsis]
+``void sync(future<> _precondition)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:sync_2_non_throwing sync (non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/sync_2_non_throwing.html"?>'''
+
+'''<indexterm><primary>sync</primary></indexterm>'''
+Synchronous content synchronisation with physical storage after a preceding operation.
+
+[heading Description]
+It goes without saying that this call can take very significant amounts of time to complete!
+
+[heading Synopsis]
+``void sync(error_code & _ec, future<> _precondition)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_to_asio_buffers.qbk b/attic/doc/generated/group_to_asio_buffers.qbk
new file mode 100644
index 00000000..5016e54d
--- /dev/null
+++ b/attic/doc/generated/group_to_asio_buffers.qbk
@@ -0,0 +1,305 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__to__asio__buffers.xml]
+[section:to_asio_buffers_1_asio_mutable_buffer to_asio_buffers (asio mutable_buffer)]
+'''<?dbhtml-include href="disqus_identifiers/to_asio_buffers_1_asio_mutable_buffer.html"?>'''
+
+'''<indexterm><primary>to_asio_buffers</primary></indexterm>'''
+Passing through asio::mutable\u005fbuffer.
+
+[heading Synopsis]
+``std::vector<asio::mutable_buffer> to_asio_buffers(asio::mutable_buffer & v)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[asio::mutable_buffer &] [] [v] []]
+]
+
+
+[heading Returns]
+A vector of ASIO buffers
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:to_asio_buffers_1_asio_const_buffer to_asio_buffers (asio const_buffer)]
+'''<?dbhtml-include href="disqus_identifiers/to_asio_buffers_1_asio_const_buffer.html"?>'''
+
+'''<indexterm><primary>to_asio_buffers</primary></indexterm>'''
+Passing through asio::const\u005fbuffer.
+
+[heading Synopsis]
+``std::vector<asio::const_buffer> to_asio_buffers(asio::const_buffer & v)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[asio::const_buffer &] [] [v] []]
+]
+
+
+[heading Returns]
+A vector of ASIO buffers
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:to_asio_buffers_2_buffer_of_t to_asio_buffers (buffer of T)]
+'''<?dbhtml-include href="disqus_identifiers/to_asio_buffers_2_buffer_of_t.html"?>'''
+
+'''<indexterm><primary>to_asio_buffers</primary></indexterm>'''
+A buffer at v sized length*sizeof(T)
+
+[heading Synopsis]
+``template<class T>
+std::vector<asio::mutable_buffer> to_asio_buffers(T * v, size_t length)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any trivial type T ] [ - ] [Must be specified]]
+[[T *] [] [v] []]
+[[size_t] [] [length] []]
+]
+
+
+[heading Returns]
+A vector of ASIO buffers
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:to_asio_buffers_2_const_buffer_of_t to_asio_buffers (const buffer of T)]
+'''<?dbhtml-include href="disqus_identifiers/to_asio_buffers_2_const_buffer_of_t.html"?>'''
+
+'''<indexterm><primary>to_asio_buffers</primary></indexterm>'''
+A buffer at v sized length*sizeof(T)
+
+[heading Synopsis]
+``template<class T>
+std::vector<asio::const_buffer> to_asio_buffers(const T * v, size_t length)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any trivial type T ] [ - ] [Must be specified]]
+[[const T *] [] [v] []]
+[[size_t] [] [length] []]
+]
+
+
+[heading Returns]
+A vector of ASIO buffers
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:to_asio_buffers_2_buffer to_asio_buffers (buffer)]
+'''<?dbhtml-include href="disqus_identifiers/to_asio_buffers_2_buffer.html"?>'''
+
+'''<indexterm><primary>to_asio_buffers</primary></indexterm>'''
+A buffer at v sized length.
+
+[heading Synopsis]
+``std::vector<asio::mutable_buffer> to_asio_buffers(void * v, size_t length)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[void *] [] [v] []]
+[[size_t] [] [length] []]
+]
+
+
+[heading Returns]
+A vector of ASIO buffers
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:to_asio_buffers_2_const_buffer_of_t to_asio_buffers (const buffer of T)]
+'''<?dbhtml-include href="disqus_identifiers/to_asio_buffers_2_const_buffer_of_t.html"?>'''
+
+'''<indexterm><primary>to_asio_buffers</primary></indexterm>'''
+A buffer at v sized length.
+
+[heading Synopsis]
+``std::vector<asio::const_buffer> to_asio_buffers(const void * v, size_t length)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const void *] [] [v] []]
+[[size_t] [] [length] []]
+]
+
+
+[heading Returns]
+A vector of ASIO buffers
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:to_asio_buffers_1_trivial_and_container_types to_asio_buffers (trivial and container types)]
+'''<?dbhtml-include href="disqus_identifiers/to_asio_buffers_1_trivial_and_container_types.html"?>'''
+
+'''<indexterm><primary>to_asio_buffers</primary></indexterm>'''
+Any trivial type T or STL container.
+
+[heading Description]
+Trivial types turn into a buffer of &v sized sizeof(T). Container types have their value type deduced and to\u005fasio\u005fbuffers() called on that value\u005ftype. Additional specialisations are provided for string, vector and array to collapse the scatter gather buffers into a single one for contiguous storage.
+
+[heading Synopsis]
+``template<class T>
+std::vector< asio::mutable_buffer > to_asio_buffers(T & v)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any trivial type T or STL container ] [ - ] [Must be specified]]
+[[T &] [] [v] []]
+]
+
+
+[heading Returns]
+A vector of ASIO buffers
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:to_asio_buffers_1_const_trivial_and_container_types to_asio_buffers (const trivial and container types)]
+'''<?dbhtml-include href="disqus_identifiers/to_asio_buffers_1_const_trivial_and_container_types.html"?>'''
+
+'''<indexterm><primary>to_asio_buffers</primary></indexterm>'''
+Any trivial type T or STL container.
+
+[heading Description]
+Trivial types turn into a buffer of &v sized sizeof(T). Container types have their value type deduced and to\u005fasio\u005fbuffers() called on that value\u005ftype. Additional specialisations are provided for string, vector and array to collapse the scatter gather buffers into a single one for contiguous storage.
+
+[heading Synopsis]
+``template<class T>
+std::vector< asio::const_buffer > to_asio_buffers(const T & v)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any trivial type T or STL container ] [ - ] [Must be specified]]
+[[const T &] [] [v] []]
+]
+
+
+[heading Returns]
+A vector of ASIO buffers
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:to_asio_buffers_1_c_arrays to_asio_buffers (C arrays)]
+'''<?dbhtml-include href="disqus_identifiers/to_asio_buffers_1_c_arrays.html"?>'''
+
+'''<indexterm><primary>to_asio_buffers</primary></indexterm>'''
+A buffer at v sized N*sizeof(T)
+
+[heading Synopsis]
+``template<class T, size_t N>
+std::vector< asio::mutable_buffer > to_asio_buffers(T(&) v)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any trivial type T ] [ - ] [Must be specified]]
+[[N] [] [ - ] [Must be specified]]
+[[T(&)] [] [v] []]
+]
+
+
+[heading Returns]
+A vector of ASIO buffers
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:to_asio_buffers_1_const_c_arrays to_asio_buffers (const C arrays)]
+'''<?dbhtml-include href="disqus_identifiers/to_asio_buffers_1_const_c_arrays.html"?>'''
+
+'''<indexterm><primary>to_asio_buffers</primary></indexterm>'''
+A buffer at v sized N*sizeof(T)
+
+[heading Synopsis]
+``template<class T, size_t N>
+std::vector< asio::const_buffer > to_asio_buffers(const T(&) v)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any trivial type T ] [ - ] [Must be specified]]
+[[N] [] [ - ] [Must be specified]]
+[[const T(&)] [] [v] []]
+]
+
+
+[heading Returns]
+A vector of ASIO buffers
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_truncate.qbk b/attic/doc/generated/group_truncate.qbk
new file mode 100644
index 00000000..3ee9840f
--- /dev/null
+++ b/attic/doc/generated/group_truncate.qbk
@@ -0,0 +1,134 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__truncate.xml]
+[section:truncate_2_batch truncate (batch)]
+'''<?dbhtml-include href="disqus_identifiers/truncate_2_batch.html"?>'''
+
+'''<indexterm><primary>truncate</primary></indexterm>'''
+Schedule a batch of asynchronous file length truncations after preceding operations.
+
+[heading Synopsis]
+``future dispatcher::truncate(const std::vector< future<>> & ops, const std::vector< off_t > & sizes)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< future<>> &] [] [ops] [A batch of op handles. ]]
+[[const std::vector< off_t > &] [] [sizes] [A batch of new lengths.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if truncating file lengths is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_truncate async_truncate]
+'''<?dbhtml-include href="disqus_identifiers/async_truncate.html"?>'''
+
+'''<indexterm><primary>async_truncate</primary></indexterm>'''
+Asynchronous file length truncation after a preceding operation.
+
+[heading Synopsis]
+``future async_truncate(future<> _precondition, off_t newsize)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[off_t] [] [newsize] [The new size for the file.]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:truncate_2_throwing truncate (throwing)]
+'''<?dbhtml-include href="disqus_identifiers/truncate_2_throwing.html"?>'''
+
+'''<indexterm><primary>truncate</primary></indexterm>'''
+Synchronous file length truncation after a preceding operation.
+
+[heading Synopsis]
+``void truncate(future<> _precondition, off_t newsize)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[off_t] [] [newsize] [The new size for the file.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:truncate_3_non_throwing truncate (non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/truncate_3_non_throwing.html"?>'''
+
+'''<indexterm><primary>truncate</primary></indexterm>'''
+Synchronous file length truncation after a preceding operation.
+
+[heading Synopsis]
+``void truncate(error_code & _ec, future<> _precondition, off_t newsize)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[off_t] [] [newsize] [The new size for the file.]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_utils.qbk b/attic/doc/generated/group_utils.qbk
new file mode 100644
index 00000000..31fa7fa9
--- /dev/null
+++ b/attic/doc/generated/group_utils.qbk
@@ -0,0 +1,200 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__utils.xml]
+[section:page_sizes page_sizes]
+'''<?dbhtml-include href="disqus_identifiers/page_sizes.html"?>'''
+
+'''<indexterm><primary>page_sizes</primary></indexterm>'''
+Returns the page sizes of this architecture which is useful for calculating direct i/o multiples.
+
+[heading Synopsis]
+``BOOST_AFIO_DECL std::vector<size_t> utils::page_sizes(bool only_actually_available = true)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[bool] [] [only_actually_available] [Only return page sizes actually available to the user running this process ]]
+]
+
+
+[heading Returns]
+The page sizes of this architecture.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Whatever the system API takes (one would hope constant time).
+[heading Exception Model]Any error from the operating system or std::bad_alloc.
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:file_buffer_default_size file_buffer_default_size]
+'''<?dbhtml-include href="disqus_identifiers/file_buffer_default_size.html"?>'''
+
+'''<indexterm><primary>file_buffer_default_size</primary></indexterm>'''
+Returns a reasonable default size for page\u005fallocator, typically the closest page size from page\u005fsizes() to 1Mb.
+
+[heading Synopsis]
+``size_t utils::file_buffer_default_size()``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+]
+
+
+[heading Returns]
+A value of a TLB large page size close to 1Mb.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Whatever the system API takes (one would hope constant time).
+[heading Exception Model]Any error from the operating system or std::bad_alloc.
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:random_fill random_fill]
+'''<?dbhtml-include href="disqus_identifiers/random_fill.html"?>'''
+
+'''<indexterm><primary>random_fill</primary></indexterm>'''
+Fills the buffer supplied with cryptographically strong randomness. Uses the OS kernel API.
+
+[heading Synopsis]
+``BOOST_AFIO_DECL void utils::random_fill(char * buffer, size_t bytes)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[char *] [] [buffer] [A buffer to fill ]]
+[[size_t] [] [bytes] [How many bytes to fill]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Whatever the system API takes.
+[heading Exception Model]Any error from the operating system.
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:to_hex_string to_hex_string]
+'''<?dbhtml-include href="disqus_identifiers/to_hex_string.html"?>'''
+
+'''<indexterm><primary>to_hex_string</primary></indexterm>'''
+Converts a number to a hex string. Out buffer can be same as in buffer.
+
+[heading Description]
+Note that the character range used is a 16 item table of:
+
+0123456789abcdef
+
+This lets one pack one byte of input into two bytes of output.
+
+[heading Synopsis]
+``size_t utils::to_hex_string(char * out, size_t outlen, const char * _in,
+ size_t inlen)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[char *] [] [out] []]
+[[size_t] [] [outlen] []]
+[[const char *] [] [_in] []]
+[[size_t] [] [inlen] []]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]O(N) where N is the length of the number.
+[heading Exception Model]Throws exception if output buffer is too small for input.
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:from_hex_string from_hex_string]
+'''<?dbhtml-include href="disqus_identifiers/from_hex_string.html"?>'''
+
+'''<indexterm><primary>from_hex_string</primary></indexterm>'''
+Converts a hex string to a number. Out buffer can be same as in buffer.
+
+[heading Description]
+Note that this routine is about 43% slower than to\u005fhex\u005fstring(), half of which is due to input validation.
+
+[heading Synopsis]
+``size_t utils::from_hex_string(char * out, size_t outlen, const char * in,
+ size_t inlen)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[char *] [] [out] []]
+[[size_t] [] [outlen] []]
+[[const char *] [] [in] []]
+[[size_t] [] [inlen] []]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]O(N) where N is the length of the string.
+[heading Exception Model]Throws exception if output buffer is too small for input or input size is not multiple of two.
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:random_string random_string]
+'''<?dbhtml-include href="disqus_identifiers/random_string.html"?>'''
+
+'''<indexterm><primary>random_string</primary></indexterm>'''
+Returns a cryptographically random string capable of being used as a filename. Essentially random\u005ffill() + to\u005fhex\u005fstring().
+
+[heading Synopsis]
+``std::string utils::random_string(size_t randomlen)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[size_t] [] [randomlen] [The number of bytes of randomness to use for the string. ]]
+]
+
+
+[heading Returns]
+A string representing the randomness at a 2x ratio, so if 32 bytes were requested, this string would be 64 bytes long.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Whatever the system API takes.
+[heading Exception Model]Any error from the operating system.
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_when_all_futures.qbk b/attic/doc/generated/group_when_all_futures.qbk
new file mode 100644
index 00000000..eb9229c5
--- /dev/null
+++ b/attic/doc/generated/group_when_all_futures.qbk
@@ -0,0 +1,11 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__when__all__futures.xml]
diff --git a/attic/doc/generated/group_write.qbk b/attic/doc/generated/group_write.qbk
new file mode 100644
index 00000000..1da621c4
--- /dev/null
+++ b/attic/doc/generated/group_write.qbk
@@ -0,0 +1,272 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__write.xml]
+[section:write_1_batch write (batch)]
+'''<?dbhtml-include href="disqus_identifiers/write_1_batch.html"?>'''
+
+'''<indexterm><primary>write</primary></indexterm>'''
+Schedule a batch of asynchronous data writes after preceding operations, where offset and total data written must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+virtual std::vector<future<> > dispatcher::write(const std::vector< io_req< const T >> & ops)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[const std::vector< io_req< const T >> &] [] [ops] [A batch of io_req<const T> structures.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if writing data is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_write_3_length_deducing async_write (length deducing)]
+'''<?dbhtml-include href="disqus_identifiers/async_write_3_length_deducing.html"?>'''
+
+'''<indexterm><primary>async_write</primary></indexterm>'''
+Asynchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+future async_write(future<> _precondition, T && v, off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_write_4_length_specifying async_write (length specifying)]
+'''<?dbhtml-include href="disqus_identifiers/async_write_4_length_specifying.html"?>'''
+
+'''<indexterm><primary>async_write</primary></indexterm>'''
+Asynchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+future async_write(future<> _precondition, T && v, size_t _length,
+ off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[size_t] [] [_length] [The length of the item ]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:write_3_length_deducing_throwing write (length deducing throwing)]
+'''<?dbhtml-include href="disqus_identifiers/write_3_length_deducing_throwing.html"?>'''
+
+'''<indexterm><primary>write</primary></indexterm>'''
+Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+void write(future<> _precondition, T && v, off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:write_4_length_specifying_throwing write (length specifying throwing)]
+'''<?dbhtml-include href="disqus_identifiers/write_4_length_specifying_throwing.html"?>'''
+
+'''<indexterm><primary>write</primary></indexterm>'''
+Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+void write(future<> _precondition, T && v, size_t _length,
+ off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[size_t] [] [_length] [The length of the item ]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:write_4_length_deducing_non_throwing write (length deducing non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/write_4_length_deducing_non_throwing.html"?>'''
+
+'''<indexterm><primary>write</primary></indexterm>'''
+Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+void write(error_code & _ec, future<> _precondition, T && v,
+ off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:write_5_length_specifying_non_throwing write (length specifying non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/write_5_length_specifying_non_throwing.html"?>'''
+
+'''<indexterm><primary>write</primary></indexterm>'''
+Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+[heading Description]
+Related types: [^`__afio_io_req__`] Note that on Windows this call issues a separate async file operation for each buffer supplied, thus making scatter/gather i/o no more efficient than making separate calls. The big exception to this is when doing unbuffered page aligned i/o for which Windows provides special scatter/gather i/o functions. You should therefore assume that only each buffer is atomically read or written rather than the full sequence of buffers at once (this also applies to POSIX systems without the preadv()/pwritev() operations).
+
+[heading Synopsis]
+``template<class T>
+void write(error_code & _ec, future<> _precondition, T && v,
+ size_t _length, off_t _where)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[class T] [Any type. ] [ - ] [Must be specified]]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[T &&] [] [v] [Some item understood by [^`to_asio_buffers()`]]]
+[[size_t] [] [_length] [The length of the item ]]
+[[off_t] [] [_where] [The file offset to do the i/o]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][readwrite_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/group_zero.qbk b/attic/doc/generated/group_zero.qbk
new file mode 100644
index 00000000..49c2d3b0
--- /dev/null
+++ b/attic/doc/generated/group_zero.qbk
@@ -0,0 +1,146 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\group__zero.xml]
+[section:zero_2_batch zero (batch)]
+'''<?dbhtml-include href="disqus_identifiers/zero_2_batch.html"?>'''
+
+'''<indexterm><primary>zero</primary></indexterm>'''
+Schedule a batch of asynchronous zeroing and deallocations of physical storage ("hole punching") after preceding operations.
+
+[heading Description]
+Most extent based filing systems provide an optimised way of zeroing parts of a file by deallocating the storage backing those regions, and marking those regions as unwritten instead of actually writing zero bytes to storage. They appear as zeroes to anything reading those ranges, and have the big advantage of not consuming any actual physical storage. On Windows, extent deallocation writes zeros for ordinary files and only actually deallocates physical storage if the file is sparse or compressed (note that AFIO by default creates sparse files where possible, and converts any file opened for writing to a sparse file). For your information, deallocation on NTFS is on a 64Kb granularity, but the zeros are written at a byte granularity. On Linux, an attempt is made to use FALLOC\u005fFL\u005fPUNCH\u005fHOLE which if it fails then a write of zeros corresponding to the same ranges is made instead. On FreeBSD, long runs of zeros are automatically detected and eliminated on physical storage, and so zeros are simply written. On OS X, there is no formal hole punching API that we are aware of, and so zeros are simply written.
+
+[heading Synopsis]
+``future dispatcher::zero(const std::vector< future<>> & ops, const std::vector< std::vector< std::pair< off_t, off_t >>> & ranges)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[const std::vector< future<>> &] [] [ops] [A batch of op handles. ]]
+[[const std::vector< std::vector< std::pair< off_t, off_t >>> &] [] [ranges] [A batch of vectors of extents to zero and deallocate.]]
+]
+
+
+[heading Returns]
+A batch of op handles.
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if deallocation is constant time.
+[heading Exception Model]Propagates exceptions of any input preconditions with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Once a batch of input ops has been verified at the point of entry as not errored, you are guaranteed that the batch is atomically scheduled as a whole, unless a failure to allocate memory occurs.
+[heading Example][extents_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:async_zero async_zero]
+'''<?dbhtml-include href="disqus_identifiers/async_zero.html"?>'''
+
+'''<indexterm><primary>async_zero</primary></indexterm>'''
+Asynchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation.
+
+[heading Description]
+Most extent based filing systems provide an optimised way of zeroing parts of a file by deallocating the storage backing those regions, and marking those regions as unwritten instead of actually writing zero bytes to storage. They appear as zeroes to anything reading those ranges, and have the big advantage of not consuming any actual physical storage. On Windows, extent deallocation writes zeros for ordinary files and only actually deallocates physical storage if the file is sparse or compressed (note that AFIO by default creates sparse files where possible, and converts any file opened for writing to a sparse file). For your information, deallocation on NTFS is on a 64Kb granularity, but the zeros are written at a byte granularity. On Linux, an attempt is made to use FALLOC\u005fFL\u005fPUNCH\u005fHOLE which if it fails then a write of zeros corresponding to the same ranges is made instead. On FreeBSD, long runs of zeros are automatically detected and eliminated on physical storage, and so zeros are simply written. On OS X, there is no formal hole punching API that we are aware of, and so zeros are simply written.
+
+[heading Synopsis]
+``future async_zero(future<> _precondition, std::vector< std::pair< off_t, off_t >> ranges)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[std::vector< std::pair< off_t, off_t >>] [] [ranges] [A sequence of extents to zero and deallocate]]
+]
+
+
+[heading Returns]
+A future<void>
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][extents_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:zero_2_throwing zero (throwing)]
+'''<?dbhtml-include href="disqus_identifiers/zero_2_throwing.html"?>'''
+
+'''<indexterm><primary>zero</primary></indexterm>'''
+Synchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation.
+
+[heading Description]
+Most extent based filing systems provide an optimised way of zeroing parts of a file by deallocating the storage backing those regions, and marking those regions as unwritten instead of actually writing zero bytes to storage. They appear as zeroes to anything reading those ranges, and have the big advantage of not consuming any actual physical storage. On Windows, extent deallocation writes zeros for ordinary files and only actually deallocates physical storage if the file is sparse or compressed (note that AFIO by default creates sparse files where possible, and converts any file opened for writing to a sparse file). For your information, deallocation on NTFS is on a 64Kb granularity, but the zeros are written at a byte granularity. On Linux, an attempt is made to use FALLOC\u005fFL\u005fPUNCH\u005fHOLE which if it fails then a write of zeros corresponding to the same ranges is made instead. On FreeBSD, long runs of zeros are automatically detected and eliminated on physical storage, and so zeros are simply written. On OS X, there is no formal hole punching API that we are aware of, and so zeros are simply written.
+
+[heading Synopsis]
+``void zero(future<> _precondition, std::vector< std::pair< off_t, off_t >> ranges)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[std::vector< std::pair< off_t, off_t >>] [] [ranges] [A sequence of extents to zero and deallocate]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][extents_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+[section:zero_3_non_throwing zero (non throwing)]
+'''<?dbhtml-include href="disqus_identifiers/zero_3_non_throwing.html"?>'''
+
+'''<indexterm><primary>zero</primary></indexterm>'''
+Synchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation.
+
+[heading Description]
+Most extent based filing systems provide an optimised way of zeroing parts of a file by deallocating the storage backing those regions, and marking those regions as unwritten instead of actually writing zero bytes to storage. They appear as zeroes to anything reading those ranges, and have the big advantage of not consuming any actual physical storage. On Windows, extent deallocation writes zeros for ordinary files and only actually deallocates physical storage if the file is sparse or compressed (note that AFIO by default creates sparse files where possible, and converts any file opened for writing to a sparse file). For your information, deallocation on NTFS is on a 64Kb granularity, but the zeros are written at a byte granularity. On Linux, an attempt is made to use FALLOC\u005fFL\u005fPUNCH\u005fHOLE which if it fails then a write of zeros corresponding to the same ranges is made instead. On FreeBSD, long runs of zeros are automatically detected and eliminated on physical storage, and so zeros are simply written. On OS X, there is no formal hole punching API that we are aware of, and so zeros are simply written.
+
+[heading Synopsis]
+``void zero(error_code & _ec, future<> _precondition, std::vector< std::pair< off_t, off_t >> ranges)``
+
+[heading Parameters]
+
+[table
+[[Type] [Concept] [Name] [Description] ]
+[[error_code &] [] [_ec] [Error code to set. ]]
+[[future<>] [] [_precondition] [The precondition to use. ]]
+[[std::vector< std::pair< off_t, off_t >>] [] [ranges] [A sequence of extents to zero and deallocate]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[heading Complexity]Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.
+[heading Exception Model]Propagates the exception of any input precondition with an errored state at the point of dispatch, and throws a `std::invalid_argument` if any inputs have values which could not possibly be correct. Note that error code returning functions may still throw exceptions e.g. failure to allocate memory.
+[heading Example][extents_example]
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_directory_entry_hash.qbk b/attic/doc/generated/struct_directory_entry_hash.qbk
new file mode 100644
index 00000000..e5376aa6
--- /dev/null
+++ b/attic/doc/generated/struct_directory_entry_hash.qbk
@@ -0,0 +1,45 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1directory__entry__hash.xml]
+[section:directory_entry_hash directory_entry_hash]
+'''<?dbhtml-include href="disqus_identifiers/directory_entry_hash.html"?>'''
+
+'''<indexterm><primary>directory_entry_hash</primary></indexterm>'''
+A hasher for directory_entry, hashing inode and birth time (if available on this platform).
+
+[heading Synopsis]
+``struct directory_entry_hash
+{
+ // ...
+};
+``
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``size_t operator()(const directory_entry & p)``
+
+] [] [[* const directory_entry &]: ['p]:
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_enumerate_req.qbk b/attic/doc/generated/struct_enumerate_req.qbk
new file mode 100644
index 00000000..b520807b
--- /dev/null
+++ b/attic/doc/generated/struct_enumerate_req.qbk
@@ -0,0 +1,146 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1enumerate__req.xml]
+[section:enumerate_req enumerate_req]
+'''<?dbhtml-include href="disqus_identifiers/enumerate_req.html"?>'''
+[section:filter filter]
+'''<?dbhtml-include href="disqus_identifiers/filter.html"?>'''
+
+'''<indexterm><primary>filter</primary></indexterm>'''
+'''<indexterm><primary>none</primary></indexterm>'''
+'''<indexterm><primary>fastdeleted</primary></indexterm>'''
+How to do deleted file elimination on Windows.
+
+[heading Synopsis]
+``enum filter {none, fastdeleted};``
+
+[heading Values]
+
+[table
+[[Value] [Description] ]
+[[none] [Do no filtering at all. ]]
+[[fastdeleted] [Filter out AFIO deleted files based on their filename (fast and fairly reliable) ]]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+
+'''<indexterm><primary>enumerate_req</primary></indexterm>'''
+A convenience bundle of precondition, number of items to enumerate, item pattern match and metadata to prefetch.
+
+[heading Description]
+You should note that shell globs must use a restricted form for portability:
+
+Microsoft Windows NT oddly does not specify what wildcards are permitted, but I think the documentation for the kernel function FsRtlIsNameInExpression() is probably sound: * means zero or more characters, ? means any one character. Do not use <, > or " as these have special MS-DOS compatibility inducing consequences. Do not use ^ as this is the Windows wildcard escape character.
+
+POSIX further extends NT's wildcards with \[seq\] which is a subset of characters and \[!seq\] which is not any subset of characters. Here a \ is the wildcard escape character.
+
+[heading Synopsis]
+``struct enumerate_req
+{
+ future precondition; // A precondition for this operation.
+ size_t maxitems; // The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+ bool restart; // Restarts the enumeration for this open directory handle.
+ path glob; // An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+ metadata_flags metadata; // The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+ filter filtering; // Any filtering you want AFIO to do for you.
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``enumerate_req()``
+
+] [Default constructor. ] [
+
+]]
+[[``enumerate_req(future<> _precondition, size_t _maxitems = 2, bool _restart = true,
+ path _glob = path(), metadata_flags _metadata = metadata_flags::None, filter _filtering = filter::fastdeleted)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: The precondition for this operation.
+
+[* size_t]: ['_maxitems]: The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+
+[* bool]: ['_restart]: Restarts the enumeration for this open directory handle.
+
+[* path]: ['_glob]: An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+
+[* metadata_flags]: ['_metadata]: The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+
+[* filter]: ['_filtering]: Any filtering you want AFIO to do for you.
+
+
+
+]]
+[[``enumerate_req(future<> _precondition, path _glob, size_t _maxitems = 2,
+ bool _restart = true, metadata_flags _metadata = metadata_flags::None, filter _filtering = filter::fastdeleted)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: The precondition for this operation.
+
+[* path]: ['_glob]: A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+
+[* size_t]: ['_maxitems]: The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+
+[* bool]: ['_restart]: Restarts the enumeration for this open directory handle.
+
+[* metadata_flags]: ['_metadata]: The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+
+[* filter]: ['_filtering]: Any filtering you want AFIO to do for you.
+
+
+
+]]
+[[``enumerate_req(future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2,
+ bool _restart = true, path _glob = path(), filter _filtering = filter::fastdeleted)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: The precondition for this operation.
+
+[* metadata_flags]: ['_metadata]: The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+
+[* size_t]: ['_maxitems]: The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+
+[* bool]: ['_restart]: Restarts the enumeration for this open directory handle.
+
+[* path]: ['_glob]: An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+
+[* filter]: ['_filtering]: Any filtering you want AFIO to do for you.
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool validate()``
+
+] [Validates contents. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_handle_1_1mapped_file.qbk b/attic/doc/generated/struct_handle_1_1mapped_file.qbk
new file mode 100644
index 00000000..d1f6eff3
--- /dev/null
+++ b/attic/doc/generated/struct_handle_1_1mapped_file.qbk
@@ -0,0 +1,96 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1handle_1_1mapped__file.xml]
+[section:handle_mapped_file handle::mapped_file]
+'''<?dbhtml-include href="disqus_identifiers/handle_mapped_file.html"?>'''
+
+'''<indexterm><primary>handle</primary></indexterm><indexterm><primary>mapped_file</primary></indexterm>'''
+A holder of a mapped file.
+
+[heading Synopsis]
+``struct handle::mapped_file
+{
+ handle_ptr h; // The file being mapped.
+ void * addr; // The address in memory of the map.
+ size_t length; // The length of the map.
+ off_t offset; // The offset of the map into the file.
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``mapped_file(const mapped_file & )``
+
+] [] [[* const mapped_file &]: [']:
+
+
+
+]]
+[[``mapped_file(mapped_file && )``
+
+] [] [[* mapped_file &&]: [']:
+
+
+
+]]
+[[``mapped_file(handle_ptr _h, void * _addr, size_t _length,
+ off_t _offset)``
+
+] [] [[* handle_ptr]: ['_h]:
+
+[* void *]: ['_addr]:
+
+[* size_t]: ['_length]:
+
+[* off_t]: ['_offset]:
+
+
+
+]]
+[[``~mapped_file()``
+
+] [] [
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``mapped_file & operator=(const mapped_file & )``
+
+] [] [[* const mapped_file &]: [']:
+
+
+
+][
+
+]
+]
+[[``mapped_file & operator=(mapped_file && )``
+
+] [] [[* mapped_file &&]: [']:
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_io_req.qbk b/attic/doc/generated/struct_io_req.qbk
new file mode 100644
index 00000000..e56000cb
--- /dev/null
+++ b/attic/doc/generated/struct_io_req.qbk
@@ -0,0 +1,136 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1io__req.xml]
+[section:io_req io_req]
+'''<?dbhtml-include href="disqus_identifiers/io_req.html"?>'''
+
+'''<indexterm><primary>io_req</primary></indexterm>'''
+A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data [*MUST] stay around until the operation completes.
+
+[heading Synopsis]
+``template<class T>
+struct io_req
+ : public detail::io_req_impl< false >
+{
+ future precondition; // A precondition containing an open file handle for this operation.
+ std::vector< asio::mutable_buffer > buffers; // A sequence of mutable Boost.ASIO buffers to read into.
+ off_t where; // The offset from which to read.
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Description]]
+[[class T] [Any readable (if const) or writable (if non-const) type T as specified by its to_asio_buffers() overload. ]]
+]
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``io_req()``
+
+] [Default constructor. ] [
+
+]]
+[[``io_req(const io_req & o)``
+
+] [Copy constructor. ] [[* const io_req &]: ['o]:
+
+
+
+]]
+[[``io_req(io_req && o)``
+
+] [Move constructor. ] [[* io_req &&]: ['o]:
+
+
+
+]]
+[[``io_req(future<> _precondition, T * v, size_t _length,
+ off_t _where)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: An optional precondition for this operation
+
+[* T *]: ['v]: A pointer to memory or reference to object into which to read or write
+
+[* size_t]: ['_length]: The number of items to transfer
+
+[* off_t]: ['_where]: The offset at which to transfer
+
+
+
+]]
+[[``template<class U>
+io_req(future<> _precondition, U & v, off_t _where)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: An optional precondition for this operation
+
+[* U &]: ['v]: A pointer to memory or reference to object into which to read or write
+
+[* off_t]: ['_where]: The offset at which to transfer
+
+
+
+]]
+[[``template<class U, size_t N>
+io_req(future<> _precondition, U(&) v, off_t _where)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: An optional precondition for this operation
+
+[* U(&)]: ['v]: A pointer to memory or reference to object into which to read or write
+
+[* off_t]: ['_where]: The offset at which to transfer
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool validate()``
+
+] [Validates contents for correctness. ] [
+
+][
+True if contents are correct
+
+]
+]
+[[``io_req & operator=(const io_req & o)``
+
+] [Copy assignment. ] [[* const io_req &]: ['o]:
+
+
+
+][
+
+]
+]
+[[``io_req & operator=(io_req && o)``
+
+] [Move assignment. ] [[* io_req &&]: ['o]:
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_io_req_3_01const_01_t_01_4.qbk b/attic/doc/generated/struct_io_req_3_01const_01_t_01_4.qbk
new file mode 100644
index 00000000..1ab76f93
--- /dev/null
+++ b/attic/doc/generated/struct_io_req_3_01const_01_t_01_4.qbk
@@ -0,0 +1,169 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1io__req_3_01const_01_t_01_4.xml]
+[section:io_req_constt_ io_req< const T >]
+'''<?dbhtml-include href="disqus_identifiers/io_req_constt_.html"?>'''
+
+'''<indexterm><primary>io_req&lt; const T &gt;</primary></indexterm>'''
+A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data [*MUST] stay around until the operation completes.
+
+[heading Synopsis]
+``template<class T>
+struct io_req< const T >
+ : public detail::io_req_impl< true >
+{
+ future precondition; // A precondition containing an open file handle for this operation.
+ std::vector< asio::const_buffer > buffers; // A sequence of const Boost.ASIO buffers to write from.
+ off_t where; // The offset at which to write.
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Description]]
+[[class T] [Any readable (if const) or writable (if non-const) type T as specified by its to_asio_buffers() overload. ]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool validate()``
+
+] [Validates contents for correctness. ] [
+
+][
+True if contents are correct
+
+]
+]
+[[`` io_req()``
+
+] [Default constructor. ] [
+
+][
+
+]
+]
+[[`` io_req(const io_req & o)``
+
+] [Copy constructor. ] [[* const io_req &]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` io_req(io_req && o)``
+
+] [Move constructor. ] [[* io_req &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` io_req(const io_req< T > & o)``
+
+] [Copy constructor. ] [[* const io_req< T > &]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` io_req(io_req< T > && o)``
+
+] [Move constructor. ] [[* io_req< T > &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[``io_req & operator=(const io_req & o)``
+
+] [Copy assignment. ] [[* const io_req &]: ['o]:
+
+
+
+][
+
+]
+]
+[[``io_req & operator=(io_req && o)``
+
+] [Move assignment. ] [[* io_req &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` io_req(future<> _precondition, const T * v, size_t _length,
+ off_t _where)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: An optional precondition for this operation
+
+[* const T *]: ['v]: A pointer to memory or reference to object into which to read or write
+
+[* size_t]: ['_length]: The number of items to transfer
+
+[* off_t]: ['_where]: The offset at which to transfer
+
+
+
+][
+
+]
+]
+[[``template<class U>
+ io_req(future<> _precondition, const U & v, off_t _where)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: An optional precondition for this operation
+
+[* const U &]: ['v]: A pointer to memory or reference to object into which to read or write
+
+[* off_t]: ['_where]: The offset at which to transfer
+
+
+
+][
+
+]
+]
+[[``template<class U, size_t N>
+ io_req(future<> _precondition, const U(&) v, off_t _where)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: An optional precondition for this operation
+
+[* const U(&)]: ['v]: A pointer to memory or reference to object into which to read or write
+
+[* off_t]: ['_where]: The offset at which to transfer
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_io_req_3_01const_01void_01_4.qbk b/attic/doc/generated/struct_io_req_3_01const_01void_01_4.qbk
new file mode 100644
index 00000000..2cf1cff9
--- /dev/null
+++ b/attic/doc/generated/struct_io_req_3_01const_01void_01_4.qbk
@@ -0,0 +1,132 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1io__req_3_01const_01void_01_4.xml]
+[section:io_req_constvoid_ io_req< const void >]
+'''<?dbhtml-include href="disqus_identifiers/io_req_constvoid_.html"?>'''
+
+'''<indexterm><primary>io_req&lt; const void &gt;</primary></indexterm>'''
+A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data [*MUST] stay around until the operation completes.
+
+[heading Synopsis]
+``struct io_req< const void >
+ : public detail::io_req_impl< true >
+{
+ future precondition; // A precondition containing an open file handle for this operation.
+ std::vector< asio::const_buffer > buffers; // A sequence of const Boost.ASIO buffers to write from.
+ off_t where; // The offset at which to write.
+};
+``
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool validate()``
+
+] [Validates contents for correctness. ] [
+
+][
+True if contents are correct
+
+]
+]
+[[`` io_req()``
+
+] [Default constructor. ] [
+
+][
+
+]
+]
+[[`` io_req(const io_req & o)``
+
+] [Copy constructor. ] [[* const io_req &]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` io_req(io_req && o)``
+
+] [Move constructor. ] [[* io_req &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` io_req(const io_req< void > & o)``
+
+] [Copy constructor. ] [[* const io_req< void > &]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` io_req(io_req< void > && o)``
+
+] [Move constructor. ] [[* io_req< void > &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[``io_req & operator=(const io_req & o)``
+
+] [Copy assignment. ] [[* const io_req &]: ['o]:
+
+
+
+][
+
+]
+]
+[[``io_req & operator=(io_req && o)``
+
+] [Move assignment. ] [[* io_req &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` io_req(future<> _precondition, const void * v, size_t _length,
+ off_t _where)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: An optional precondition for this operation
+
+[* const void *]: ['v]: A pointer to memory or reference to object into which to read or write
+
+[* size_t]: ['_length]: The number of items to transfer
+
+[* off_t]: ['_where]: The offset at which to transfer
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_io_req_3_01void_01_4.qbk b/attic/doc/generated/struct_io_req_3_01void_01_4.qbk
new file mode 100644
index 00000000..ff414503
--- /dev/null
+++ b/attic/doc/generated/struct_io_req_3_01void_01_4.qbk
@@ -0,0 +1,112 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1io__req_3_01void_01_4.xml]
+[section:io_req_void_ io_req< void >]
+'''<?dbhtml-include href="disqus_identifiers/io_req_void_.html"?>'''
+
+'''<indexterm><primary>io_req&lt; void &gt;</primary></indexterm>'''
+A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data [*MUST] stay around until the operation completes.
+
+[heading Synopsis]
+``struct io_req< void >
+ : public detail::io_req_impl< false >
+{
+ future precondition; // A precondition containing an open file handle for this operation.
+ std::vector< asio::mutable_buffer > buffers; // A sequence of mutable Boost.ASIO buffers to read into.
+ off_t where; // The offset from which to read.
+};
+``
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool validate()``
+
+] [Validates contents for correctness. ] [
+
+][
+True if contents are correct
+
+]
+]
+[[`` io_req()``
+
+] [Default constructor. ] [
+
+][
+
+]
+]
+[[`` io_req(const io_req & o)``
+
+] [Copy constructor. ] [[* const io_req &]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` io_req(io_req && o)``
+
+] [Move constructor. ] [[* io_req &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[``io_req & operator=(const io_req & o)``
+
+] [Copy assignment. ] [[* const io_req &]: ['o]:
+
+
+
+][
+
+]
+]
+[[``io_req & operator=(io_req && o)``
+
+] [Move assignment. ] [[* io_req &&]: ['o]:
+
+
+
+][
+
+]
+]
+[[`` io_req(future<> _precondition, void * v, size_t _length,
+ off_t _where)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: An optional precondition for this operation
+
+[* void *]: ['v]: A pointer to memory or reference to object into which to read or write
+
+[* size_t]: ['_length]: The number of items to transfer
+
+[* off_t]: ['_where]: The offset at which to transfer
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_is_future.qbk b/attic/doc/generated/struct_is_future.qbk
new file mode 100644
index 00000000..236dca99
--- /dev/null
+++ b/attic/doc/generated/struct_is_future.qbk
@@ -0,0 +1,38 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1is__future.xml]
+[section:is_future is_future]
+'''<?dbhtml-include href="disqus_identifiers/is_future.html"?>'''
+
+'''<indexterm><primary>is_future</primary></indexterm>'''
+Trait for determining if a type is an afio::future<T>
+
+[heading Synopsis]
+``template<class T>
+struct is_future
+ : public false_type
+{
+ // ...
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Description]]
+[[class T] []]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_is_future_3_01future_3_01_t_01_4_01_4.qbk b/attic/doc/generated/struct_is_future_3_01future_3_01_t_01_4_01_4.qbk
new file mode 100644
index 00000000..c4a3b479
--- /dev/null
+++ b/attic/doc/generated/struct_is_future_3_01future_3_01_t_01_4_01_4.qbk
@@ -0,0 +1,38 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1is__future_3_01future_3_01_t_01_4_01_4.xml]
+[section:is_future< future< t > > is_future< future< T > >]
+'''<?dbhtml-include href="disqus_identifiers/is_future_-future_-t-_.html"?>'''
+
+'''<indexterm><primary>is_future< future< T > ></primary></indexterm>'''
+
+
+[heading Synopsis]
+``template<class T>
+struct is_future< future< T > >
+ : public true_type
+{
+ // ...
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Description]]
+[[class T] []]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_lock_req.qbk b/attic/doc/generated/struct_lock_req.qbk
new file mode 100644
index 00000000..1dbdaede
--- /dev/null
+++ b/attic/doc/generated/struct_lock_req.qbk
@@ -0,0 +1,133 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1lock__req.xml]
+[section:lock_req lock_req]
+'''<?dbhtml-include href="disqus_identifiers/lock_req.html"?>'''
+[section:type Type]
+'''<?dbhtml-include href="disqus_identifiers/type.html"?>'''
+
+'''<indexterm><primary>Type</primary></indexterm>'''
+'''<indexterm><primary>unknown</primary></indexterm>'''
+'''<indexterm><primary>read_lock</primary></indexterm>'''
+'''<indexterm><primary>write_lock</primary></indexterm>'''
+'''<indexterm><primary>unlock</primary></indexterm>'''
+
+
+[heading Synopsis]
+``enum Type {unknown, read_lock, write_lock, unlock};``
+
+[heading Values]
+
+[table
+[[Value] [Description] ]
+[[unknown] []]
+[[read_lock] []]
+[[write_lock] []]
+[[unlock] []]
+]
+
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
+
+'''<indexterm><primary>lock_req</primary></indexterm>'''
+
+
+[heading Synopsis]
+``struct lock_req
+{
+ future precondition;
+ enum boost::afio::lock_req::Type type;
+ off_t offset;
+ off_t length;
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``lock_req()``
+
+] [] [
+
+]]
+[[``lock_req(future<> _precondition, Type _type = Type::write_lock)``
+
+] [] [[* future<>]: ['_precondition]:
+
+[* Type]: ['_type]:
+
+
+
+]]
+[[``lock_req(future<> _precondition, std::nullptr_t )``
+
+] [] [[* future<>]: ['_precondition]:
+
+[* std::nullptr_t]: [']:
+
+
+
+]]
+[[``lock_req(future<> _precondition, Type _type, off_t _offset,
+ off_t _length)``
+
+] [] [[* future<>]: ['_precondition]:
+
+[* Type]: ['_type]:
+
+[* off_t]: ['_offset]:
+
+[* off_t]: ['_length]:
+
+
+
+]]
+[[``lock_req(future<> _precondition, off_t _offset, off_t _length,
+ Type _type = Type::write_lock)``
+
+] [] [[* future<>]: ['_precondition]:
+
+[* off_t]: ['_offset]:
+
+[* off_t]: ['_length]:
+
+[* Type]: ['_type]:
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool validate()``
+
+] [Validates contents. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_path_1_1direct.qbk b/attic/doc/generated/struct_path_1_1direct.qbk
new file mode 100644
index 00000000..a8ccce4d
--- /dev/null
+++ b/attic/doc/generated/struct_path_1_1direct.qbk
@@ -0,0 +1,30 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1path_1_1direct.xml]
+[section:path_direct path::direct]
+'''<?dbhtml-include href="disqus_identifiers/path_direct.html"?>'''
+
+'''<indexterm><primary>path</primary></indexterm><indexterm><primary>direct</primary></indexterm>'''
+
+
+[heading Synopsis]
+``struct path::direct
+{
+ // ...
+};
+``
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_path_1_1make_absolute.qbk b/attic/doc/generated/struct_path_1_1make_absolute.qbk
new file mode 100644
index 00000000..198deed6
--- /dev/null
+++ b/attic/doc/generated/struct_path_1_1make_absolute.qbk
@@ -0,0 +1,321 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1path_1_1make__absolute.xml]
+[section:path_make_absolute path::make_absolute]
+'''<?dbhtml-include href="disqus_identifiers/path_make_absolute.html"?>'''
+
+'''<indexterm><primary>path</primary></indexterm><indexterm><primary>make_absolute</primary></indexterm>'''
+Makes a path absolute according to the current working directory.
+
+[heading Synopsis]
+``struct path::make_absolute
+ : public path
+{
+ // ...
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``make_absolute(const path & p)``
+
+] [] [[* const path &]: ['p]:
+
+
+
+]]
+[[``make_absolute(path && p)``
+
+] [] [[* path &&]: ['p]:
+
+
+
+]]
+[[``template<class T, typename>
+make_absolute(T && p)``
+
+] [] [[* T &&]: ['p]:
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``template<class Source>
+path & assign(Source const & source)``
+
+] [Converts source to AFIO path format. ] [[* Source const &]: ['source]:
+
+
+
+][
+
+]
+]
+[[``template<class InputIterator>
+path & assign(InputIterator begin, InputIterator end)``
+
+] [] [[* InputIterator]: ['begin]:
+
+[* InputIterator]: ['end]:
+
+
+
+][
+
+]
+]
+[[``path & operator/=(const path & p)``
+
+] [] [[* const path &]: ['p]:
+
+
+
+][
+
+]
+]
+[[``template<class Source>
+path & operator/=(Source const & source)``
+
+] [] [[* Source const &]: ['source]:
+
+
+
+][
+
+]
+]
+[[``template<class Source>
+path & append(Source const & source)``
+
+] [] [[* Source const &]: ['source]:
+
+
+
+][
+
+]
+]
+[[``template<class InputIterator>
+path & append(InputIterator begin, InputIterator end)``
+
+] [] [[* InputIterator]: ['begin]:
+
+[* InputIterator]: ['end]:
+
+
+
+][
+
+]
+]
+[[``path & operator+=(const path & x)``
+
+] [] [[* const path &]: ['x]:
+
+
+
+][
+
+]
+]
+[[``path & operator+=(const string_type & x)``
+
+] [] [[* const string_type &]: ['x]:
+
+
+
+][
+
+]
+]
+[[``path & operator+=(const value_type * x)``
+
+] [] [[* const value_type *]: ['x]:
+
+
+
+][
+
+]
+]
+[[``path & operator+=(value_type x)``
+
+] [] [[* value_type]: ['x]:
+
+
+
+][
+
+]
+]
+[[``template<class Source>
+path & operator+=(Source const & x)``
+
+] [] [[* Source const &]: ['x]:
+
+
+
+][
+
+]
+]
+[[``template<class Source>
+path & concat(Source const & x)``
+
+] [] [[* Source const &]: ['x]:
+
+
+
+][
+
+]
+]
+[[``template<class InputIterator>
+path & concat(InputIterator begin, InputIterator end)``
+
+] [] [[* InputIterator]: ['begin]:
+
+[* InputIterator]: ['end]:
+
+
+
+][
+
+]
+]
+[[``path & make_preferred()``
+
+] [] [
+
+][
+
+]
+]
+[[``path & remove_filename()``
+
+] [] [
+
+][
+
+]
+]
+[[``path & replace_extension(const path & new_extension = path())``
+
+] [] [[* const path &]: ['new_extension]:
+
+
+
+][
+
+]
+]
+[[``path root_name()``
+
+] [] [
+
+][
+
+]
+]
+[[``path root_directory()``
+
+] [] [
+
+][
+
+]
+]
+[[``path root_path()``
+
+] [] [
+
+][
+
+]
+]
+[[``path relative_path()``
+
+] [] [
+
+][
+
+]
+]
+[[``path parent_path()``
+
+] [] [
+
+][
+
+]
+]
+[[``path filename()``
+
+] [] [
+
+][
+
+]
+]
+[[``path stem()``
+
+] [] [
+
+][
+
+]
+]
+[[``path extension()``
+
+] [] [
+
+][
+
+]
+]
+[[``iterator begin()``
+
+] [] [
+
+][
+
+]
+]
+[[``iterator end()``
+
+] [] [
+
+][
+
+]
+]
+[[``filesystem::path filesystem_path()``
+
+] [Return a normalised filesystem::path from an AFIO path. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_path_hash.qbk b/attic/doc/generated/struct_path_hash.qbk
new file mode 100644
index 00000000..c8eed842
--- /dev/null
+++ b/attic/doc/generated/struct_path_hash.qbk
@@ -0,0 +1,45 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1path__hash.xml]
+[section:path_hash path_hash]
+'''<?dbhtml-include href="disqus_identifiers/path_hash.html"?>'''
+
+'''<indexterm><primary>path_hash</primary></indexterm>'''
+A hasher for path.
+
+[heading Synopsis]
+``struct path_hash
+{
+ std::hash< path::string_type > hasher;
+};
+``
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``size_t operator()(const path & p)``
+
+] [] [[* const path &]: ['p]:
+
+
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_path_req.qbk b/attic/doc/generated/struct_path_req.qbk
new file mode 100644
index 00000000..00e722b9
--- /dev/null
+++ b/attic/doc/generated/struct_path_req.qbk
@@ -0,0 +1,135 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1path__req.xml]
+[section:path_req path_req]
+'''<?dbhtml-include href="disqus_identifiers/path_req.html"?>'''
+
+'''<indexterm><primary>path_req</primary></indexterm>'''
+A convenience bundle of path and flags, with optional precondition. Paths may be a path fragment (relative to the precondition) or absolute, in which case if necessary they are made canonical and absolute in the constructor according to the current working directory.
+
+[heading Synopsis]
+``struct path_req
+{
+ bool is_relative; // Whether the precondition is also where this path begins.
+ boost::afio::path path; // The filing system path to be used for this operation.
+ file_flags flags; // The flags to be used for this operation (note they can be overriden by flags passed during dispatcher construction).
+ future precondition; // An optional precondition for this operation
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``path_req()``
+
+] [Default constructor. ] [
+
+]]
+[[``path_req(const path_req & o)``
+
+] [Copy constructor. ] [[* const path_req &]: ['o]:
+
+
+
+]]
+[[``path_req(path_req && o)``
+
+] [Move constructor. ] [[* path_req &&]: ['o]:
+
+
+
+]]
+[[``path_req(absolute && o)``
+
+] [Move constructor. ] [[* absolute &&]: ['o]:
+
+
+
+]]
+[[``path_req(relative && o)``
+
+] [Move constructor. ] [[* relative &&]: ['o]:
+
+
+
+]]
+[[``template<class T, typename>
+path_req(T && _path, file_flags _flags = file_flags::none)``
+
+] [Constructs an instance. ] [[* T &&]: ['_path]: The filing system path to be used.
+
+[* file_flags]: ['_flags]: The flags to be used.
+
+
+
+]]
+[[``template<class T, typename>
+path_req(bool _is_relative, future<> _precondition, T && _path,
+ file_flags _flags = file_flags::none)``
+
+] [Constructs an instance. ] [[* bool]: ['_is_relative]: Whether the precondition is where the path begins
+
+[* future<>]: ['_precondition]: The precondition for this operation.
+
+[* T &&]: ['_path]: The filing system path to be used.
+
+[* file_flags]: ['_flags]: The flags to be used.
+
+
+
+]]
+[[``path_req(bool _is_relative, future<> _precondition, boost::afio::path _path,
+ file_flags _flags = file_flags::none)``
+
+] [] [[* bool]: ['_is_relative]:
+
+[* future<>]: ['_precondition]:
+
+[* boost::afio::path]: ['_path]:
+
+[* file_flags]: ['_flags]:
+
+
+
+]]
+[[``path_req(future<> _precondition, file_flags _flags = file_flags::none)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: The precondition for this operation (used as the path).
+
+[* file_flags]: ['_flags]: The flags to be used.
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool validate()``
+
+] [Validates contents. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[include generated/struct_path_req_1_1absolute.qbk]
+[include generated/struct_path_req_1_1relative.qbk]
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_path_req_1_1absolute.qbk b/attic/doc/generated/struct_path_req_1_1absolute.qbk
new file mode 100644
index 00000000..178c94da
--- /dev/null
+++ b/attic/doc/generated/struct_path_req_1_1absolute.qbk
@@ -0,0 +1,64 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1path__req_1_1absolute.xml]
+[section:path_req_absolute path_req::absolute]
+'''<?dbhtml-include href="disqus_identifiers/path_req_absolute.html"?>'''
+
+'''<indexterm><primary>path_req</primary></indexterm><indexterm><primary>absolute</primary></indexterm>'''
+Convenience tag type constructing an absolute path path_req.
+
+[heading Synopsis]
+``struct path_req::absolute
+ : public path_req
+{
+ bool is_relative; // Whether the precondition is also where this path begins.
+ boost::afio::path path; // The filing system path to be used for this operation.
+ file_flags flags; // The flags to be used for this operation (note they can be overriden by flags passed during dispatcher construction).
+ future precondition; // An optional precondition for this operation
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``template<class T>
+absolute(future<> _precondition, T && _path, file_flags _flags = file_flags::none)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: The precondition for this operation.
+
+[* T &&]: ['_path]: The filing system path to be used.
+
+[* file_flags]: ['_flags]: The flags to be used.
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool validate()``
+
+] [Validates contents. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_path_req_1_1relative.qbk b/attic/doc/generated/struct_path_req_1_1relative.qbk
new file mode 100644
index 00000000..79d962f4
--- /dev/null
+++ b/attic/doc/generated/struct_path_req_1_1relative.qbk
@@ -0,0 +1,73 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1path__req_1_1relative.xml]
+[section:path_req_relative path_req::relative]
+'''<?dbhtml-include href="disqus_identifiers/path_req_relative.html"?>'''
+
+'''<indexterm><primary>path_req</primary></indexterm><indexterm><primary>relative</primary></indexterm>'''
+Convenience tag type constructing a relative path path_req.
+
+[heading Synopsis]
+``struct path_req::relative
+ : public path_req
+{
+ bool is_relative; // Whether the precondition is also where this path begins.
+ boost::afio::path path; // The filing system path to be used for this operation.
+ file_flags flags; // The flags to be used for this operation (note they can be overriden by flags passed during dispatcher construction).
+ future precondition; // An optional precondition for this operation
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``template<class T>
+relative(future<> _precondition, T && _path, file_flags _flags = file_flags::none)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: The precondition for this operation.
+
+[* T &&]: ['_path]: The filing system path to be used.
+
+[* file_flags]: ['_flags]: The flags to be used.
+
+
+
+]]
+[[``relative(future<> _precondition, file_flags _flags = file_flags::none)``
+
+] [Constructs an instance. ] [[* future<>]: ['_precondition]: The precondition for this operation.
+
+[* file_flags]: ['_flags]: The flags to be used.
+
+
+
+]]
+]
+
+[heading Member Function(s)]
+[table
+[[Function] [Description] [Parameters] [Returns]]
+[[``bool validate()``
+
+] [Validates contents. ] [
+
+][
+
+]
+]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_stat_t.qbk b/attic/doc/generated/struct_stat_t.qbk
new file mode 100644
index 00000000..32d3e4d5
--- /dev/null
+++ b/attic/doc/generated/struct_stat_t.qbk
@@ -0,0 +1,79 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1stat__t.xml]
+[section:stat_t stat_t]
+'''<?dbhtml-include href="disqus_identifiers/stat_t.html"?>'''
+
+'''<indexterm><primary>stat_t</primary></indexterm>'''
+Metadata about a directory entry.
+
+[heading Description]
+This structure looks somewhat like a [^`struct stat`], and indeed it was derived from BSD's [^`struct stat`]. However there are a number of changes to better interoperate with modern practice, specifically:
+
+* inode value containers are forced to 64 bits.
+* Timestamps use C++11's [^`std::chrono::system_clock::time_point`] or Boost equivalent. The resolution of these may or may not equal what a [^`struct timespec`] can do depending on your STL.
+* The type of a file, which is available on Windows and on POSIX without needing an additional syscall, is provided by [^`st_type`] which is one of the values from [^`filesystem::file_type`].
+* As type is now separate from permissions, there is no longer a [^`st_mode`], instead being a [^`st_perms`] which is solely the permissions bits. If you want to test permission bits in [^`st_perms`] but don't want to include platform specific headers, note that [^`filesystem::perms`] contains definitions of the POSIX permissions flags.
+* The st\u005fsparse and st\u005fcompressed flags indicate if your file is sparse and/or compressed, or if the directory will compress newly created files by default. Note that on POSIX, a file is sparse if and only if st\u005fallocated < st\u005fsize which can include compressed files if that filing system is mounted with compression enabled (e.g. ZFS with ZLE compression which elides runs of zeros).
+* The st\u005freparse\u005fpoint is a Windows only flag and is never set on POSIX, even on a NTFS volume.
+
+
+
+[heading Synopsis]
+``struct stat_t
+{
+ uint64_t st_dev; // inode of device containing file (POSIX only)
+ uint64_t st_ino; // inode of file (Windows, POSIX)
+ filesystem::file_type st_type; // type of file (Windows, POSIX)
+ filesystem::perms st_perms; // uint16_t bitfield perms of file (POSIX only)
+ int16_t st_nlink; // number of hard links (Windows, POSIX)
+ int16_t st_uid; // user ID of the file (POSIX only)
+ int16_t st_gid; // group ID of the file (POSIX only)
+ dev_t st_rdev; // id of file if special (POSIX only)
+ chrono::system_clock::time_point st_atim; // time of last access (Windows, POSIX)
+ chrono::system_clock::time_point st_mtim; // time of last data modification (Windows, POSIX)
+ chrono::system_clock::time_point st_ctim; // time of last status change (Windows, POSIX)
+ off_t st_size; // file size, in bytes (Windows, POSIX)
+ off_t st_allocated; // bytes allocated for file (Windows, POSIX)
+ off_t st_blocks; // number of blocks allocated (Windows, POSIX)
+ uint16_t st_blksize; // block size used by this device (Windows, POSIX)
+ uint32_t st_flags; // user defined flags for file (FreeBSD, OS X, zero otherwise)
+ uint32_t st_gen; // file generation number (FreeBSD, OS X, zero otherwise)
+ chrono::system_clock::time_point st_birthtim; // time of file creation (Windows, FreeBSD, OS X, zero otherwise)
+ unsigned st_sparse; // if this file is sparse, or this directory capable of sparse files (Windows, POSIX)
+ unsigned st_compressed; // if this file is compressed, or this directory capable of compressed files (Windows)
+ unsigned st_reparse_point; // if this file or directory is a reparse point (Windows)
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``stat_t()``
+
+] [Constructs a UNINITIALIZED instance i.e. full of random garbage. ] [
+
+]]
+[[``stat_t(std::nullptr_t )``
+
+] [Constructs a zeroed instance. ] [[* std::nullptr_t]: [']:
+
+
+
+]]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_statfs_t.qbk b/attic/doc/generated/struct_statfs_t.qbk
new file mode 100644
index 00000000..35a38edf
--- /dev/null
+++ b/attic/doc/generated/struct_statfs_t.qbk
@@ -0,0 +1,55 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1statfs__t.xml]
+[section:statfs_t statfs_t]
+'''<?dbhtml-include href="disqus_identifiers/statfs_t.html"?>'''
+
+'''<indexterm><primary>statfs_t</primary></indexterm>'''
+Metadata about a filing system. Unsupported entries are -1.
+
+[heading Synopsis]
+``struct statfs_t
+{
+ struct boost::afio::statfs_t::f_flags_t f_flags; // copy of mount exported flags (Windows, POSIX)
+ uint64_t f_bsize; // fundamental filesystem block size (Windows, POSIX)
+ uint64_t f_iosize; // optimal transfer block size (Windows, POSIX)
+ uint64_t f_blocks; // total data blocks in filesystem (Windows, POSIX)
+ uint64_t f_bfree; // free blocks in filesystem (Windows, POSIX)
+ uint64_t f_bavail; // free blocks avail to non-superuser (Windows, POSIX)
+ uint64_t f_files; // total file nodes in filesystem (POSIX)
+ uint64_t f_ffree; // free nodes avail to non-superuser (POSIX)
+ uint32_t f_namemax; // maximum filename length (Windows, POSIX)
+ int16_t f_owner; // user that mounted the filesystem (BSD, OS X)
+ uint64_t f_fsid; // filesystem id (Windows, POSIX)
+ std::string f_fstypename; // filesystem type name (Windows, POSIX)
+ std::string f_mntfromname; // mounted filesystem (Windows, POSIX)
+ path f_mntonname; // directory on which mounted (Windows, POSIX)
+};
+``
+
+[heading Constructor(s)]
+[table
+[[Function] [Description] [Parameters] ]
+[[``statfs_t()``
+
+] [] [
+
+]]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+[include generated/struct_statfs_t_1_1f_flags_t.qbk]
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_statfs_t_1_1f_flags_t.qbk b/attic/doc/generated/struct_statfs_t_1_1f_flags_t.qbk
new file mode 100644
index 00000000..2839e82f
--- /dev/null
+++ b/attic/doc/generated/struct_statfs_t_1_1f_flags_t.qbk
@@ -0,0 +1,37 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1statfs__t_1_1f__flags__t.xml]
+[section:statfs_t_f_flags_t statfs_t::f_flags_t]
+'''<?dbhtml-include href="disqus_identifiers/statfs_t_f_flags_t.html"?>'''
+
+'''<indexterm><primary>statfs_t</primary></indexterm><indexterm><primary>f_flags_t</primary></indexterm>'''
+
+
+[heading Synopsis]
+``struct statfs_t::f_flags_t
+{
+ uint32_t rdonly; // Filing system is read only (Windows, POSIX)
+ uint32_t noexec; // Filing system cannot execute programs (POSIX)
+ uint32_t nosuid; // Filing system cannot superuser (POSIX)
+ uint32_t acls; // Filing system provides ACLs (Windows, POSIX)
+ uint32_t xattr; // Filing system provides extended attributes (Windows, POSIX)
+ uint32_t compression; // Filing system provides whole volume compression (Windows, POSIX)
+ uint32_t extents; // Filing system provides extent based file storage (sparse files) (Windows, POSIX)
+ uint32_t filecompression; // Filing system provides per-file selectable compression (Windows)
+};
+``
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_utils_1_1page_allocator_1_1rebind.qbk b/attic/doc/generated/struct_utils_1_1page_allocator_1_1rebind.qbk
new file mode 100644
index 00000000..9fff58bf
--- /dev/null
+++ b/attic/doc/generated/struct_utils_1_1page_allocator_1_1rebind.qbk
@@ -0,0 +1,37 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1utils_1_1page__allocator_1_1rebind.xml]
+[section:utils_page_allocator_rebind utils::page_allocator::rebind]
+'''<?dbhtml-include href="disqus_identifiers/utils_page_allocator_rebind.html"?>'''
+
+'''<indexterm><primary>utils</primary></indexterm><indexterm><primary>page_allocator</primary></indexterm><indexterm><primary>rebind</primary></indexterm>'''
+
+
+[heading Synopsis]
+``template<class U>
+struct utils::page_allocator::rebind
+{
+ // ...
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Description]]
+[[class U] []]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/generated/struct_utils_1_1page_allocator_3_01void_01_4_1_1rebind.qbk b/attic/doc/generated/struct_utils_1_1page_allocator_3_01void_01_4_1_1rebind.qbk
new file mode 100644
index 00000000..29a52216
--- /dev/null
+++ b/attic/doc/generated/struct_utils_1_1page_allocator_3_01void_01_4_1_1rebind.qbk
@@ -0,0 +1,37 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+
+[/ Generated by doxygen_xml2qbk 1.1.1, don't change, will be overwritten automatically]
+[/ Generated from doxy/doxygen_output/xml\structboost_1_1afio_1_1utils_1_1page__allocator_3_01void_01_4_1_1rebind.xml]
+[section:utils_page_allocator< void >_rebind utils::page_allocator< void >::rebind]
+'''<?dbhtml-include href="disqus_identifiers/utils_page_allocator_-void-_.html"?>'''
+
+'''<indexterm><primary>utils</primary></indexterm><indexterm><primary>page_allocator< void ></primary></indexterm><indexterm><primary>rebind</primary></indexterm>'''
+
+
+[heading Synopsis]
+``template<class U>
+struct utils::page_allocator< void >::rebind
+{
+ // ...
+};
+``
+
+[heading Template parameter(s)]
+[table
+[[Parameter] [Description]]
+[[class U] []]
+]
+
+[heading Header]
+`#include <boost/afio/v2/afio.hpp>`
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+[endsect]
+
diff --git a/attic/doc/html/myboostbook.css b/attic/doc/html/myboostbook.css
new file mode 100644
index 00000000..21591724
--- /dev/null
+++ b/attic/doc/html/myboostbook.css
@@ -0,0 +1,723 @@
+/*=============================================================================
+ Copyright (c) 2004 Joel de Guzman
+ http://spirit.sourceforge.net/
+
+ Distributed under the Boost Software License, Version 1.0. (See accompany-
+ ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================*/
+
+/*=============================================================================
+ Body defaults
+=============================================================================*/
+
+ body
+ {
+ margin: 1em;
+ font-family: sans-serif;
+ }
+
+/*=============================================================================
+ Paragraphs
+=============================================================================*/
+
+ p
+ {
+ text-align: left;
+ font-size: 10pt;
+ line-height: 1.15;
+ }
+
+/*=============================================================================
+ Program listings
+=============================================================================*/
+
+ /* Code on paragraphs */
+ p tt.computeroutput
+ {
+ font-size: 9pt;
+ }
+
+ pre.synopsis
+ {
+ font-size: 9pt;
+ margin: 1pc 4% 0pc 4%;
+ padding: 0.5pc 0.5pc 0.5pc 0.5pc;
+ }
+
+ .programlisting,
+ .screen
+ {
+ font-size: 9pt;
+ display: block;
+ margin: 1pc 4% 0pc 4%;
+ padding: 0.5pc 0.5pc 0.5pc 0.5pc;
+ }
+
+ /* Program listings in tables don't get borders */
+ td .programlisting,
+ td .screen
+ {
+ margin: 0pc 0pc 0pc 0pc;
+ padding: 0pc 0pc 0pc 0pc;
+ }
+
+/*=============================================================================
+ Headings
+=============================================================================*/
+
+ h1, h2, h3, h4, h5, h6
+ {
+ text-align: left;
+ margin: 1em 0em 0.5em 0em;
+ font-weight: bold;
+ }
+
+ h1 { font-size: 140%; }
+ h2 { font-weight: bold; font-size: 140%; }
+ h3 { font-weight: bold; font-size: 130%; }
+ h4 { font-weight: bold; font-size: 120%; }
+ h5 { font-weight: normal; font-style: italic; font-size: 110%; }
+ h6 { font-weight: normal; font-style: italic; font-size: 100%; }
+
+ /* Top page titles */
+ title,
+ h1.title,
+ h2.title
+ h3.title,
+ h4.title,
+ h5.title,
+ h6.title,
+ .refentrytitle
+ {
+ font-weight: bold;
+ margin-bottom: 1pc;
+ }
+
+ h1.title { font-size: 140% }
+ h2.title { font-size: 140% }
+ h3.title { font-size: 130% }
+ h4.title { font-size: 120% }
+ h5.title { font-size: 110% }
+ h6.title { font-size: 100% }
+
+ .section h1
+ {
+ margin: 0em 0em 0.5em 0em;
+ font-size: 140%;
+ }
+
+ .section h2 { font-size: 140% }
+ .section h3 { font-size: 130% }
+ .section h4 { font-size: 120% }
+ .section h5 { font-size: 110% }
+ .section h6 { font-size: 100% }
+
+ /* Code on titles */
+ h1 tt.computeroutput { font-size: 140% }
+ h2 tt.computeroutput { font-size: 140% }
+ h3 tt.computeroutput { font-size: 130% }
+ h4 tt.computeroutput { font-size: 130% }
+ h5 tt.computeroutput { font-size: 130% }
+ h6 tt.computeroutput { font-size: 130% }
+
+
+/*=============================================================================
+ Author
+=============================================================================*/
+
+ h3.author
+ {
+ font-size: 100%
+ }
+
+/*=============================================================================
+ Lists
+=============================================================================*/
+
+ li
+ {
+ font-size: 10pt;
+ line-height: 1.3;
+ }
+
+ /* Unordered lists */
+ ul
+ {
+ text-align: left;
+ }
+
+ /* Ordered lists */
+ ol
+ {
+ text-align: left;
+ }
+
+/*=============================================================================
+ Links
+=============================================================================*/
+
+ a
+ {
+ text-decoration: none; /* no underline */
+ }
+
+ a:hover
+ {
+ text-decoration: underline;
+ }
+
+/*=============================================================================
+ Spirit style navigation
+=============================================================================*/
+
+ .spirit-nav
+ {
+ text-align: right;
+ }
+
+ .spirit-nav a
+ {
+ color: white;
+ padding-left: 0.5em;
+ }
+
+ .spirit-nav img
+ {
+ border-width: 0px;
+ }
+
+/*=============================================================================
+ Copyright footer
+=============================================================================*/
+ .copyright-footer
+ {
+ text-align: right;
+ font-size: 70%;
+ }
+
+ .copyright-footer p
+ {
+ text-align: right;
+ font-size: 80%;
+ }
+
+/*=============================================================================
+ Table of contents
+=============================================================================*/
+
+ div.toc
+ {
+ margin: 1pc 4% 0pc 4%;
+ padding: 0.1pc 1pc 0.1pc 1pc;
+ font-size: 80%;
+ line-height: 1.15;
+ }
+
+ .boost-toc
+ {
+ float: right;
+ padding: 0.5pc;
+ }
+
+ /* Code on toc */
+ .toc .computeroutput { font-size: 120% }
+
+ /* No margin on nested menus */
+
+ .toc dl dl { margin: 0; }
+
+/*=============================================================================
+ Tables
+=============================================================================*/
+
+ .table-title,
+ div.table p.title
+ {
+ margin-left: 4%;
+ padding-right: 0.5em;
+ padding-left: 0.5em;
+ }
+
+ .informaltable table,
+ .table table
+ {
+ width: 92%;
+ margin-left: 4%;
+ margin-right: 4%;
+ }
+
+ div.informaltable table,
+ div.table table
+ {
+ padding: 4px;
+ }
+
+ /* Table Cells */
+ div.informaltable table tr td,
+ div.table table tr td
+ {
+ padding: 0.5em;
+ text-align: left;
+ font-size: 9pt;
+ }
+
+ div.informaltable table tr th,
+ div.table table tr th
+ {
+ padding: 0.5em 0.5em 0.5em 0.5em;
+ border: 1pt solid white;
+ font-size: 80%;
+ }
+
+ table.simplelist
+ {
+ width: auto !important;
+ margin: 0em !important;
+ padding: 0em !important;
+ border: none !important;
+ }
+ table.simplelist td
+ {
+ margin: 0em !important;
+ padding: 0em !important;
+ text-align: left !important;
+ font-size: 9pt !important;
+ border: none !important;
+ }
+
+/*=============================================================================
+ Blurbs
+=============================================================================*/
+
+ div.note,
+ div.tip,
+ div.important,
+ div.caution,
+ div.warning,
+ p.blurb
+ {
+ font-size: 9pt; /* A little bit smaller than the main text */
+ line-height: 1.2;
+ display: block;
+ margin: 1pc 4% 0pc 4%;
+ padding: 0.5pc 0.5pc 0.5pc 0.5pc;
+ }
+
+ p.blurb img
+ {
+ padding: 1pt;
+ }
+
+/*=============================================================================
+ Variable Lists
+=============================================================================*/
+
+ div.variablelist
+ {
+ margin: 1em 0;
+ }
+
+ /* Make the terms in definition lists bold */
+ div.variablelist dl dt,
+ span.term
+ {
+ font-weight: bold;
+ font-size: 10pt;
+ }
+
+ div.variablelist table tbody tr td
+ {
+ text-align: left;
+ vertical-align: top;
+ padding: 0em 2em 0em 0em;
+ font-size: 10pt;
+ margin: 0em 0em 0.5em 0em;
+ line-height: 1;
+ }
+
+ div.variablelist dl dt
+ {
+ margin-bottom: 0.2em;
+ }
+
+ div.variablelist dl dd
+ {
+ margin: 0em 0em 0.5em 2em;
+ font-size: 10pt;
+ }
+
+ div.variablelist table tbody tr td p,
+ div.variablelist dl dd p
+ {
+ margin: 0em 0em 0.5em 0em;
+ line-height: 1;
+ }
+
+/*=============================================================================
+ Misc
+=============================================================================*/
+
+ /* Title of books and articles in bibliographies */
+ span.title
+ {
+ font-style: italic;
+ }
+
+ span.underline
+ {
+ text-decoration: underline;
+ }
+
+ span.strikethrough
+ {
+ text-decoration: line-through;
+ }
+
+ /* Copyright, Legal Notice */
+ div div.legalnotice p
+ {
+ text-align: left
+ }
+
+/*=============================================================================
+ Colors
+=============================================================================*/
+
+ @media screen
+ {
+ body {
+ background-color: #FFFFFF;
+ color: #000000;
+ }
+
+ /* Syntax Highlighting */
+ .keyword { color: #0000AA; }
+ .identifier { color: #000000; }
+ .special { color: #707070; }
+ .preprocessor { color: #402080; }
+ .char { color: teal; }
+ .comment { color: #800000; }
+ .string { color: teal; }
+ .number { color: teal; }
+ .white_bkd { background-color: #FFFFFF; }
+ .dk_grey_bkd { background-color: #999999; }
+
+ /* Links */
+ a, a .keyword, a .identifier, a .special, a .preprocessor
+ a .char, a .comment, a .string, a .number
+ {
+ color: #005a9c;
+ }
+
+ a:visited, a:visited .keyword, a:visited .identifier,
+ a:visited .special, a:visited .preprocessor a:visited .char,
+ a:visited .comment, a:visited .string, a:visited .number
+ {
+ color: #9c5a9c;
+ }
+
+ h1 a, h2 a, h3 a, h4 a, h5 a, h6 a,
+ h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover, h5 a:hover, h6 a:hover,
+ h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited
+ {
+ text-decoration: none; /* no underline */
+ color: #000000;
+ }
+
+ /* Copyright, Legal Notice */
+ .copyright
+ {
+ color: #666666;
+ font-size: small;
+ }
+
+ div div.legalnotice p
+ {
+ color: #666666;
+ }
+
+ /* Program listing */
+ pre.synopsis
+ {
+ border: 1px solid #DCDCDC;
+ }
+
+ .programlisting,
+ .screen
+ {
+ border: 1px solid #DCDCDC;
+ }
+
+ td .programlisting,
+ td .screen
+ {
+ border: 0px solid #DCDCDC;
+ }
+
+ /* Blurbs */
+ div.note,
+ div.tip,
+ div.important,
+ div.caution,
+ div.warning,
+ p.blurb
+ {
+ border: 1px solid #DCDCDC;
+ }
+
+ /* Table of contents */
+ div.toc
+ {
+ border: 1px solid #DCDCDC;
+ }
+
+ /* Tables */
+ div.informaltable table tr td,
+ div.table table tr td
+ {
+ border: 1px solid #DCDCDC;
+ }
+
+ div.informaltable table tr th,
+ div.table table tr th
+ {
+ background-color: #F0F0F0;
+ border: 1px solid #DCDCDC;
+ }
+
+ .copyright-footer
+ {
+ color: #8F8F8F;
+ }
+
+ /* Misc */
+ span.highlight
+ {
+ color: #00A000;
+ }
+ }
+
+ @media print
+ {
+ /* Links */
+ a
+ {
+ color: black;
+ }
+
+ a:visited
+ {
+ color: black;
+ }
+
+ .spirit-nav
+ {
+ display: none;
+ }
+
+ /* Program listing */
+ pre.synopsis
+ {
+ border: 1px solid gray;
+ }
+
+ .programlisting,
+ .screen
+ {
+ border: 1px solid gray;
+ }
+
+ td .programlisting,
+ td .screen
+ {
+ border: 0px solid #DCDCDC;
+ }
+
+ /* Table of contents */
+ div.toc
+ {
+ border: 1px solid gray;
+ }
+
+ .informaltable table,
+ .table table
+ {
+ border: 1px solid gray;
+ border-collapse: collapse;
+ }
+
+ /* Tables */
+ div.informaltable table tr td,
+ div.table table tr td
+ {
+ border: 1px solid gray;
+ }
+
+ div.informaltable table tr th,
+ div.table table tr th
+ {
+ border: 1px solid gray;
+ }
+
+ table.simplelist tr td
+ {
+ border: none !important;
+ }
+
+ /* Misc */
+ span.highlight
+ {
+ font-weight: bold;
+ }
+ }
+
+/*=============================================================================
+ Images
+=============================================================================*/
+
+ span.inlinemediaobject img
+ {
+ vertical-align: middle;
+ }
+
+/*==============================================================================
+ Super and Subscript: style so that line spacing isn't effected, see
+ http://www.adobe.com/cfusion/communityengine/index.cfm?event=showdetails&productId=1&postId=5341
+==============================================================================*/
+
+sup,
+sub {
+ height: 0;
+ line-height: 1;
+ vertical-align: baseline;
+ position: relative;
+
+}
+
+/* For internet explorer: */
+
+* html sup,
+* html sub {
+ vertical-align: bottom;
+}
+
+sup {
+ bottom: 1ex;
+}
+
+sub {
+ top: .5ex;
+}
+
+/*==============================================================================
+ Indexes: pretty much the same as the TOC.
+==============================================================================*/
+
+ .index
+ {
+ font-size: 80%;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ margin-left: 0px;
+ }
+
+ .index ul
+ {
+ padding-left: 3em;
+ }
+
+ .index p
+ {
+ padding: 2px;
+ margin: 2px;
+ }
+
+ .index-entry-level-0
+ {
+ font-weight: bold;
+ }
+
+ .index em
+ {
+ font-weight: bold;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/* Make TOC subsections multi-column */
+body div.chapter div.toc dl dd dl dd dl dd dl, body div.section div.toc dl dd dl dd dl {
+ -moz-column-count:auto;
+ -webkit-column-count:auto;
+ column-count:auto;
+ -moz-column-width: 30em;
+ -webkit-column-width: 30em;
+ column-width: 30em;
+}
+
+/* Stop APIs consuming all the horizontal space */
+div.informaltable table tbody tr td pre.table-programlisting {
+ white-space: pre-wrap;
+ padding-left: 4em;
+ text-indent: -4em;
+}
+pre.programlisting {
+ white-space: pre-wrap;
+}
+
+/* Put some spacing between list items */
+div.orderedlist ol.orderedlist li.listitem, div.itemizedlist ul.itemizedlist li.listitem {
+ padding-top: 0.25em;
+ padding-bottom: 0.25em;
+}
+
+/* Add text alignment and text colours */
+span.aligncenter { display: inline-block; width: 100%; text-align: center; }
+span.alignright { display: inline-block; width: 100%; text-align: right; }
+span.alignjustify { display: inline-block; width: 100%; text-align: justify; }
+span.red { color: red; }
+span.green { color: green; }
+
+/* Have Disqus comment counts be small */
+span.disqus-comment-count { font-size: x-small; margin-left: 1em; }
+
+/* Try it now online button */
+div.spirit-nav form { display: inline; float: left; height: 0px; }
+input.tryitnowbtn {
+ background: #3498db;
+ background-image: -webkit-linear-gradient(top, #3498db, #17425c);
+ background-image: -moz-linear-gradient(top, #3498db, #17425c);
+ background-image: -ms-linear-gradient(top, #3498db, #17425c);
+ background-image: -o-linear-gradient(top, #3498db, #17425c);
+ background-image: linear-gradient(to bottom, #3498db, #17425c);
+ -webkit-border-radius: 28;
+ -moz-border-radius: 28;
+ border-radius: 28px;
+ text-shadow: 2px 2px 3px #001040;
+ font-family: Arial;
+ color: #ffffff;
+ font-size: 12px;
+ padding: 2px 8px 2px 8px;
+ text-decoration: none;
+}
+input.tryitnowbtn:hover {
+ background: #46fc3c;
+ background-image: -webkit-linear-gradient(top, #46fc3c, #218a21);
+ background-image: -moz-linear-gradient(top, #46fc3c, #218a21);
+ background-image: -ms-linear-gradient(top, #46fc3c, #218a21);
+ background-image: -o-linear-gradient(top, #46fc3c, #218a21);
+ background-image: linear-gradient(to bottom, #46fc3c, #218a21);
+ text-decoration: none;
+}
diff --git a/attic/doc/latencies.xlsx b/attic/doc/latencies.xlsx
new file mode 100644
index 00000000..2eef61ff
--- /dev/null
+++ b/attic/doc/latencies.xlsx
Binary files differ
diff --git a/attic/doc/make_qbk.py b/attic/doc/make_qbk.py
new file mode 100755
index 00000000..0f1a3c00
--- /dev/null
+++ b/attic/doc/make_qbk.py
@@ -0,0 +1,245 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+# ===========================================================================
+# Use, modification and distribution is subject to the Boost Software License,
+# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+# ============================================================================
+
+import os, sys, glob, shutil
+
+os.chdir(os.path.dirname(sys.argv[0]))
+
+if 'DOXYGEN' in os.environ:
+ doxygen_cmd = os.environ['DOXYGEN']
+else:
+ doxygen_cmd = 'doxygen'
+
+if 'DOXYGEN_XML2QBK' in os.environ:
+ doxygen_xml2qbk_cmd = os.environ['DOXYGEN_XML2QBK']
+else:
+ doxygen_xml2qbk_cmd = 'doxygen_xml2qbk'
+
+if os.path.exists("generated"):
+ shutil.rmtree("generated", True)
+if os.path.exists("disqus_identifiers"):
+ shutil.rmtree("disqus_identifiers", True)
+if os.path.exists("html/afio"):
+ shutil.rmtree("html/afio", True)
+if os.path.exists("doxy/doxygen_output"):
+ shutil.rmtree("doxy/doxygen_output", True)
+os.mkdir("generated")
+os.mkdir("disqus_identifiers")
+os.mkdir("doxy/doxygen_output")
+
+cmd = doxygen_xml2qbk_cmd
+cmd = cmd + " --xml %s"
+cmd = cmd + " --start_include boost/afio/"
+cmd = cmd + " --convenience_header_path ../../include/boost/afio/"
+cmd = cmd + " --convenience_headers afio.hpp"
+cmd = cmd + " --skip_namespace boost::afio::"
+cmd = cmd + " --copyright copyright_block.qbk"
+#cmd = cmd + " --output_style alt
+cmd = cmd + ' --output_member_variables ""'
+cmd = cmd + " > generated/%s.qbk"
+
+def call_doxygen():
+ os.chdir("doxy");
+ os.system(doxygen_cmd)
+ os.chdir("..")
+
+def path2identifier(prefix, i):
+ i=i[len(prefix):-4]
+ return i.replace('__', '_')
+
+call_doxygen()
+
+# Add all classes
+for i in glob.glob("doxy/doxygen_output/xml/classboost_1_1afio_1_1*.xml"):
+ if "detail_1_1" not in i:
+ os.system(cmd % (i, "class_"+path2identifier("doxy/doxygen_output/xml/classboost_1_1afio_1_1", i)));
+# Add all structs
+for i in glob.glob("doxy/doxygen_output/xml/structboost_1_1afio_1_1*.xml"):
+ if "detail_1_1" not in i:
+ os.system(cmd % (i, "struct_"+path2identifier("doxy/doxygen_output/xml/structboost_1_1afio_1_1", i)));
+# Add all namespaces
+#for i in glob.glob("doxy/doxygen_output/xml/namespaceboost_1_1afio_1_1*.xml"):
+# if "detail" not in i:
+# os.system(cmd % (i, "namespace_"+path2identifier("doxy/doxygen_output/xml/namespaceboost_1_1afio_1_1", i)));
+# Add all groups
+for i in glob.glob("doxy/doxygen_output/xml/group__*.xml"):
+ if "detail_1_1" not in i:
+ os.system(cmd % (i, "group_"+path2identifier("doxy/doxygen_output/xml/group__", i)));
+
+# Patch broken index term generation
+for i in glob.glob("generated/struct_io_req_3_01*.qbk"):
+ with open(i, "r+b") as ih:
+ t=ih.read();
+ # Fix erroneous expansions of nested types
+ t=t.replace("</primary></indexterm><indexterm><primary>", "::")
+ # Fix failure to escape < and >
+ it1=t.find("<indexterm><primary>")+20
+ it2=t.find("</primary></indexterm>", it1)
+ indexterm=t[it1:it2]
+ indexterm=indexterm.replace("<", "&lt;")
+ indexterm=indexterm.replace(">", "&gt;")
+ t=t[:it1]+indexterm+t[it2:]
+ # Fix failure to collapse section ids
+ si1=t.find("[section:io_req<")
+ if si1!=-1:
+ si1+=15;
+ si2=si1+1
+ count=1
+ while count>0:
+ if t[si2]=='<': count+=1
+ elif t[si2]=='>': count-=1
+ si2+=1
+ postfix=t[si1:si2]
+ postfix=postfix.replace("<", "_")
+ postfix=postfix.replace(">", "_")
+ postfix=postfix.replace(",", "_")
+ postfix=postfix.replace(" ", "")
+ t=t[:si1]+postfix+t[si2:]
+ ih.seek(0)
+ ih.truncate(0)
+ ih.write(t)
+for i in glob.glob("generated/class_enqueued_task*.qbk"):
+ with open(i, "r+b") as ih:
+ t=ih.read();
+ # Fix failure to escape < and >
+ it1=t.find("<indexterm><primary>")+20
+ it2=t.find("</primary></indexterm>", it1)
+ indexterm=t[it1:it2]
+ indexterm=indexterm.replace("<", "&lt;")
+ indexterm=indexterm.replace(">", "&gt;")
+ t=t[:it1]+indexterm+t[it2:]
+ # Fix failure to collapse section ids
+ si1=t.find("[section:enqueued_task<")
+ if si1!=-1:
+ si1+=22;
+ si2=si1+1
+ count=1
+ while count>0:
+ if t[si2]=='<': count+=1
+ elif t[si2]=='>': count-=1
+ si2+=1
+ postfix=t[si1:si2]
+ postfix=postfix.replace("<", "_")
+ postfix=postfix.replace(">", "_")
+ postfix=postfix.replace("(", "_")
+ postfix=postfix.replace(")", "_")
+ postfix=postfix.replace(",", "_")
+ postfix=postfix.replace(" ", "")
+ t=t[:si1]+postfix+t[si2:]
+ ih.seek(0)
+ ih.truncate(0)
+ ih.write(t)
+for i in glob.glob("generated/class_future*.qbk"):
+ with open(i, "r+b") as ih:
+ t=ih.read();
+ # Fix failure to escape < and >
+ it1=t.find("<indexterm><primary>")+20
+ it2=t.find("</primary></indexterm>", it1)
+ indexterm=t[it1:it2]
+ indexterm=indexterm.replace("<", "&lt;")
+ indexterm=indexterm.replace(">", "&gt;")
+ t=t[:it1]+indexterm+t[it2:]
+ # Fix failure to collapse section ids
+ si1=t.find("[section:future<")
+ if si1!=-1:
+ si1+=15;
+ si2=si1+1
+ count=1
+ while count>0:
+ if t[si2]=='<': count+=1
+ elif t[si2]=='>': count-=1
+ si2+=1
+ postfix=t[si1:si2]
+ postfix=postfix.replace("<", "_")
+ postfix=postfix.replace(">", "_")
+ postfix=postfix.replace("(", "_")
+ postfix=postfix.replace(")", "_")
+ postfix=postfix.replace(",", "_")
+ postfix=postfix.replace(" ", "")
+ t=t[:si1]+postfix+t[si2:]
+ ih.seek(0)
+ ih.truncate(0)
+ ih.write(t)
+
+# Patch failure to put in-class enums in the right section
+for i in glob.glob("generated/class_*.qbk")+glob.glob("generated/struct_*.qbk"):
+ with open(i, "r+b") as ih:
+ t=ih.readlines()
+ sections=[]
+ stack=[]
+ n=-1
+ for line in t:
+ n+=1
+ if line[:9]=="[section:":
+ stack.append(n)
+ elif line[:9]=="[endsect]":
+ if len(stack)==1:
+ sections.append((stack[0], n))
+ stack.pop()
+ if len(sections)>2:
+ print("There are more than two standalone sections in "+i+". This is bad.")
+ elif len(sections)>1:
+ print("In "+i+" relocating "+t[sections[0][0]]+" into "+t[sections[1][0]])
+ t.insert(sections[0][0], t.pop(sections[1][0]))
+ with open(i, "w+b") as ih:
+ ih.writelines(t)
+
+def write_disqus_fragment(name, title):
+ with open("disqus_identifiers/"+name+".html", "wt") as identh:
+ identh.write("""<script type="text/javascript">
+var disqus_identifier = '"""+name+"""';
+var disqus_title = 'Boost.AFIO """+title+"""';
+</script>
+""")
+
+# Patch all reference sections with Disqus commenting
+for i in glob.glob("generated/*.qbk"):
+ with open(i, "r+b") as ih:
+ t=ih.readlines()
+ n=-1
+ skip=0
+ for line in t:
+ n+=1
+ if skip:
+ skip=skip-1
+ continue
+ if line[:9]=="[section:":
+ name=line[9:-2];
+ if name[-1]==']': name=name[:-1]
+ firstspace=name.find(' ')
+ if name[firstspace-1]=='<':
+ firstspace=name.find('>', firstspace)+1
+ title=name[firstspace+1:].replace('<', '&lt;').replace('>', '&gt;')
+ name=name[:firstspace].replace('<', '_').replace('>', '_').replace(' ', '-')
+ write_disqus_fragment(name, title)
+ t.insert(n+1, """'''<?dbhtml-include href="disqus_identifiers/"""+name+""".html"?>'''
+""")
+ elif line[:9]=="[endsect]":
+ t.insert(n, """'''<?dbhtml-include href="disqus_comments.html"?>'''
+""")
+ skip=2
+ with open(i, "w+b") as ih:
+ ih.writelines(t)
+
+# Generate disqus html fragments for these sections
+disqus_sections=[
+ ("introduction", "Introduction"),
+ ("design_rationale", "Design Introduction and Rationale"),
+ ("overview", "Single page cheat sheet"),
+ ("compilation", "Compilation"),
+ ("hello_world", "Hello World, asynchronously!"),
+ ("file_concat", "Concatenating files"),
+ ("atomic_logging", "Achieving atomicity on the filing system"),
+ ("filesystem_races", "Handling races on the filing system"),
+ ("so_what", "What benefit does asynchronous file i/o bring me? A demonstration of AFIO's power")
+]
+for name, title in disqus_sections:
+ write_disqus_fragment(name, title)
+
+# Use either bjam or b2 or ../../../b2 (the last should be done on Release branch)
+#os.system("..\\..\\b2.exe")
diff --git a/attic/doc/power_loss_safety_table.qbk b/attic/doc/power_loss_safety_table.qbk
new file mode 100644
index 00000000..ee58033e
--- /dev/null
+++ b/attic/doc/power_loss_safety_table.qbk
@@ -0,0 +1,17 @@
+[table:power_loss_safety Power loss safety matrix: What non-trivially reconstructible data can you lose if power is suddenly lost? [*Any help which can be supplied in filling in the unknowns in this table would be hugely appreciated].
+ [[][[role aligncenter Newly created file content corruptable after close]][[role aligncenter File data content rewrite corruptable after close]][[role aligncenter Cosmic ray bitrot corruptable]][[role aligncenter Can punch holes into physical storage of files[footnote This is where a filing system permits you to deallocate the physical storage of a region of a file, so a file claiming to occupy 8Mb could be reduced to 1Mb of actual storage consumption. This may sound like sparse file support, but transparent compression support also counts as it would reduce a region written with all zeros to nearly zero physical storage]]][[role aligncenter Default max seconds of writes reordered without using `fsync()`]]]
+ [[[role alignright FAT32]][__itick__][__itick__][__itick__][__cross__][[role aligncenter ?]]]
+ [[[role alignright ext2]][__itick__][__itick__][__itick__][__cross__][[role aligncenter 35]]]
+ [
+ [[role alignright ext3/4 `data=writeback`]][__itick__][__itick__][__itick__][[role aligncenter [role green ext4 only]]]
+ [[role aligncenter 35[footnote This is the `commit` mount setting added to the `/proc/sys/vm/dirty_expire_centiseconds` value. Sources: [@https://www.kernel.org/doc/Documentation/filesystems/ext4.txt] and [@http://www.westnet.com/~gsmith/content/linux-pdflush.htm]]]]
+ ]
+ [[[role alignright ext3/4 `data=ordered` (default)]][__icross__][__itick__][__itick__][[role aligncenter [role green ext4 only]]][[role aligncenter 35]]]
+ [[[role alignright UFS + soft updates[footnote Source: [@http://www.freebsd.org/cgi/man.cgi?query=syncer]]]][__icross__][__itick__][__itick__][__tick__[footnote BSD automatically detects extended regions of all bits zero, and eliminates their physical representation on storage.]][[role aligncenter 30]]]
+ [[[role alignright HFS+]][__icross__][__itick__][__itick__][__tick__][[role aligncenter ?]]]
+ [[[role alignright NTFS[footnote Source: [@http://technet.microsoft.com/en-us/library/bb742613.aspx]]]][__icross__][__itick__][__itick__][__tick__][[role aligncenter Until idle or write limit]]]
+ [[[role alignright ext3/4 `data=journal`]][__icross__][__icross__][__itick__][[role aligncenter [role green ext4 only]]][[role aligncenter 5]]]
+ [[[role alignright BTRFS[footnote Source: [@https://wiki.archlinux.org/index.php/Btrfs]]]][__icross__][__icross__][__icross__][__tick__][[role aligncenter 30]]]
+ [[[role alignright ReFS]][__icross__][[role aligncenter [role green not if integrity streams enabled]]][[role aligncenter [role green not if integrity streams enabled]]][__tick__][[role aligncenter Until idle or write limit]]]
+ [[[role alignright ZFS]][__icross__][__icross__][__icross__][__tick__][[role aligncenter 30]]]
+]
diff --git a/attic/doc/quickstart.qbk b/attic/doc/quickstart.qbk
new file mode 100644
index 00000000..ebf9b0bc
--- /dev/null
+++ b/attic/doc/quickstart.qbk
@@ -0,0 +1,1261 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+[section:quickstart Quick Start Tutorial]
+
+So why bother with __boost_afio__? What's wrong with the STL iostreams and Filesystem?
+
+The answer is that there is nothing wrong with either for 95% of use cases. Performance
+of both is pretty good in fact most of the time __dash__ which actually isn't that
+surprising as C++ is a pay-for-what-you-use systems language, and you'll see how well
+STL iostreams does later on in this tutorial.
+
+However a surprising amount of production code out there is highly unreliable when used on a filing
+system experiencing rapid change, or even just a filing system mounted on a network. In many
+ways it is when your code needs to ["grow up] from assuming a never changing static filesystem
+into a more realistic model is when you ought to reach AFIO which has hopefully abstracted away
+all those tedious platform specific and filing system specific quirks for you.
+
+The quick start tutorial is broken into two sections:
+
+# A step by step workshop in building an AFIO program which can concurrently read/write an indexed
+blobstore. This workshop was presented at CppCon 2015 and the slides and materials can be found
+at <TBD LINK>. Issues covered:
+ * Files being renamed, created or deleted by others when you are using them.
+ * Directories being renamed, created or deleted by others when you are using them.
+ * Windows only: how to correctly delete a directory tree on Windows, and why almost every
+implementation of directory tree deletion for Windows is unreliable.
+ * Techniques for avoiding filing system races:
+ * Inode checking
+ * Lock files
+ * Atomic renames
+ * Never use absolute paths
+# Real world sample mini-programs written using AFIO which show various filing system algorithms
+and strategies in action.
+ * Hello world
+ * Concatenating files
+ * Atomicity on the filing system
+ * Races on the filing system
+ * Find regular expression in files in directory tree
+
+[section:workshop Step by step workshop building an AFIO-based key-value store]
+
+[section:naive 1. World's simplest named blob store in STL iostreams]
+
+Let us imagine your application needs to persist some state across executions, and that that state
+is merely a collection of key-value items e.g.
+
+* "dog" => "I am a dog"
+* "cat" => "I am a cat"
+* "horse" => "I am a horse"
+* ...
+
+Let us imagine that you write the following low level public interface for this key-value store:
+
+[workshop_naive_interface]
+
+The macros for the monad and afio namespaces are to enforce a ['hard] version dependency. By aliasing
+into the `BOOST_AFIO_V2_NAMESPACE`, you are creating a specific dependency on v2 of the AFIO ABI. AFIO
+ships some still supported previously used ABI versions of itself in every version, so by pinning yourself
+to v2 you mean v2 and nothing but v2. If you just wanted the current AFIO, simply alias into namespace
+`boost::afio` as with other libraries, this picks up whatever the current configuration and version of
+AFIO is.
+
+The `outcome<>` comes from __boost_outcome__ which is a dependency of __boost_afio__. It has an identical
+API to `std::future<>` or more rather `boost::future<>`, so simply treat it as an always ready future.
+`outcome<>` here can return a shared pointer to the iostream, or empty (item not found), or an error, or
+an exception as you can see in this example use case:
+
+[workshop_use_naive]
+
+A perfectly straightforward and simple way of implementing `data_store` using pure C++ STL is this:
+
+[workshop_naive]
+
+Note that `outcome<T>` implicitly consumes any `T`, `std::error_code`, `std::exception_ptr` or `empty`, hence
+the ability to return any of those directly.
+
+This very simple solution assumes that the key-value store is a directory in the current path called ["store] and
+that each key-value item is simply a file called the same name as the key name.
+
+[table:conditions This solution will perform pretty well and is perfectly fine under these conditions:
+[[][Condition]]
+[[__cross__][On Microsoft Windows you don't place the store deep in a directory hierarchy and you use only short key names.]]
+[[__cross__][No third party thread or process will rename the location of the store during use.]]
+[[__cross__][The size of the values being read and written is small.]]
+[[__cross__][Only one thread or process will ever interact with the key-value store at a time.]]
+[[__cross__][You don't care what happens if your process unexpectedly exits during a modify.]]
+[[__cross__][Maximum performance isn't important to you.]]
+[[__cross__][You don't care what happens under power loss.]]
+[[__cross__][You don't need to update more than one key-value at once.]]
+]
+
+[endsect] [/ naive]
+
+[section:naive_afio 2. World's simplest named blob store in AFIO (synchronous)]
+
+Let's see the same thing as in the last section, but written using AFIO. First, the interface is identical
+to before, just with different private member variables:
+
+[workshop_naive_afio_interface]
+
+We now have a `dispatcher_ptr` and a `handle_ptr` to the store directory which is created/opened during
+construction of `data_store`. Instead of constructing to a filesystem path, we now construct to an AFIO path.
+Note that AFIO paths are always either absolute or relative to some open file handle, and therefore in this
+situation at the point of construction the current working directory will be fetched and an absolute path
+constructed. This differs from filesystem which passes through relative paths and lets the OS resolve from
+the current working directory __dash__ AFIO does it this way as the current working directory setting at some
+later point of asynchronous execution is inherently unpredictable. As the `data_store` interface is identical,
+so is the use case from the previous page. The implementation is rather different however:
+
+[workshop_naive_afio1]
+
+This is a good bit longer and looks more complex, however there is already a big if not obvious gain: third party
+processes can happily rename and move around the store directory once it has been opened and this implementation
+will work perfectly. This is because we open a handle to the store directory in the `data_store` constructor,
+and thereafter use that open handle as a base location for all leaf path operations. Except on OS X which currently
+doesn't support the POSIX race free filesystem extensions, that makes all data store operations invariant to
+store path mutation on all supported platforms.
+
+Another big gain is we are now using memory mapped files for the lookup which avoids any memory copying, and
+there is a hint we are also avoiding memory copies in the write too.
+
+You will also note that in the constructor, we specify a URI to `make_dispatcher()`. This lets you open
+different kinds of filesystem e.g. ZIP archives, HTTP sites etc. AFIO currently doesn't provide backends except
+for `file:///` i.e. the local filesystem, however future versions will. Note that a dispatcher can force on or off
+`file_flags` for all operations scheduled against that dispatcher __dash__ here we force mask out any attempt to
+open anything for writing if we are opening the store read-only.
+
+Finally, you may wonder why we only use `current_dispatcher_guard` once in the constructor. This is because
+if AFIO can deduce the dispatcher to use from any precondition you supply, you need not set the thread local
+dispatcher. As the opening of the store directory is done as an absolute path lookup and therefore takes no
+inputs specifying a dispatcher, you need to set the current dispatcher in that one situation alone.
+
+The remaining magic is in the custom iostreams implementations `idirectstream` and `odirectstream`:
+
+[workshop_naive_afio2]
+
+These are not hugely conforming iostreams implementations in order to keep them brief for the purposes of this
+tutorial. The read stream is very straightforward, we simply have `streambuf` directly use the memory map of
+the file.
+
+The write stream is a bit more complicated: we fill a 4Kb page and ['asynchronously] write it out
+using a page stealing friendly algorithm which in the usual case means the kernel simply takes possession of
+the 4Kb page as-is with no memory copying at all. At some future point it will be flushed onto storage. The
+reason this works is thanks to the special STL allocator `utils::page_allocator<>` which returns whole kernel
+page cache pages. Most kernels will mark whole pages scheduled for write with copy-on-write behaviour such that
+they can be safely DMAed by the kernel DMA engine as any subsequent write will cause a copy, so because we never write to the page between the write
+request and freeing the page, most kernels simply transfer ownership of the page from the user space process
+to the file page cache with no further processing. Hence the asynchronous write of the page tends to complete very
+quickly __dash__ indeed far faster than copying 4Kb of memory and often quicker than the time to fill another page, and hence we wait for any previous write to
+complete before scheduling the next write in order to report any errors which occurred during the write.
+
+This is the first use of asynchronous i/o in this tutorial. AFIO provides a custom `future<T>` type extending
+the lightweight monadic futures framework in __boost_outcome__, so you get all the
+[@http://www.drdobbs.com/parallel/improving-futures-and-callbacks-in-c-to/240004255 C++ 1z Concurrency TS extensions],
+[@http://blogs.msdn.com/b/vcblog/archive/2014/11/12/resumable-functions-in-c.aspx C++ 1z coroutines support] and
+[@http://www.boost.org/doc/html/thread/synchronization.html#thread.synchronization.futures Boost.Thread future extensions] in the AFIO custom `future<>`. There are also many
+additional extensions beyond what Boost.Thread or the Concurrency TS provides, including foreign future composable waits.
+
+[table:conditions This solution will perform reasonably well under these conditions:
+[[][Condition]]
+[[__tick__] [On Microsoft Windows you can place the store deep in a directory hierarchy and use long key names.]]
+[[__tick__] [Third party threads and processes can rename the location of the store during use.]]
+[[__tick__] [The size of ['all] the values being read at any given time fits into your virtual address space (which is at least 2Gb on 32 bit, 8Tb on 64 bit).]]
+[[__cross__][Only one thread or process will ever interact with the key-value store at a time.]]
+[[__cross__][You don't care what happens if your process unexpectedly exits during a modify.]]
+[[__cross__][Maximum performance isn't important to you.]]
+[[__cross__][You don't care what happens under power loss.]]
+[[__cross__][You don't need to update more than one key-value at once.]]
+]
+
+[endsect] [/ naive_afio]
+
+[section:naive_afio_async 3. World's simplest named blob store in AFIO (asynchronous)]
+
+Let's see the same exact thing as in the last section, but this time with a fully asynchronous public interface.
+Instead of returning `outcome<>`, we now return `shared_future<>`:
+
+[workshop_naive_async_afio_interface]
+
+An extension to the Concurrency TS futures is that Boost.Monad futures are also monads, and therefore implicitly
+convert from their allowed monadic input types without you needing to write `make_ready_future(T)` as with the
+Concurrency TS.
+
+You may be interested to know that the benchmarking harness code is 100% identical for all three of these
+implementations. This is because `outcome<>` is sufficiently API-identical to `future<>` that identical code can drive all
+three implementations.
+
+`write()` is now implemented by scheduling the file to be opened for write access and attaching a continuation
+which will propagate any error, and if no error returns via the shared future the shared pointer to the output stream.
+
+[workshop_naive_async_afio1]
+
+`lookup()` takes the chaining of continuations one sequence further. It schedules the file to be opened for reading
+and attaches a continuation which firstly propagates any error, and then if the file is big enough it memory maps
+the file and returns the input stream. If the file is small enough it schedules a read of the entire file, attaching
+a second continuation to return the input stream.
+
+The below pattern is 100% pure soon-to-be-standardised Concurrency TS future continuations which can be easily coroutinised
+automatically on a C++ 1z Coroutines supporting compiler (simply insert `await` before any AFIO `async_XXX` function). Apart from the fact that
+__boost_outcome__ lightweight futures also provide an `error_code` transport, there is nothing below which wouldn't
+work with an upcoming standard C++ library (unless the TS changes substantially, which is very unlikely at this late
+stage).
+
+[note This peer review edition of AFIO v1.40 provides a shim future type standing in for an eventual lightweight future
+implementation. C++ 1z coroutine support is not implemented for the shim future type.]
+
+[workshop_naive_async_afio2]
+
+The input and output stream implementations are pretty similar to before, but here they are for reference:
+
+[workshop_naive_async_afio3]
+
+
+[section:naive_racy The problems with this naive design]
+
+You might be curious as to the performance of this naive key-value blob database. I was, so I performed the
+following benchmarks on Win8.1 NTFS and Linux ext4 both on SSD using the code above. Note that the test
+uses OpenMP to parallelise the operations, so the below shows AFIO in a worst case light as will be explained
+shortly:
+
+[role aligncenter [$../../../../../libs/afio/doc/src/images/workshop_naive_insertions.png] [$../../../../../libs/afio/doc/src/images/workshop_naive_lookups.png]]
+
+I don't know about you, but I was more than quite surprised at how good the performance is considering its
+naive simplicity. As a reference comparison, with durability disabled these NoSQL key-value databases achieve
+the following performance ([@http://www.datastax.com/wp-content/themes/datastax-2014-08/files/NoSQL_Benchmarks_EndPoint.pdf source]):
+
+[table:nosql Performance for various NoSQL databases with fsync disabled on Linux ext4:
+[[][[*Our naive design]][Cassandra][Couchbase][HBase][MongoDB]]
+[[Insertion][[*25,576]][53,067][40,063][38,991][18,038]]
+[[Lookup][[*64,495]][38,235][14,503][3,955][2,752]]
+]
+
+The naive results were calculated by excluding the highest value and averaging the next three highest values.
+Now this is not a fair comparison by any means __dash__ the NoSQL databases are running over a network stack
+(which particularly penalises read performance) whereas ours is direct to the OS API more or less,
+and our naive benchmark only writes 10 bytes of value per item which is small enough to pack into the inode
+on both NTFS and ext4, so the extent allocator is never getting invoked. Still, one is used to thinking of
+the filesystem as incredibly slow and to be avoided with fancy database software __dash__ this simple test
+shows that ['recent] filesystems are competitive to fancy databases (and indeed even more so with a more
+sophisticated design than the one we used, as you will see later).
+
+[note NTFS has such poor insertion performance because Microsoft Windows performs a sequentially consistent flush of the
+containing directory on file handle close, whereas Linux doesn't even flush the containing directory after
+you fsync a file and you must explicitly fsync the directory itself. You may find the
+`afio::file_flags::temporary_file` flag of use as this hints to Windows that the file is not important,
+and it may encourage Windows to not be so careful with sequentially flushing directory changes every single written
+file close.]
+
+As is fairly obvious from the results, AFIO lags far behind iostreams at about one half to one third the performance.
+We are using AFIO's synchronous API for the most part, and that is simply a `get()` on the asynchronous API which
+introduces an unnecessary thread sleep (the fully asynchronous edition is about 0-5% faster). Much more importantly,
+because no platform provides asynchronous file open nor close, AFIO emulates asynchronicity by
+executing the file opens and closes in a threadpool which is exactly what the iostreams does via OpenMP, however there
+isn't a copy of ASIO and AFIO and all the futures and completion handlers standing in between, so the iostreams
+implementation is far faster and will [*always] be faster than AFIO if you do a lot of file opens and closes which
+is pretty much this benchmark in a nutshell.
+
+[note This peer review edition of AFIO v1.40 uses a mocked up v1.40 API based on the v1.3 series engine. Performance
+of this mockup will be considerably below the final v1.40 engine written using __boost_outcome__'s lightweight futures.]
+
+A design which makes better use of AFIO is one which is asynchronous throughout and which avoids file opens and closes
+(['tip:] always avoid file opens period if you want fast filesystem performance, they are always very slow due
+to the security checking of every single item in the path. File closes are also particularly slow on Microsoft Windows
+and Apple OS X).
+
+Some might think that the flat store of all the keys in a single directory would scale poorly to item count.
+This certainly ['used] to be true for the previous generation of filing system, however modern filing
+systems usually have between `O(log N)` to `O(1)` lookup complexity and between `O(N)` and `O(1)` insertion
+complexity __dash__ this author found excellent lookup performance with 10M items on ext4 and NTFS,
+though insertion performance was not great for NTFS (which does a directory flush each file close).
+
+[*Problem 1 with naive design:] We are doing too many file opens and closes for really good performance.
+
+[*Problem 2 with naive design:] ['Atomicity]: Writes do not atomically appear fully completed to other readers, so if one thread/process reads a value
+currently being written by another thread/process, they will see a partially written value which is a filesystem race.
+
+[*Problem 3 with naive design:] ['Consistency]: If a writer process fatal exits during a write and therefore does not complete a write, the key value store
+is corrupted.
+
+[*Problem 4 with naive design:] ['Isolation]: There is no concurrency ordering control apart from that provided by the operating system. AFIO exposes
+the strong guarantees made by operating systems as to the atomicity of read and write visibility to other readers and writers (see each
+API reference documentation page for the guarantees per API per platform), however the STL iostreams wrapper
+around AFIO ruins those guarantees (this is why AFIO does not provide iostreams wrappers).
+
+[*Problem 5 with naive design:] ['Durability]: If power loss occurs during use of the key-value store, you are at the mercy of random chance as to
+what data in what order reached physical storage. On many filing systems you aren't even guaranteed
+that extent metadata matches contents, so your value may consist of portions of valid data intermixed with
+garbage.
+
+[*Problem 6 with naive design:] Insertion and update complexity may be as poor as `O(N)` on some filesystems.
+
+[*Problem 7 with naive design:] There is no way of atomically updating two or more key-value items in a transaction.
+
+Luckily, AFIO exposes exactly the right features to make five of those seven problems go away with very few changes
+to the above code.
+
+[endsect] [/naive_racy]
+
+[endsect] [/naive_afio_async]
+
+[section:atomic_updates 4. Second attempt at a key-value store: How to implement atomic value updates and therefore Atomicity, Consistency, Isolation and Durability]
+
+The naive key-value store simply stored each key in a single flat directory, so:
+
+* "dog" => `store/dog`
+* "cat" => `store/cat`
+* "horse" => `store/horse`
+* ...
+
+When inserting or updating an item, it simply opened the filename and wrote out the contents
+thus creating races for concurrent users and potentially writing out the entire directory inode
+if that filesystem rebalances its directory contents. So how do we implement ACID-ity of updates
+with constant time updates?
+
+It is actually surprisingly simple thanks to AFIO. Firstly, we are going to reorganise the
+store's structure with a directory per key name to become this instead:
+
+* "dog" => `store/dog/0`
+* "cat" => `store/cat/0`
+* "horse" => `store/horse/0`
+* ...
+
+We are going to change the lookup semantics to this instead:
+
+# For some key, open the file `store/key/0` for reading.
+
+The insertion semantics becomes this:
+
+# For some key, open the file `store/key/tmpXXXXXXXXXXXXXXXX` for writing ['optionally] using `afio::file_flags::always_sync`
+(this causes the OS to not report write completion until the write reaches physical storage),
+creating any `store/key` directory as necessary, where `XXXXXXXXXXXXXXXX` is a cryptographically strong
+128-bit random number.
+
+# Write the new value using the output stream as normal, keeping the handle open.
+
+# Atomically rename `store/key/tmpXXXXXXXXXXXXXXXX` to `store/key/0` using AFIO's special `atomic_relink()`
+function (which because it acts on an open file handle, is invariant to path relocations of itself).
+This function does as it says, and atomically makes visible at `store/key/0` our new value for
+the key, unlinking any file previously there[footnote This operation is not possible on Microsoft
+Windows using the Win32 API __dash__ nowhere in the Win32 API is this operation made available.
+However the NT kernel API does provide this operation, hence AFIO can provide POSIX atomic relink
+guarantees on Windows.]. [*Never at any point is there not a complete and finished
+value file on physical storage at `store/key/0`][footnote This may not be true on older NFS mounts,
+or NFS mounts with the wrong configuration. See the NFS documentation. It is also possible to
+configure Samba in a way which breaks the atomicity of atomic renaming, see the Samba documentation.].
+On Linux only we then execute a sync on the directory handle for `store/key` because Linux is
+different from other operating systems in that you must explicitly request metadata updates to be sent
+to physical storage.
+
+Not that we'll be implementing this here, but the deletion semantics would be:
+
+# Atomically rename `store/key` to `store/deadXXXXXXXXXXXXXXXX` where the latter part is another 128-bit
+crypto strong random number. Note that Microsoft Windows does not permit you to rename a directory
+containing an open file, so you may need to move all the contents of the directory into a newly created
+`deadXXXXXXXXXXXXXXXX` directory instead, being careful of races caused by someone setting a new key-value
+into the directory you are trying to delete.
+
+# Recursively delete the dead directory. On Microsoft Windows, AFIO tags the items for later deletion when
+the last handle to them is closed. Remember that someone doing a lookup may still have a handle open to
+the just deleted version of the handle, but on both POSIX and Windows this is fine __dash__ the physical
+storage will be deallocated on last handle close, even (usually) under unexpected power loss.
+
+What these new semantics achieve:
+
+# [*Atomicity]: Values always appear to concurrent readers complete and finished.
+# [*Consistency]: If a writer fatal exits during a key update, the store is left in a consistent state.
+# [*Isolation]: The operating system provides strong ordering guarantees about atomic relinking,
+thus creating a sequentially consistent ordering of key-value update visibility.
+# [*(Durability)]: If power loss suddenly occurs, the key-value store will [*always] be internally
+consistent, even if `afio::file_flags::always_sync` was not specified (see below for why).
+It may not have all the key-values in an identical order to how they were written as
+this second generation design does not implement transactions, but there will always be a complete
+value for a key which at some point was stored to that key. Strictly speaking, this means that
+durability is not fulfilled __dash__ durability means when you are told a write completes, it is
+durably stored, however for full durability simply add `afio::file_flags::always_sync`.
+# [*Much improved update complexity]: New key insertion may still be as poor as `O(N)` complexity, but
+updates of existing keys is now no worse than lookup complexity, which is a big improvement over
+the naive design. If your filing system has `O(N)` insert complexity, you could use prefix directories to
+form a binary tree, thus reducing insert complexity to `O(log N)`.
+
+Many readers will be wondering why `afio::file_flags::always_sync` is not necessary for consistency
+even under power loss, and the answer is because of journalled filing systems combined with how we are
+modifying data. If you examine [link afio.design.acid_write_ordering.write_ordering_data.power_loss_safety the table of power loss
+safety guarantees in the design rationale], you will note that journalled file systems (with write barriers
+enabled) make strong guarantees about newly created file content but not about rewritten file content:
+if a newly created file appears in the filing system, it is guaranteed to have correct contents because
+['journalled filing systems do not write an updated directory pointing to the new file until the file
+contents are on physical storage]. Our naive design of creating a brand new file per key-value update
+exploits this guarantee, so the sequentially consistent ordering of writes to physical storage, even
+without `afio::file_flags::always_sync`, is this:
+
+# Allocate extents for new file content. Write journal.
+# Write new file content to physical storage. Write journal.
+# Write new copy of directory containing atomic rename of randomly named file with deallocation
+of extents of previous file. Write journal.
+
+If power is lost at any point, during journal replay on power restore a journalled filing system will
+throw away any extents allocated for content not referenced by a directory on physical storage. The
+filing system on power restore therefore refers to a previously consistent filing system i.e. how it
+was just before power was lost. This is why the store will be consistent even under power loss[footnote
+This is as yet untested by empirical testing, however AFIO does no magic tricks, it just thinly wraps
+the operating system APIs.], though without `afio::file_flags::always_sync` you may lose up to 5-35
+seconds of updates and there may be reordering of updates by up to 5-35 seconds. Here is the table
+from the design rationale once again, for your convenience:
+
+[include power_loss_safety_table.qbk]
+
+It goes, of course, without saying that if you are [*not] on a journalled filing system then you absolutely
+need `afio::file_flags::always_sync` and on Linux you had best sync the directories containing newly written
+files too.
+
+So let's look at the code: you will be surprised at how few changes there are compared to the earlier
+asynchronous AFIO implementation. Firstly, the interface is unchanged from before:
+
+[workshop_atomic_updates_afio_interface]
+
+`lookup()` is almost identical to before, now it simply opens `key/0` instead.
+
+[workshop_atomic_updates_afio1]
+
+`write()` is a bit different. We now open the key directory, and while that is happening asynchronously
+we generate a crypto strong random name which tends to be slow. We then schedule creating that temporary
+file with the extra flag `afio::file_flags::hold_parent_open` (which incidentally can dramatically increase AFIO
+filesystem performance because a number of fast code paths can be taken e.g. fetching most metadata
+about a file on Windows will actually do a single glob directory enumeration on its parent directory when available
+which is about 20x faster. The reason it is not enabled by default is because of process file descriptor limits
+on POSIX, especially the severely low default limit on Apple OS X). In this particular case, we just want
+our temporary file to retain knowledge of its parent because we will fetch that parent later.
+
+[workshop_atomic_updates_afio2]
+
+Finally the input and output streams. The input stream is unchanged, whilst the output stream
+destructor now simply atomically renames the temp file to "0" using the parent directory handle
+as the base for the rename target in order to ensure race freedom.
+
+[workshop_atomic_updates_afio3]
+
+Obviously enough we don't have any garbage collection in here for failed or aborted writes. Thanks
+to the unique naming of those files, these are very easy to spot during a GC pass and if they had a
+last modified date several days ago they would ideal for purging. Implementing one of those is easy
+using __afio_enumerate__, and is left as an exercise for the reader.
+
+[table:conditions This second generation solution will perform reasonably well under these conditions:
+[[][Condition]]
+[[__tick__] [On Microsoft Windows you can place the store deep in a directory hierarchy and use long key names.]]
+[[__tick__] [Third party threads and processes can rename the location of the store during use.]]
+[[__tick__] [The size of ['all] the values being read at any given time fits into your virtual address space (which is at least 2Gb on 32 bit, 8Tb on 64 bit).]]
+[[__tick__] [As many processes and threads may read and write to the store concurrently as you like,
+including any mix of CIFS and NFS clients.]]
+[[__tick__] [Processes may unexpectedly exit during modifies with no consequence on consistency.]]
+[[__cross__][Maximum performance isn't important to you.]]
+[[__tick__] [Sudden power loss may not maintain original write ordering across multiple key-value
+updates, however each key will have some complete value which it had at some point in history.]]
+[[__cross__][You don't need to update more than one key-value at once.]]
+]
+
+[section:atomic_updates_problems The performance of and problems with this second generation design]
+
+Let's see how this second generation ACI(D) design benchmarks compared to the first AFIO implementation
+where the dotted lines are the previous generation design:
+
+[role aligncenter [$../../../../../libs/afio/doc/src/images/workshop_atomic_updates_insertions.png] [$../../../../../libs/afio/doc/src/images/workshop_atomic_updates_lookups.png]]
+
+Lookups are actually not better __dash__ we are comparing the synchronous naive design to an asynchronous
+design, so the asynchronous design pulls ahead a bit on Microsoft Windows. Note, though, that the extra
+directory indirection costs you a noticeable amount of performance on ext4 __dash__ perhaps as much as 15%
+once you include the 10% bump from the asynchronous API.
+
+Insertions are however diametrically slower: about four times slower on both NTFS and ext4. This is
+because atomic renames appear to not parallelise well __dash__ both filing systems are gating hard at about
+four parallel atomic renames (the number of cores on my test CPU), and if you throttle the parallelism
+by hand you'll find performance is really only half that of the first generation design which is logical
+as we are committing twice the new items to before. This is a good example of where Intel's Hyperthreading
+can really penalise overall performance sometimes: filing systems tend to parallelise linearly to real
+CPU core count only.
+
+This second generation design is actually quite solid, and assuming that the design rationale is correct
+about the sequential consistency of journalling file systems then this key-value store design is likely production code
+useful, especially if you are storing large blobs which you might like to individually memory map. What you don't have
+though is the ability to update key-values as a transaction, and insertion performance is already heading
+downwards towards less than 1000 items/second which bodes poorly for adding transaction support.
+
+Out of curiosity, how might we add transaction support to this design? One's first thought is a lock file
+of some sort, perhaps even a lock file for the entire store. AFIO v1.3 can push about 3000 exclusive lock files
+per second (see the mini-programs on atomicity following this section) so you might imagine holding a global lock
+file during reads and during a sequence of multiple atomic renames which make up committing a transaction.
+
+Unfortunately, while that strategy would work well most of the time, it falls down spectacularly in the
+situation of power loss. The problem you have is that you have no way at all of forcing the order of
+key-value updates to reach physical storage. You might think that calling `fsync()` or using `O_SYNC`
+and then writing each item sequentially might work, but imagine if power is lost in the middle of
+that sequence: how do you know if the store you are now looking at was in the middle of a transaction,
+and if so what needs to be rolled back?
+
+If you think this through, you realise that you need some form of write log from which an interrupted
+sequence of operations can be reconstituted and either completed or discarded to return a store to
+a consistent state. This write log is called a journal by journalling file systems, and is called
+various things by databases ([@https://www.sqlite.org/wal.html write ahead logging, rollback journal etc]).
+It has the special property that it is always moving forwards in time and what it records is the
+sequentially consistent history of the database.
+
+You have probably guessed what the final part of this workshop tutorial does: and indeed it is
+implementing a transactional write log.
+
+[endsect] [/atomic_updates_problems]
+
+[endsect] [/atomic_updates]
+
+
+[section:extents_theory 5. Third attempt at a key-value store: Thinking like a filing system]
+
+You should prepare yourself to now think outside your normal comfort zone, and like a filing system engineer.
+
+Modern filing systems are ['extents] based, which means that instead of your file content being stored
+as the program sees it when reading it, physical storage allocation is [*lazy]. In other words, only when
+you write first into an extent is physical storage actually allocated, and if you never write into an
+extent then no physical storage is consumed, despite that that region of the file reads as all bits zero.
+
+You can test this for yourself by the following shell command on an extents-based filing system (e.g.
+ZFS, UFS, HFS+, ext4):
+
+```
+ned@kate:~$ truncate -s 2T sparse.img
+ned@kate:~$ ls -ls sparse.img
+0 -rw-rw-r-- 1 ned ned 2199023255552 Aug 19 16:13 sparse.img
+```
+
+The first zero shows the allocated size of the file, which is zero. The maximum allocation of the file,
+which is reported as the size, is 2Tb. Let's try writing a single byte onto the end of the file:
+
+```
+ned@kate:~$ echo '0' >> sparse.img
+ned@kate:~$ ls -ls sparse.img
+4 -rw-rw-r-- 1 ned ned 2199023255554 Aug 19 16:17 sparse.img
+```
+
+The maximum allocation has increased by two bytes as you would expect. However, the actual real
+allocation is now in fact four bytes! If you were not on an extents-based filing system the
+echo append command would take a very long time to execute and you would in fact get a 2Tb
+sized file almost entirely made of zeroes!
+
+Different filing systems have different granularities of extent. ext4, for example, usually
+has a 4Kb extent granularity unless it is a partial final extent. ZFS usually has a 128Kb
+granularity, while NTFS usually has a 64Kb granularity. The key thing to remember here is
+that the granularity is usually quite chunky, so storing a single byte every 256Kb offset
+is going to be extremely wasteful of physical storage. AFIO provides enumeration of the
+allocated extents of a file using __afio_extents__.
+
+Just as you can lazily allocate physical storage for file content using extents, so too
+can you deallocate extents by ["punching holes] in a file's content storage. This simply
+eliminates the physical storage for a region, and therefore subsequent reads from that
+region will now return all bits zero. AFIO's API for doing this deallocation is
+__afio_zero__, and just to remind you of which filing systems provide extents and APIs
+to manage them, here is that power loss safety table once again:
+
+[include power_loss_safety_table.qbk]
+
+[heading Visibility of concurrent writes to concurrent readers in file i/o]
+
+The final piece of the puzzle is [*atomicity of writes]. Many people do not realise that POSIX operating
+systems are supposed to provide some strong POSIX-mandated guarantees about the visibility of file i/o to concurrent
+readers and writers, and these guarantees AFIO goes out of its way not to interfere with. These are:
+
+# The ['visibility] of a write to a file is to be atomic to all readers of the same file, including
+gather writes (note gather writes on Microsoft Windows are not atomic, but single buffer writes may be,
+see below). The POSIX-2008 wording is this:
+
+ [:['["If a `read()` of file data can be proven (by any means) to occur after a `write()` of the data, it
+must reflect that `write()`, even if the calls are made by different processes. A similar requirement
+applies to multiple write operations to the same file position. This is needed to guarantee the
+propagation of data from `write()` calls to subsequent `read()` calls.]] ([@http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html POSIX-2008])]
+
+ If you try to implement this wording, you quickly realise there is an implied mutex on each file in the
+kernel, and every read to and write from that file across the system involves holding that mutex during
+the read or write. Whether that is actually the case is unimportant, POSIX requires that it must appear
+to file i/o readers and writers that this is as if so.
+
+ In actual implementations however, the reality is somewhat different. The first obvious difficulty
+is with networked filing systems where performance would be awful if an implied mutex had to be claimed
+for every single read and write, so networked filing systems don't do that. The second big deviation
+is with memory mapped files as reading and writing raw memory does not synchronise on
+the implied per-file mutex and therefore you may see a partially written write during a read. All the
+major operating systems do just happen to synchronise i/o between memory maps and normal
+read/write so long as nobody is using `afio::file_flags::os_direct` i.e. `msync(MS_INVALIDATE)`
+is a noop on any recent major operating system, however memory map i/o still races on itself and
+on normal i/o operations. This, incidentally, is why AFIO does not provide much memory map support currently.
+
+ An additional problem beyond these is that Linux is simply non-conforming to POSIX-2008 here,
+and has unfortunately been so for a very long time. Linux does lock on a per-page granularity,
+so per 4Kb page it locks and nothing past that. This implies that if your read unfortunately
+straddles a 4Kb boundary, you have a race. XFS has implemented additional locking to solve this
+problem, but ext4 has not. You can see more detail
+[@http://jeffr-tech.livejournal.com/20707.html here] and [@http://permalink.gmane.org/gmane.comp.file-systems.ext4/27225 here].
+
+ Finally, Microsoft Windows makes no public guarantees about the atomic visibility of writes except
+that nothing is guaranteed past a sector size, and by that they are referring to the atomicity
+of a write reaching storage rather than anything about visibility to concurrent reader writers.
+From how the NT kernel IRP queue works whereby reads and writes both enter the same serialised queue,
+it seems to me that the POSIX visibility guarantees are met which would be expected as the NT kernel
+is explicitly designed to be POSIX compliant, however one should be cautious here. I note from a
+review of portable databases that none makes strong assumptions about read-write visibility ordering, and
+I assume that is for a reason __dash__ though that reason could simply be ["Linux].
+
+ [*If] you are willing to restrict your end use cases to:
+
+ * Any of the BSDs (these conform to POSIX).
+ * Linux with XFS (XFS implements special workarounds to conform with POSIX).
+ * Microsoft Windows with NTFS (it is thought to conform to POSIX).
+
+ ... then you may be able to dispense with explicit locks. You may find the __afio_statfs__ API useful
+as it can return the name of the filing system providing an open file handle.
+
+# By extension of the above, POSIX requires that the visibility of a write to a file opened for append-only access is also
+atomic to all readers and writers of the same file. Or put more succinctly, appending to
+an append-only file is atomic per syscall, so concurrent appenders are guaranteed each of
+their writes will appear to be appended in full before another appender's write. Here is
+the POSIX-2008 wording:
+
+ [:['["If the `O_APPEND` flag of the file status flags is set, the file offset shall be set to the end
+of the file prior to each write and no intervening file modification operation shall occur between
+changing the file offset and the write operation.]] ([@http://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html POSIX-2008])]
+
+ You will be glad to know that this is a much better supported feature in implementations.
+The atomicity of file append even works on CIFS/Samba network shares in the usual configuration, albeit
+with a severe performance penalty (NFS network shares unfortunately do not preserve
+append atomicity because the protocol is incapable of expressing such an operation). Because
+memory maps are for a given file extent, file appends don't affect them, and because the requirement
+is merely for the atomicity of the increment of the file size, the atomicity of the visibility of the content
+appended is a separate problem to ['where] the content is appended whose increment is guaranteed
+to be atomic except on NFS.
+
+[heading The relevance of all this filing system theory to the final key-value BLOB store]
+
+Why all this matters is because you need to understand all this theory to understand why the third generation
+key-value BLOB store presented next is designed exactly the way it is. Unintuitively, it is going to
+be a set of always-growing atomically-appended sparse files. We take advantage of hole punching
+to deallocate the parts of the files no longer needed so physically consumed storage does not grow
+past what is needed, but their apparent sizes grow forever and the atomicity of the atomic append operation
+is used as the concurrency control mechanism for implementing transactional updates as effectively copy-on-write, plus as the mechanism
+for marking time to the filing system such that extents which have exceeded their maximum age and must be
+flushed to storage are always exactly those at an offset earlier in the file. Because we never open
+nor close files, we avoid the costs of security checking during path lookup, and therefore maximise performance.
+While the next example does not make use of memory maps as that would severely complicate the code, the
+on-disk file format is capable of being used directly from a memory map which would allow high performance
+scaling to hundreds of millions of key-value pairs without blinking. The only real pessimism in the
+following design is that it doesn't scale well to modifies with worse than
+`O(N^2)` complexity to concurrency of writers and worse than `O(N)` complexity to key count.
+
+We do have to take some care however. As on Linux writes are not atomic with respect to reads across
+4Kb boundaries, we are going to need some mechanism of checking if some given append is valid or not, which usefully
+can be reused to check a store for validity after a sudden power loss (just because reads and
+writes may have sequential consistency of visibility to processes has no bearing whatsoever on what order writes are flushed
+to physical storage). Any writes we make within a 4Kb boundary is probably safe e.g. to the very front
+of the store file, so long as it does not exceed 4Kb.
+
+And just to be extra special careful, we will never do a ['rewrite] of existing content exceeding
+a sector size which we'll assume is 512 bytes on a 512 byte boundary as that is publicly documented
+as guaranteed for all the major operating systems. For that reason, as you will note in the implementation
+next section, everything is aligned to a 32 byte boundary and we never rewrite more than 32 bytes which
+guarantees we will never rewrite across a 512 byte boundary, and therefore risk atomicity.
+
+[section:extents The final named blob store design]
+
+[caution This section was not finished in time for the beginning of the Boost peer review due a hard
+drive failure induced by the testing of AFIO-based key-value stores in this workshop tutorial (sigh!), but it will be finished
+shortly after I get up and running again on a new hard drive (as I write this, the drive is being cloned in between
+periods it hangs and I have to power cycle to get it back for more cloning). The page content is representative of the
+final edition, however the source code listed has not been debugged nor tuned for performance yet
+and is therefore not listed here. The benchmarks are obviously missing for this solution on the next page.]
+
+As you might have guessed from the theory on the preceding page, the final design of the key-value store is radically
+different to before. Instead of storing each value in its own file, we have this store structure instead:
+
+* `store/index`
+
+ This always growing file is a simple linear history of snapshot state of the key-value store with a full copy of
+all the key-value pairs being written per transaction as a single shot atomic append. The blobs are referred to by
+unique integer index into a linked small blob index (see below). The key-value pairs are stored on-disk as a dense hash
+map which is very friendly to memory maps, eight bytes per entry, with the key strings stored contiguously after
+the hash array.
+
+ Each index record appended to the index is content hashed, and if during later parsing the hash does not match
+the contents the record is ignored. If after power loss the end of the index is trashed, the solution
+below will replay the history of transactions from the beginning, skipping any deallocated extents, until it finds the most recent complete
+undamaged index which does not refer to damaged blobs (see below).
+
+* `store/large_blob_store`
+* `store/large_blob_store.ecc`
+
+ We don't implement the large blob store in the solution below to keep the code brief, however it is intended
+to house any blob sized 4Kb or above as memory maps are a much superior way of working with larger values
+and keeping large blobs separately makes extent fragmentation from hole punching much less expensive. Unlike the other
+files, the large blob store is not necessarily always atomic appended, a deallocated extent might be reallocated
+for example. This eliminates the problem of ever exceeding a `2^64` maximum file size.
+
+ Each large blob is [*always] aligned to a 4Kb boundary to allow memory mapping. The large blob store is quite
+stupid and contains no metadata at all __dash__ it relies on the small blob store for all metadata. This
+stupidity, and the fact it is a separate file, allows you to speculatively allocate new
+blob value storage before a new value is written such that one writes directly into the final storage from
+the beginning with no unneeded memory copying. One then either commits the new value or throws it away by
+punching a hole over it. You will notice below that each blob keeps an age from a monotonically increase count
+based on updates, this is used to keep a blob pinned into existence for a period even if it is not in use.
+
+ In short, the large blob file provides a large number of useful optimisations, but is not strictly speaking
+necessary. You could just use the small blob file for all blobs, and that is what this simplified implementation
+does for brevity.
+
+ You are probably curious about the ECC file. The ECC file is a SECDED 32784,32768 Hamming code, so 16 bits (2 bytes)
+of ECC per 4096 bytes. This file allows single bit flips to be healed which can save a blob from being
+invalidated due to failing its hash check. We don't bother using this for either the index or the small
+blob store as almost all bit flips seen in a computer system stem from non-ECC RAM rather than long term
+storage, and the overhead of ECC calculation is probably not worth it except for large BLOBs given the
+statistical chance of a bit flip. AFIO provides a very fast SECDED implementation in `afio::utils::secded_ecc<>`.
+
+* `store/small_blob_store`
+
+ The small blob store is where all the blob values live. To add a new value, one simply constructs
+a blob value record and atomically appends it in a single shot. Each blob has a hash of its content, and
+if the hash does not match its contents it is considered invalid and pending deallocation via hole punching.
+
+The operations involved in opening a data store for use become these:
+
+# Create/open the store directory.
+# Create/open for atomic append only the `index` and `small_blob_store` files.
+# Open for read write the `index` and `small_blob_store` files into a second open handle.
+# If either the `index` or `small_blob_store` files have zero length, atomic append into each their 32 byte header.
+This is racy, but safe as spurious additional 32 byte headers are ignored by the parsing code.
+# If either the `index` or `small_blob_store` files specify a last known length which is greater than the size
+reported by the operating system, or the last good index specified by the header points to a corrupted
+index, we consider either or both to be dirty. We firstly replay the
+small blob store from the beginning, skipping any deallocated extents, and we atomic append a blob store index (a map of content hashes to
+offsets in either the small or large blob store), updating the header to point at the new blob store index.
+
+ We then replay the index from the beginning, skipping any deallocated extents, matching off valid index
+records against our most recently generated blob store index. We choose the last uncorrupted index
+which does not refer to hashes of unavailable content.
+
+If two processes concurrently open the same dirty data store and both repair the store, you will note
+that the above healing process is invariant to concurrent heals. Again, the parsing code correctly skips
+spurious atomic writes.
+
+The operations involved for reading a key-value:
+
+# We reload the index and blob store index loaded into memory if it has changed (by simply checking
+if the file size for either has grown).
+# We look up the blob index mapped by the key.
+# We look up the blob store file linked to by the index and get the offset and file for the content.
+# We read the value to be delivered using the input stream, similar to the second generation key-value store.
+
+The operations involved for writing a key-value:
+
+# We hand out an output stream which records all output to memory.
+# On the destruction of the output stream, we build the gather write buffers for writing a blob with this value
+(i.e. hashing the contents etc).
+# We refresh the blob store index and see if this blob is already stored with an age less than 50. If so, we
+update its age to 0 along with all other blobs referred to by the index. If not, we atomic append the new blob
+and loop trying to atomic append an updated canonical blob store index until success (we must loop as we must
+reconcile with other concurrent writers). On success, we update the header at the front of the small blob store
+file to point at the new canonical blob store index with an incremented time count (this being a monotonically
+increasing count of successful blob store updates).
+# We refresh the index and update or add the key mapping to the map of keys to blob store index item, atomically
+appending the new index. If someone else wrote a new index between our starting index and our just written index,
+we return a transaction conflict error. On success, we update the header at the front of the
+index file to point at the new canonical index.
+# Every few thousand new indices we punch a hole earlier in index history by calling __afio_zero__. We always leave
+around a few thousand indices for replay after sudden power loss.
+
+It may now seem that there is a big race condition in between atomic appends of a new canonical index for either
+store and the updating of the header at the front of the file. This is correct, but is solved by the following
+index parsing algorithm for both store files:
+
+# Read the header at the front of the file for the hint to the current canonical index.
+# Parse each record from the hint onwards until the end of the file.
+
+In other words, a racily written hint adds a small bit of work to the parser, but nothing important. All we care
+is that it is approximately correct.
+
+We don't implement item deletion as we didn't in either of the previous two generation key-value stores, however if one were
+to implement it it would have these operations:
+
+# For all blobs in the blob store index with an age greater than 100 (this implies that writer concurrency
+must never exceed 50), and where their contiguous storage exceeds 4Kb, we remove those entries from the blob
+store index and write a new canonical blob store index.
+# If entries were removed, we calculate a set of extents to be deallocated and fire and forget a call
+to batch __afio_zero__. The extents to be deallocated are calculated by walking the small blob index
+and keeping a record of extents not referred to (i.e. the gaps between blobs stored). For each of those containing
+an aligned 4Kb quantity or more we rewrite the record header to have any parser skip the deallocated extent, and
+deallocate the rest.
+
+So let's look at our new interface file. It looks pretty similar to before, the only real change is the many new
+private member variable `handle_ptr`'s to the store files described above. Another change is that we now return
+a `outcome<ostream>` for writing but a `shared_future<istream>` for reading __dash__ this is because as explained
+earlier, writes are now fully buffered to memory before being hashed and committed as a transaction, so by explicitly
+returning a monad we are saying that this is now a synchronous operation (one could just return an already ready
+shared future, but this method makes public API assurances, and because a __boost_outcome__ future is a monad,
+your code will likely not even notice).
+
+[workshop_final_interface]
+
+More code is to come ...
+
+[table:conditions This third generation solution will perform excellently under these conditions:
+[[][Condition]]
+[[__tick__] [On Microsoft Windows you can place the store deep in a directory hierarchy and use long key names.]]
+[[__tick__] [Third party threads and processes can rename the location of the store during use.]]
+[[__tick__] [The size of ['all] the values being read at any given time fits into your virtual address space (which is at least 2Gb on 32 bit, 8Tb on 64 bit).]]
+[[__tick__] [As many processes and threads may read and write to the store concurrently as you like,
+including CIFS clients but excluding NFS clients.]]
+[[__tick__] [Processes may unexpectedly exit during modifies with no consequence on consistency.]]
+[[__tick__ __tick__] [Lookup performance is breathtakingly swift and close to invariant to item count.
+Write performance is much slower, but a lot faster than the atomic rename based design.]]
+[[__tick__ __tick__] [Sudden power loss will restore the key-value store to some point in history
+preserving the exact ordering of updates.]]
+[[__tick__] [This design allows the atomic transactional update of as many key-value item at once as you like.]]
+]
+
+
+[endsect] [/extents]
+
+[section:extents_problems The performance of and problems with this third generation design]
+
+[caution This section was not completed in time for the beginning of the Boost peer review, but
+it will be finished shortly.]
+
+[endsect] [/extents_problems]
+
+[endsect] [/extents_theory]
+
+[endsect] [/ workshop]
+
+[section:mini_programs Example mini-programs written using AFIO]
+
+[section:hello_world Hello World, asynchronously!]
+
+'''<?dbhtml-include href="disqus_identifiers/hello_world.html"?>'''
+
+We might as well jump straight in: on the left is a traditional STL iostreams
+implementation with its equivalent in AFIO free functions on the right.
+
+[table:hello_world Traditional STL iostreams and AFIO side by side
+ [[[readwrite_example_traditional]][[readwrite_example]]]
+]
+
+AFIO is based on future continuations as will be in the standard C++ library from C++ 1z onwards.
+Each continuation enforces an order of execution between the asynchronous operations.
+
+What this means is straightforward: in the example above `async_file()` outputs on completion
+an open file handle which is fed to `async_truncate()` which on completion feeds the same input
+handle to `async_write()` and so on. `async_close()` takes in the open file handle, closes it, and
+outputs a null file handle on completion to `async_rmfile()` which can still retrieve its last known
+path before being closed.
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[endsect] [/hello_world]
+
+[section:file_concat A less toy example: Concatenating files]
+
+'''<?dbhtml-include href="disqus_identifiers/file_concat.html"?>'''
+
+The Hello World example didn't really demonstrate why using AFIO is any better than using
+STL iostreams. The final example in this quick start will give a ["real world] and unfortunately
+somewhat overwhelming in complexity example of how
+AFIO can run rings around STL iostreams (complete with benchmarks!), so as an intermediate
+__dash__ and hopefully not as overwhelming __dash__
+step here is a simple file copy utility which can also concatenate multiple source files
+into a destination file[footnote My thanks to Artur Laksberg, Dev Lead of the Microsoft
+Visual C++ Parallel Libraries team, for suggesting this as a good demonstration of an
+asynchronous file i/o library. I had been until then quite stumped for an intermediate
+quick start example between the first and last examples.].
+
+[filecopy_example]
+
+This example consists of a function called `async_concatenate_files()` which will
+asynchronously append those file paths in ['sources] into the file path in ['dest], with
+the `main()` function simply parsing arguments and printing progress every second.
+I won't explain the example hugely __dash__ it's pretty straightforward, it simply
+parallel reads all source files in `chunk_size` chunks, writing them to their appropriate
+offset into the output file. It
+is a very good example, incidentally, of why C++11 is like a whole new language over
+C++98 even for simple systems programming tasks like this one.
+
+You may note that the temporary buffers for each chunk are allocated using a special
+`page_allocator`. If your operating system allows it, regions of memory returned
+by this allocator have the maximum possible scatter gather DMA efficiency possible.
+Even if your operating system doesn't allow it, it does no harm to use this allocator
+instead of the system allocator.
+
+The chances are that this file copying implementation would be little faster than a naive
+implementation however (unless the source files are on different physical devices, in which
+case you'd see maximum performance). Some might also argue that firing a consumer producer thread per source
+file would be just as easy, albeit that output file position management across threads
+might be slightly tricky.
+
+Let us start into where the AFIO implementation starts to deliver real value add: a multiprocess safe log file and
+finding which files in a directory tree contain a regular expression.
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[endsect] [/file_concat]
+
+[section:atomic_logging Achieving atomicity on the filing system]
+
+'''<?dbhtml-include href="disqus_identifiers/atomic_logging.html"?>'''
+
+__triplegit__, the forthcoming reliable graph database store, ideally needs to allow multiple processes to
+concurrently read and write the graph store without mutual exclusion where possible. Each collection of hashes which form
+the tree which makes up some given version of the graph is itself a hashed object in the content
+addressable store, and you can have multiple named graphs in the graph store which may or may not
+share nodes. One problem as with all databases is how to efficiently issue an atomic transaction
+which updates multiple graphs simultaneously and atomically when there is the potential of concurrent
+writers also trying to issue write transactions which may, or may not, cause conflict with other
+transactions.
+
+The really naive solution is to keep a single lock file which is created using O_EXCL i.e. fail
+if you didn't create the file for the entire graph store. This serialises
+all transactions, and therefore eliminates any problems with updates clashing. This is usefully
+well supported by all operating systems, and by NFS and Samba.
+
+A less naive solution is to keep one lock file per graph, and to
+use a multiple lock and backoff strategy to lock the requisite number of graphs for some given
+transaction. The big problem with this approach is that you are unfairly penalised for especially large multi-graph transactions over others
+with smaller transactions as lock complexity is worse than linear. Nevertheless performance is actually not bad: these are results for my 3.9Ghz i7-3770K workstation
+using AFIO to implement the lock file with various numbers of concurrent writers (note that Windows provides magic
+flags for telling it about lock files, if not used expect a 40% performance reduction):
+
+[table:lock_file_performance Lock file performance on various operating systems:
+ [[writers][lock files][Win8.1 x64 NTFS HD][Win8.1 x64 NTFS SSD][Linux x64 ext4 SSD][FreeBSD 10.1 ZFS SSD]]
+ [[1][1][2468][2295][3590][9922]]
+ [[2][1][2507][2385][3583][9903]]
+ [[4][1][1966][2161][3664][9684]]
+ [[8][1][1400][1851][3703][6537]]
+ [[16][1][742][602][3833][1251]]
+ []
+ [[1][8][826][888][1378][2455]]
+ [[2][8][508][637][576][917]]
+ [[4][8][67][167][417][63]]
+ [[8][8][37][117][106][0.55]]
+ [[16][8][33][77][26][0.5]]
+]
+
+As you can see, Linux does a very good job of O(1) to waiters complexity, but performance on Windows
+and FreeBSD collapses once you exceed CPU cores. Also, Windows is sensitive to device block size __dash__ the
+hard drive outperforms the SSD because it has 512 byte sectors and the SSD has 4096 byte sectors.
+I suspect that internally Windows memcpy()'s in device native sector sizes, or is actually sending
+data to physical storage despite us marking this file as temporary. One very striking observation is
+how FreeBSD is a full 2.76x the performance of Linux and 4.32x that of Windows.
+
+A much more intelligent way of solving this problem is to figure out which graphs are common across each
+of the transactions currently pending, and to strictly order the transactions in the sequence
+which maximises throughput without updates clashing.
+One way of many distributed writers constructing a shared graph of dependencies is to append messages
+into a shared file, and then one can deploy a distributed mutual exclusion algorithm of which those
+by Suzuki and Kasami, Maekawa and Ricart and Agrawala are the most famous. This requires the ability
+to atomically append to the shared file, something guaranteed on all operating systems, but unfortunately
+not guaranteed by NFS nor Samba (though when combined with advisory file locking those do work as
+expected, albeit with poor performance). This means that on an all-Windows setup, or if on POSIX and
+not using NFS nor Samba, the atomic append method could be valuable, especially as the cost of locking
+multiple actors is pretty much the same as locking a single actor so you get eight graphs locked for
+the same cost as locking one.
+
+[table:atomic_append_performance Atomic append lock performance on various operating systems:
+ [[writers][lock files][Win8.1 x64 NTFS HD][Win8.1 x64 NTFS SSD][Linux x64 ext4 SSD][FreeBSD 10.1 ZFS SSD]]
+ [[1][1][2592][2875][1198][29]]
+ [[2][1][1284][2565][1344][25]]
+ [[4][1][1420][2384][1327][35]]
+ [[8][1][1262][1764][1254][55]]
+ [[16][1][428][520][1260][37]]
+]
+
+Linux once against does a great job of O(1) to waiters complexity, but at the third of the speed of
+a simple lock file and up to half the speed of Windows. Windows does better than Linux here especially
+on SSDs where it is faster than a simple lock file, but doesn't scale to waiters once they pass CPU core count.
+FreeBSD is two orders of magnitude slower which is because ZFS checksums and copy on writes file changes,
+so every time we append 16 bytes we are forcing a full copy of the 128Kb extent to be issued. It would
+appear that ZFS syncs its internal buffers when a different file descriptor atomic appends to the same file
+__dash__ this has the above pathological performance outcome unfortunately.
+
+This introduces the final potential solution which is that of the quagmire of advisory file locking.
+This is an area where Windows and POSIX diverge very significantly, and the interactions between
+Windows and POSIX when Windows locks regions in a file on a Samba share on a POSIX machine or when
+POSIX does byte range locking at all (there is a very fun stanza in the POSIX standard which basically
+releases all your locks on first file descriptor close) are full of quirks, races and other nasties.
+For this reason you should avoid the very temporary and experimental code currently in AFIO which
+implements Samba and NFS safe file range locking where theoretically both Windows and POSIX code
+can safely lock ranges in files concurrently, those APIs are not documented for good reason!
+Still, performance with these __dash__ despite the hoop jumping inefficiencies AFIO leaps through
+to implement relatively sane semantics __dash__ is impressive.
+
+[table:advisory_lock_file_performance Advisory lock file performance on various operating systems:
+ [[writers][lock files][Win8.1 x64 NTFS HD][Win8.1 x64 NTFS SSD][Linux x64 ext4 SSD][FreeBSD 10.1 ZFS SSD]]
+ [[1][1][5799][5166][3466][21536]]
+ [[2][1][5788][6656][2215][11654]]
+ [[4][1][5775][7020][1073][5384]]
+ [[8][1][5773][6738][518][2584]]
+ [[16][1][5695][5617][360][1326]]
+]
+
+Fascinatingly the tables suddenly switch here: Windows sees O(1) to waiters complexity, whilst
+Linux and FreeBSD sees a mostly O(N) to waiters complexity drop in performance. FreeBSD, as with the
+simple lock file performance, blows all others out of the water again in advisory lock performance too. I
+should add here that because POSIX advisory locks are per process, the Linux and FreeBSD benchmarks were
+generated by running N copies of the benchmark program whereas the NT kernel inherits the really
+excellent and well thought through file byte range locking model of DEC VMS and treats locks as
+effectively reference counted byte ranges, and therefore works as expected from a single process.
+I have yet to add process-local byte range locking to simulate sane range locking for POSIX, so
+expect the numbers above to worsen.
+
+After all of that, we are left with this locking strategy matrix for __triplegit__:
+
+[table:best_locking_strategy_matrix Fastest file system locking strategy for various operating systems:
+ [[Operating system][Best locking policy]]
+ [[Win8.1 x64 NTFS][Advisory locks fastest, then atomic append locks, finally lock files]]
+ [[Linux x64 ext4][Lock files fastest, then advisory locks, finally atomic append locks]]
+ [[FreeBSD 10.1 ZFS][Advisory locks fastest, then lock files, avoid atomic append locks at all costs]]
+]
+
+I should [*emphasise] once again that the advisory locking code is riddled with bugs and you should
+not use it in your code at this time. Once I have a CI testing all possible combinations of locking
+and nothing is erroring out I'll release that code for production use, probably in v1.4.
+
+All these benchmarks came from this benchmarking program I wrote using AFIO which illustrates how
+you might implement the techniques used above:
+
+[benchmark_atomic_log]
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[endsect] [/atomic_logging]
+
+[section:filesystem_races Handling races on the filing system]
+
+'''<?dbhtml-include href="disqus_identifiers/filesystem_races.html"?>'''
+
+Filing systems are a shared resource common to all processes on the system and sometimes the network,
+and are therefore as a globally shared resource inherently racy. Yet overwhelmingly programs, even often those written
+by world expert programmers, singularly assume
+the filing system to be a static, constant and unchanging place only modifiable by the current program,
+as indeed did until very recently the POSIX API standard which defines the common API for Linux, FreeBSD,
+Mac OS X and other Unices.
+When bug reports come in of data being lost, even very large professional corporations can make a real
+hash of testing that their ["fix] isn't worse at losing data than the previous more naive implementation.
+This is because when you program against a mental model of a static, unchanging filesystem you will become
+inevitably surprised when it happens to change at exactly the wrong moment __dash__ which of course is
+a moment you can never replicate on your developer workstation, thus making finding and fixing these
+sorts of bug highly non-trivial.
+
+In case you don't realise how much user data and productivity is lost each year to filing system races,
+just look up ["corrupted Office file] on Google and weep. Even for us programmers, if you try keeping a
+Git repository on a Samba drive expect some interesting, and moreover quite strongly associated to specific
+combinations of client accessing the repo concurrently, object database corruption from time to time.
+
+Well, there is some good news: AFIO makes maximum use of host OS filing system race safeguards, so if you
+write your code against AFIO and take note of the race guarantees section in each individual per-API
+reference documentation page, you should hopefully avoid any possibility of experiencing filing system
+races.
+
+[heading What AFIO provides for managing filing system raciness]
+
+Firstly, readers will probably be quite surprised to learn that the only operating system capable of providing
+completely race free filing system behaviour is Microsoft Windows, or rather the very well designed NT kernel API which AFIO uses directly.
+Linux provides robust file descriptor path discovery and the `XXXat()` POSIX APIs, and with those AFIO can provide pretty
+good race condition safety on Linux up to the final directory in the path.
+Mac OS X provides an unfortunately quite broken file descriptor path discovery, and additionally does not provide the `XXXat()`
+POSIX APIs and so AFIO cannot provide race protection, but can throw exceptions sometimes if it detects the filesystem
+has suddenly changed and you're about to delete the wrong file (you shouldn't rely on this, it's racy). FreeBSD provides the `XXXat()` POSIX APIs, but its file
+descriptor path discovery only works correctly for directory not file handles due to a kernel bug (I've opened
+a feature request ticket for this at [@https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198570 https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198570]) and therefore AFIO
+can provide only race condition safety for directories only on FreeBSD.
+
+Additionally, working with the filing system in a race safe way on POSIX requires opening a file descriptor to the
+containing directory for every operation (some proprietary Linux extensions allow this to be avoided for some operations on
+newer Linux kernels). AFIO will keep a global cache of open file handles for containing directories
+on request using the `file_flags::hold_parent_open` flag which can be enabled per dispatcher or per individual file handle open,
+this very significantly reduces the cost of race condition safety on POSIX ['for file entries only] as directories ignore
+the `file_flags::hold_parent_open` flag, though at the cost of increased file descriptor usage, which has low hard limits
+especially on OS X which is why it is disabled by default. The alternative if you don't want AFIO to bother with race safety
+is to specify the `file_flags::no_race_protection` flag per dispatcher or per individual file handle open, this causes AFIO
+to use the same maximum performance code paths as used before the v1.3 engine.
+
+[heading How to implement filing system race safety on AFIO]
+
+The essential rule for achieving maximum filing system race safety is to avoid using absolute paths where possible. If
+you want your code to also be safe on POSIX, you must additionally only assume race safety up to the final directory in
+a path __dash__ thereafter design your node to never behave racily within a single directory.
+
+The core container type for specifying a location on the filing system to AFIO is __afio_path_req__ which looks like this:
+
+ struct path_req
+ {
+ bool is_relative; // Whether the precondition is also where this path begins.
+ afio::path path; // The filing system path to be used for this operation.
+ file_flags flags; // The flags to be used for this operation (note they can be overriden by flags passed during dispatcher construction).
+ future<> precondition; // An optional precondition for this operation.
+ path_req(T &&); // Converts T to a filesystem::path, makes it absolute, then converts to an afio::path
+ path_req(bool, T &&); // If the bool is true, converts T to an afio::path fragment. If false, same as above overload (i.e. make absolute).
+ };
+
+For convenience, type markup is provided for the boolean taking constructor, these being `path_req::relative` and
+`path_req::absolute`.
+
+If the path is relative, then the path of the precondition is used as the base from which the relative path fragment
+operates. On FreeBSD, Linux and Windows this base extension happens inside the kernel and so the current path of the precondition
+really doesn't matter __dash__ it could be changing a thousand times per second and it wouldn't matter. On OS X due
+to lack of the `XXXat()` POSIX APIs the path of the precondition is fetched and the extension done by hand.
+
+An AFIO extension allows you to specify a file as precondition. In this situation, if you specify an empty path
+then you mean the precondition itself which is very useful for deleting or renaming an open file handle. If you want
+a sibling file, this can be found via a path fragment starting with `../`, though note that this necessarily is racy
+to the containing directory
+(AFIO opens the containing directory of the file, ensuring the directory contains an inode matching the file, and
+then uses that directory handle as a base __dash__ the race here being if the file relocates after matching its
+containing directory).
+
+[heading Gotchas specific to Microsoft Windows]
+
+Finally, there are some gotchas specific to Microsoft Windows:
+
+1. You cannot rename a directory which has an open file handle in any process to any item anywhere within itself or its children.
+
+2. You cannot rename to a destination which has an open file handle with DELETE permissions (`file_flags::write`)
+to itself or any of its parent directories in any process. You CAN do this from a source like this, but the destination cannot be
+like this (why is this? It is not documented anywhere in Microsoft's documentation, but if I had to guess, I'd suggest that the
+atomicity of the rename is implemented by taking an op lock on the destination, an op lock not granted if any handles exist which
+could change the path suddenly. I'm not sure if Microsoft are themselves aware of this limitation).
+
+One might note that much of the utility of race protecting APIs is lost with these restrictions. However, note that one could
+emulate POSIX semantics by renaming out all the contents of a directory to be renamed to elsewhere, rename the directory, and then
+renaming the contents back in. Given the pathetic slowness of opening handles on Windows, this might seem impossibly inefficient,
+however NT provides a little known `FILE_DELETE_CHILD` permission which gives you delete/rename permission on all the children
+and subchildren of a directory with just one handle open. I learned about this flag the hard way, by it breaking in many subtle ways
+AFIO's functioning on Windows when it was requested by default, something which took several days of head scratching to track down. AFIO doesn't
+currently do this trick of renaming out and back in on Windows, but might in the future after a lot more experimentation as to if
+it is viable and reliable without surprises.
+
+On Windows opening a directory with write access requests rename/delete privileges, whereas on POSIX
+the write access request is ignored for directories as POSIX doesn't allow it anyway. This allows you to write identical code
+which works universally.
+
+As an example of some programming AFIO safely on an extremely unstable filing system, below is the functional test which verifies AFIO
+for filing system race safety.
+As you will see, a worker thread is solely dedicated to renaming directories to unique names whilst the main thread creates files
+inside those constantly changing directories, and relinks them into another directory which is also constantly changing on POSIX, but
+is stable on Windows. This is iterated for a substantial period of time to verify that nothing goes wrong.
+
+[race_protection_example]
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[endsect] [/filesystem_races]
+
+
+[section:so_what What performance benefit does asynchronous file i/o bring me? A demonstration]
+
+'''<?dbhtml-include href="disqus_identifiers/so_what.html"?>'''
+
+So we can schedule file i/o operations asynchronously with AFIO: what's the benefit of doing that
+instead of creating separate threads of execution and executing the file i/o there instead?
+
+As a quick summary as we're about to prove our case, there are three main benefits to using AFIO
+over doing it by hand:
+
+# You don't have to worry about threading or race conditions or losing error state (as much). AFIO
+does all that for you.
+# On platforms which provide native asynchronous file i/o support and/or native scatter/gather
+file i/o support, AFIO will use that
+instead of issuing multiple filing system operations using a thread pool to achieve
+concurrency. This can very significantly reduce the number of threads needed to keep your
+storage device fully occupied __dash__ remember that ['queue depth] i.e. that metric you see all
+over storage device benchmarks is the number of operations in flight at once, which implies
+the number of threads needed. Most storage devices are IOPS limited due to SATA or SAS
+connection latencies without introducing queue depth __dash__ in particular, modern SSDs cannot
+achieve tens of thousands of IOPS range without substantial queue depths, which without
+using a native asynchronous file i/o API means lots of threads.
+# It's very, very easy to have AFIO turn off file system caching, readahead or buffering
+on a case by case basis which makes writing scalable random synchronous file i/o applications
+much easier.
+
+What these three items mean is that writing scalable high-performance filing system code is much easier.
+Let us take a real world comparative example, this being a simple STL iostreams, Boost Filesystem and OpenMP
+based find regular expression in files implementation:
+
+[find_in_files_iostreams]
+
+This implementation is fairly straightforward: enumerate all files in all directories below
+the current directory into a vector, fire off N threads to open each file, read it entirely
+into memory, regex it for the pattern and if found print the file's path.
+
+Let's now look at the AFIO implementation, and you should prepare yourself because it is far
+more mind bendy due to the many nestings of callbacks needed (it may remind you of WinRT or .NET
+code, everything is asynchronous callback):
+
+[warning In the v1.4 engine we ought to gain C++ 1z stackful coroutine support which will let me rewrite
+the below to be vastly simpler and without the multitude of nested callback handlers.]
+
+[find_in_files_afio]
+
+Here the `find_in_files` class is used to carry state across the callbacks __dash__ one could just
+as equally bind the state into each callback's parameters instead via some sort of pointer to
+a struct. But the difference in complexity is noticeable __dash__ you're probably now asking, why
+choose this hideous complexity over the much clearer OpenMP and iostreams implementation[footnote
+Indeed many ask the same when programming WinRT apps __dash__ Microsoft's insistance on never allowing
+any call to block can make simple designs explode with complexities of nested callbacks.]?
+
+Well, that's simple: do you want maximum file i/o performance? Here is a search for ["Niall] in a
+Boost working directory which contained about 7Gb of data across ~35k files[footnote Test machine
+was a 3.5Ghz Intel i7-3770K Microsoft Windows 8 x64 machine with Seagate ST3320620AS 7200rpm hard
+drive. Note that AFIO has a native WinIOCP backend which leverages host asynchronous file i/o support.]:
+
+[table:find_in_files_performance Find in files performance for traditional vs AFIO implementations
+ [[][iostreams, single threaded][iostreams, OpenMP][Boost.AFIO][Boost.AFIO `file_flags::os_direct`][Boost.AFIO `file_flags::os_mmap`[footnote The superiority of memory maps on Windows is because all buffered file i/o is done via memory copying to/from memory maps on Windows anyway, so eliminating that memory copy is huge.]]]
+ [[Warm cache][812 Mb/sec][1810 Mb/sec][2663 Mb/sec][N/A][6512 Mb/sec]]
+ [[][+0%][+123%][+228%][][+702%]]
+ [[Cold cache[footnote File cache reset using [@http://technet.microsoft.com/en-us/sysinternals/ff700229.aspx]]][16 Mb/sec][[*8 Mb/sec]][15 Mb/sec][13.14 Mb/sec][24 Mb/sec]]
+ [[][+0%][[*-50%]][-6%][-18%][+50%]]
+]
+
+Note how AFIO outperforms the OpenMP iostreams implementation by about 50% for a warm cache, with only
+a 6% penalty for a cold cache over a single threaded implementation. Note the [*50%] penalty the OpenMP
+iostreams implementation suffers for a cold cache __dash__ a naive multithreaded implementation causes
+a lot of disc seeks. If you map a file into memory using `file_flags::os_mmap`, AFIO will `memcpy()` from
+that map instead of reading __dash__ or of course you can use the map directly using the pointer returned
+by `try_mapfile()`.
+
+The eagle eyed amongst you will have noticed that the AFIO implementation looks hand tuned with a
+special depth first algorithm balancing concurrency with seek locality __dash__ that's because I invested
+two days into achieving maximum possible performance as a demonstration of AFIO's power (and to find
+and fix some bottlenecks). Some might argue that this is therefore not a fair comparison to the OpenMP iostreams
+implementation.
+
+There are two parts to answering this argument. The first is that yes, the OpenMP iostreams search
+algorithm is fairly stupid and simply tries to read as many files as there are CPUs, and those files could
+be stored anywhere on the spinning disc. Because AFIO
+can issue far more concurrent file open and read requests than OpenMP, it gives a lot more scope to
+the filing system to optimise hard drive seeks and satisfy as many requests as is feasible __dash__
+sufficiently so that with a cold cache, AFIO is a little slower than a single threaded iostreams
+implementation where the filing system can spot the access pattern and prefetch quite effectively.
+A completely valid solution to the OpenMP performance deficit would be to increase thread count dramatically.
+
+The second part of answering that argument is this: AFIO's very flexible op chaining structure lets you
+very easily twiddle with the execution dependency graph to achieve maximum possible performance by
+balancing concurrency (too much or too little is slower, what you need is just the right balance)
+and disc seeks (enumerations are best not done in parallel, it defeats any prefetching algorithm),
+unlike the naive OpenMP implementation which is much harder to play around with. Don't get me wrong:
+if you have plenty of time on your hands, you can implement a hand written and tuned find in files
+implementation that is faster than AFIO's implementation, but it will have taken you a lot longer,
+and the code will neither be as portable nor as maintainable.
+
+'''<?dbhtml-include href="disqus_comments.html"?>'''
+
+[endsect] [/so_what]
+
+[endsect] [/ mini_programs]
+
+[endsect] [/quickstart]
diff --git a/attic/doc/reference.qbk b/attic/doc/reference.qbk
new file mode 100644
index 00000000..0e0dd56f
--- /dev/null
+++ b/attic/doc/reference.qbk
@@ -0,0 +1,110 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+[section:reference Reference]
+
+[section:macros_flags Macros and Flags]
+[include generated/group_macros.qbk]
+[include generated/group_file_flags.qbk]
+[include generated/group_fs_metadata_flags.qbk]
+[include generated/group_metadata_flags.qbk]
+[endsect]
+
+[section:structs Structures]
+[include generated/struct_enumerate_req.qbk]
+[section:io_req io_req]
+[include generated/struct_io_req.qbk]
+[include generated/struct_io_req_3_01const_01_t_01_4.qbk]
+[include generated/struct_io_req_3_01void_01_4.qbk]
+[include generated/struct_io_req_3_01const_01void_01_4.qbk]
+[endsect]
+[include generated/struct_path_req.qbk]
+[include generated/struct_stat_t.qbk]
+[include generated/struct_statfs_t.qbk]
+[endsect]
+[section:classes Classes]
+[include generated/class_current_dispatcher_guard.qbk]
+[include generated/class_directory_entry.qbk]
+[include generated/class_dispatcher.qbk]
+[include generated/class_enqueued_task_3_01_r_07_08_4.qbk]
+[include generated/class_future.qbk]
+[include generated/class_future_3_01void_01_4.qbk]
+[include generated/class_handle.qbk]
+[include generated/class_path.qbk]
+[include generated/class_thread_source.qbk]
+[include generated/class_std_thread_pool.qbk]
+[endsect]
+
+[section:functions Functions]
+[include generated/group_async_file_io_dispatcher.qbk]
+[include generated/group_make_io_req.qbk]
+[include generated/group_process_threadpool.qbk]
+
+[section:dir Functions for opening/creating directories]
+[include generated/group_dir.qbk]
+[endsect]
+[section:rmdir Functions for deleting directories]
+[include generated/group_rmdir.qbk]
+[endsect]
+[section:file Functions for opening/creating files]
+[include generated/group_file.qbk]
+[endsect]
+[section:rmfile Functions for deleting files]
+[include generated/group_rmfile.qbk]
+[endsect]
+[section:symlink Functions for opening/creating symlinks]
+[include generated/group_symlink.qbk]
+[endsect]
+[section:rmsymlink Functions for deleting symlinks]
+[include generated/group_rmsymlink.qbk]
+[endsect]
+[section:sync Functions for synchronising changes to physical storage]
+[include generated/group_sync.qbk]
+[endsect]
+[section:zero Functions for deallocating/zeroing physical storage]
+[include generated/group_zero.qbk]
+[endsect]
+[section:close Functions for closing open handles]
+[include generated/group_close.qbk]
+[endsect]
+[section:read Functions for scatter reading file contents]
+[include generated/group_read.qbk]
+[endsect]
+[section:write Functions for gather writing file contents]
+[include generated/group_write.qbk]
+[endsect]
+[section:truncate Functions for setting maximum file extent]
+[include generated/group_truncate.qbk]
+[endsect]
+[section:enumerate Functions for enumerating directory contents and fetching file metadata]
+[include generated/group_enumerate.qbk]
+[endsect]
+[section:extents Functions for enumerating file physical storage extents]
+[include generated/group_extents.qbk]
+[endsect]
+[section:statfs Functions for fetching storage volume metadata]
+[include generated/group_statfs.qbk]
+[endsect]
+
+[section:to_asio_buffers Specialisations for how types should be parsed into ASIO scatter/gather buffers]
+[include generated/group_to_asio_buffers.qbk]
+[endsect]
+
+[endsect]
+
+[section:utilities Utilities]
+[section:classes Classes]
+[include generated/class_utils_1_1page_allocator.qbk]
+[include generated/class_utils_1_1secded_ecc.qbk]
+[endsect]
+[section:function Functions in namespace utils]
+[include generated/group_utils.qbk]
+[endsect]
+[endsect]
+
+[endsect] [/reference]
diff --git a/attic/doc/release_notes.qbk b/attic/doc/release_notes.qbk
new file mode 100644
index 00000000..6dc10a80
--- /dev/null
+++ b/attic/doc/release_notes.qbk
@@ -0,0 +1,833 @@
+[/============================================================================
+ Boost.AFIO
+
+ Use, modification and distribution is subject to the Boost Software License,
+ Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+=============================================================================/]
+
+[section:release_notes Release Notes]
+
+[/=================]
+[heading Known deviations in this version being presented for Boost peer review from
+version to enter Boost if accepted]
+[/=================]
+
+This peer review edition of AFIO v1.4 has been ["mocked up] with an API which should very closely
+resemble the eventual API in the v1.4 engine which will be rewritten to use the just written
+lightweight future-promise factory toolkit in forthcoming __boost_outcome__. It is, however,
+still in fact the mature v1.3 engine with a faked wrapper API simulating the v1.4 engine.
+Known deviations from the eventual v1.4 release:
+
+* AFIO's `future` is a shim type standing in for the eventual custom Boost.Monad based future.
+Continuations work, but are horribly hacked together __dash__ if your continuation returns
+a Boost.Monad future, it shims in a one way conversion to lightweight futures. If your continuation
+returns anything else, including an AFIO future, it stays within AFIO < v1.4's fairly broken future
+semantics (AFIO was first written back in 2012 when the Concurrency TS looked very different
+indeed, and AFIO has not kept up).
+
+[warning AFIO's future shim type can't do continuations on anything but `future<void>`. For
+any `future<T>` it actually executes the continuation immediately which will usually just
+happen to work through fortune as the continuation usually does a `get()`.]
+
+* Relatedly, because AFIO < v1.4 implemented future continuations atop STL and Boost futures
+using an internal hash table to look up the associated extra operation metadata per future,
+these APIs will also be vanishing:
+
+ * `async_io_dispatcher_base::completion()` - replaced with future.then()
+ * `async_io_dispatcher_base::call()` - replaced with future.then()
+ * `async_io_dispatcher_base::barrier()` - replaced with when_all().
+
+* Lightweight future-promise also enables C++ 1z coroutines, and therefore the code examples
+in the documentation can then be rewritten to use C++ 1z coroutines where appropriate. This
+should particularly aid the find regex in files tutorial example which is currently very messy.
+
+[/=================]
+[heading Anticipated forthcoming features in future versions]
+[/=================]
+
+* The v1.4 engine will be rewritten yet again to use a new custom future
+implementation whereby async_io_op shall become afio::future<T>. This should
+let the API no longer return two sets of futures when returning results
+(async_io_op therefore matches afio::future<void>), plus make best use of the
+proposed concurrent_unordered_map by finally actually processing batches of
+operations as a batch, instead of one at a time.
+
+* The v1.4 engine afio::future<T> ought to transparently support Boost.Fiber,
+this should let you program against AFIO using awaitable resumable functions
+which is much cleaner. The v1.3 engine already reduced latency by 50% over
+the v1.2 engine, but with Fibers we finally ought to eliminate the O(waiters)
+scaling for op completions in favour of O(1) to waiters.
+
+* AFIO v1.5 will abstract out all the OS specific code into a C (not C++)
+abstraction layer. This makes it far easier to reuse the same code for a later
+AFIO based Filesystem TS implementation. It also eliminates the use of C++
+exceptions at the OS specific layer. Note I may still use lambdas in the C
+abstraction layer, if compiled as C these appear as C callbacks but if compiled
+as C++ they are some callable type.
+
+* It is hoped that the v1.5 engine will have two additional bindings, one
+for Microsoft COM thanks to John Bandela's CppComponents (https://github.com/jbandela/cppcomponents)
+and another with plain C bindings (which actually wrap the COM object). Both
+these bindings have no requirement for anything Microsoft, and work as expected
+on POSIX.
+
+* The v1.5 engine will finally make use of the API support for alternative async_io_dispatcher
+implementations, adding at least one for temp file support with special temp
+file semantics, and maybe others with transparent hashing and bit flip healing
+(see below). The temp file support will allow anonymous and named temp files
+which can be device volume matched to some path, thus allowing file move to be
+done without copying. Anonymous temp files can use Linux specific facilities
+for those.
+
+* Finally, making good use of the new coroutine support the v1.5 engine should
+have an async fast batch hash engine which provides transparent hashing of all
+async reads and writes with optional SECDEC ECC calculation. Hashes will probably
+be initially limited to Blake2b (crypto strong and very fast, even on ARM) and SpookyHash
+(not crypto strong, but superbly fast and for small blocks), though I may dust off
+my 4-SHA SSE2/NEON SHA256 engine for Intel and ARM for the giggle. In addition
+to fast batch hashing, the coroutine support should make filing system tree
+algorithms enormously easier to write, so expect an optimally fast race free
+directory tree visitor, deleter, mover and copier which is the find in files
+tutorial made generic.
+
+
+[h5 In some later version, in order:]
+
+* When ignoring the close of a cached directory handle, kick out its weak_ptr from
+the central directory cache if its reference count is 1.
+
+* Individual file change monitoring. This would be very useful for implementing
+distributed mutual exclusion algorithms to avoid spinning on file updates.
+
+* Related to the preceding item, formal async lock file support with deadline
+timeouts.
+
+* Portable fast file byte range advisory locking which works across network shares,
+but can still utilise shared memory when possible.
+
+* Related to locking files, the ability to copy files (unwritten destination locked until
+complete) using a variety of algorithms (including cheap copies on btrfs [cp --reflink=auto])
+with progress notification via the file change monitoring. Extend rename file and delete file
+with lock file options.
+
+* Also related to locking files, DeleteOnClose should use advisory locks to have the
+last handle close do the file delete on POSIX.
+
+* Right now specifying an empty path with precondition to mean same as precondition is
+inefficient and racy: it opens the containing directory and uses the containing
+directory as the base with its leafname. What it should do is to duplicate the handle
+and use fnctl() etc to reopen the handle with the desired access and flags.
+
+* Extended attributes support. TripleGit could use this to avoid a second file
+handle open of metadata per graph object read. Unsure if NTFS is any faster
+opening EA though, need to test.
+
+* AFIO's stable DLL ABI is intentionally C compatible, so all one needs is a libclang
+tool for generating C wrappers for the DLL ABI which massage C arrays into pseudo std::vector
+const lvalue refs. Such C bindings would be all batch and have none of the friendliness of
+programming AFIO in C++, but they ought to work quite well.
+
+* Kernel side file <=> socket data copying via sendfile and splice on Linux. Integrating
+splice as async with ASIO is unfortunately painful :(
+
+* async_io_dispatcher_base::read_partial() to read as much of a single buffer as
+possible, rather than only complete buffers.
+
+* Fast, scalable portable directory contents change monitoring. It should be able
+to monitor a 1M entry directory experiencing 1% entry changes per second without
+using a shocking amount of RAM.
+
+* ACL support
+
+* Asynchronous file handle closing in ~async_io_handle() (currently if not
+explicitly closed, the async_io_handle destructor must synchronously close)
+
+
+[/=================]
+[heading Boost 1.59 AFIO v1.40 beta]
+[/=================]
+
+* Unit tests were not trapping exception throws from normalise_path(path_normalise::guid_all),
+link() or file(file_flags::create_compressed). This caused test failure on FAT32 and ReFS
+volumes on Windows as those operations are not supported.
+
+* Rename file_buffer_allocator to page_allocator. Fixes issue #95 from review.
+
+* Replace all use of std::cerr with BOOST_AFIO_LOG_FATAL_EXIT(). Fixes issue #104 from review.
+
+* All synchronous free functions not returning anything now return a void. Fixes issue #107 from review.
+
+* ASIO_STANDALONE is now detected using #ifdef not #if. Fixes issue #113 from review.
+
+[/=================]
+[heading Boost 1.59 AFIO v1.40 alpha 21st August 2015 Boost peer review 1 edition]
+[/=================]
+
+* Removed VS2013 support as lightweight future-promise will require VS2015:
+ * Replaced all BOOST_NOEXCEPT and BOOST_NOEXCEPT_OR_NOTHROW with noexcept
+ * Replaced all BOOST_CONSTEXPR with constexpr
+
+* Replaced camel cased items in file_flags with Boost compliant forms.
+
+* Replaced all async_io_op with afio::future<void>. async_io_op::get() is now called
+future<>::get_handle(), plus the former structure members are now member function
+accessors.
+
+* Replaced all functions returning pairs of futures with afio::future<T> instead.
+
+* Docs now contain commenting facility per reference page plus on some of the essay pages.
+
+* Online docs now include a "Try AFIO now in online compiler" button every page.
+
+* Implemented multi-version compatibility, and backported that patch to the v1.3 branch.
+The v1.3 branch is now included in the v1.4 branch as v1.
+
+* Implemented free functions for future continuation for all AFIO operations.
+
+* async_io_handle is now handle. Instead of explicit std::shared_ptr<async_io_handle>
+we now have handle_ptr.
+
+* async_file_io_dispatcher_base is now dispatcher. Instead of explicit std::shared_ptr<async_file_io_dispatcher_base>
+we now have dispatcher_ptr. make_async_io_dispatcher() is now make_dispatcher().
+
+* make_dispatcher() now takes a URI and returns a monad.
+
+* Fixed issue #84 Symlinks were never being followed on Windows.
+
+* Fixed issue #83 Fix failure to handle Dedup reparse point types.
+
+* async_data_op_req is now io_req, similarly for make_async_data_op_req.
+
+* async_enumerate_op_req is now enumerate_req.
+
+* async_path_op_req is now path_req.
+
+* Added optional additional target parameter to symlink().
+
+* Added reparse_point flag to stat_t.
+
+* Verified as working with Boost 1.59 release.
+
+* `handle::try_mapfile()` is no more, we now have `handle::map_file()` which can also
+map writeable files now plus map offsets.
+
+
+Still to do in this release:
+
+* ADD URI REGEX REGISTRATION SYSTEM (need priorities? Need to do some research. Make sure
+it's DLL unload safe).
+
+* Add unit test for multi-version use within the same translation unit.
+
+[/=================]
+[heading Boost 1.58 AFIO v1.31 18th April 2015]
+[/=================]
+
+* Added Appveyor CI support which complements the Travis CI support.
+
+* Verified as working with Boost 1.58 release.
+
+[/=================]
+[heading Boost 1.57 AFIO v1.30 18th March 2015]
+[/=================]
+
+AFIO is now a Boost.BindLib based library. This has resulted in an enormous change set
+which is only barely summarised here:
+
+* AFIO is now capable of:
+ * Being used standalone, or as a Boost module, or if you have inline namespace support in your
+ compiler then both simultaneously in the same translation unit or in the same binary, including
+ any combination of the following library dependency configurations (config macro and its default
+ is shown):
+ * Using Boost.Atomic, Boost.Chrono and Boost.Thread OR the C++ 11 STL (BOOST_AFIO_USE_BOOST_THREAD=0).
+ * Using Boost.Filesystem OR the C++ 1z Filesystem TS (BOOST_AFIO_USE_BOOST_FILESYSTEM=1, except on
+ VS2015 which provides Filesystem).
+ * Using Boost.ASIO OR standalone ASIO (ASIO_STANDALONE=1)
+
+ That makes eight different potential configurations, and all eight can coexist in the same translation unit,
+ though you will find compilation time becomes enormous.
+
+* AFIO's unit test suite no longer requires Boost.Test, and can now alternatively use CATCH C++
+(BOOST_AFIO_USE_BOOST_UNIT_TEST=0) via BindLib.
+
+* Dropped support for these compiler versions due to insufficient C++ 11 support:
+ * Anything before GCC 4.7, as these lack template alias support.
+ * Anything before VS2013, as these lack template alias support. I took the opportunity to clean out
+ the VS2010 special code paths and all the variadic template emulation.
+
+* symlink() now can create file symbolic links on Windows (it'll probably error out due to lack of
+privileges held by the user, but it can now at least try).
+
+* Added the TemporaryFile and DeleteOnClose file_flags. These improve lock file performance on Windows
+by about 60%.
+
+* Added sparse file support. AFIO now always creates sparse file where possible, and converts any
+files it opens for writing into a sparse file where possible. You can disable that behaviour using
+the NoSparse flag.
+
+* stat_t now contains member flags indicating if a file entry is sparse and/or compressed.
+
+* Added a new api zero() which can very efficiently zero ranges in a file by deallocating them on
+physical storage (["hole punching]).
+
+* Added a new api extents() which lets you query which ranges in a file contain valid data.
+
+* Added a new api statfs() which returns a statfs_t which lets you query the volume on which a file
+or directory lives. Again, thanks to using the NT kernel API directly the structure on Windows is
+almost as complete as on POSIX.
+
+* Improved unit testing, especially unit testing of error handling, and indeed found that on Windows
+an invalid handle object generated by a failed open or an explicit close caused the next dependant
+operation to segfault. Also on Windows any errors generated during read() and write() were being lost
+and the dispatcher hanged instead of being reported (oops!).
+
+* Added an extra section to the tutorial on how to implement an atomic log file using the new
+features in AFIO.
+
+* Completely rewrote the async_data_op_req metaprogramming which assembles ASIO scatter gather buffers
+from types and containers supplied to read() and write(). The old system which had to work on VS2010
+was basically a set of repetitive hardcoded template specialisation overloads for void *, T *,
+std::array, std::vector and std::basic_string. The new much more sophisticated metaprogramming (with
+an unfortunate corresponding increase in compilation times) now understands any STL like container
+(detected using SFINAE std::begin() and std::end()), including nested STL like containers, and will
+correctly generate ASIO scatter gather buffers from say something like a std::list<std::list<std::string>>
+but also correctly coalesce buffers for trivial types such as std::vector<std::array<trivial type, N>> where it spots that
+only one ASIO scatter gather buffer is needed. Const types were supported before in that only
+an asio::const_buffer could be constructed from the limited list of supported input types, but now the
+metaprogramming asks STL like containers if their values are const (e.g. unordered_set), and if so
+it will always generate an asio::const_buffer, which of course means that such STL like containers can
+only be used for writing only and not reading.
+
+* Fixed memory corruption in async_file_io_dispatcher destructor which only occurred when there were extant ops
+during destruction.
+
+* Use rename to random name before deletion pattern in rmfile() to work around Windows refusing new file
+creation with the same name as a recently deleted file. enumerate() now filters out such zombied pending
+delete files automatically. This makes AFIO rmfile() work on Windows with POSIX semantics.
+
+* Fixed bug where direntry() was using lstat() instead of fstat() on POSIX and therefore failing if the
+file handle had been deleted.
+
+* symlink() no longer opens target file on POSIX, and instead returns a closed handle. The previous
+behaviour was throwing exceptions when the symlink was broken.
+
+* Made async_io_handle::path() virtual and now capable of asking for the current path of the file handle
+even after the file has been renamed or deleted. Doing this required a refactoring of the error generation
+code which previously consumed a const filesystem::path &, now it consumes a lambda which generates the path
+on demand.
+
+* File handles now refresh their path() to their true path on open, and again at various points during execution.
+A careful audit was undertaken to spot all instances where a fresh path is really needed versus a stale path,
+nevertheless some instances may have been missed. Nevertheless, this now means that AFIO can work with files
+whose names and locations change throughout the time that AFIO holds a path open to them, though
+note that inevitably this support will be racy if the filename changes between AFIO's refresh and the operation.
+
+* Replaced all use of filesystem::path with afio::path which on Windows enforces NT kernel path naming and
+ensures that multiple conversions from win32 path to NT paths are no longer done. Support is also now provided
+for converting the NT kernel paths AFIO uses to traditional DOS ones.
+
+* directory_entry::name() is now a string instead of a path.
+
+* Thanks to Travis CI adding Mac OS X as a CI target, added OS X support to AFIO. This involved writing an
+expect-based lldb automator to get stack backtraces of failures, which was tedious.
+
+* Added APIs random_fill() and random_string() useful for generating crypto strong random file names.
+
+* page_size() has become page_sizes(), and now can return large/huge page sizes. A file_buffer_default_size()
+new API will return the closest page size to 1Mb.
+
+* Added page_allocator, a STL allocator suitable for allocating efficient large file buffers.
+
+* stat_t flags, gen and birthtime weren't being set on FreeBSD. Sorry.
+
+* Made big changes to how async_path_op_req works and is used. Instead of hard coded overloads,
+async_path_op_req now takes a template parameter for the path and has metaprogramming for converting that into
+an afio::path. You can also now supply absolute and relative-to-the-precondition paths via
+`async_path_op_req::absolute` and `async_path_op_req::relative`, plus you can supply a bare precondition
+which finally allows rmfile() et al to directly delete inputs by op handle. Also added a depends() API
+which lets you return one op handle only when another op handle completes.
+
+* Made AFIO provide [*much] stronger filing system race condition protection. This comes with a performance
+cost, so a new file_flags::no_race_protection can disable the new semantics. Renamed file_flags::fast_directory_enumeration
+to file_flags::hold_parent_open as enabling HoldParentOpen will make most of the performance cost of race
+protection disappear.
+
+* Implemented WillBeSequentiallyAccessed and WillBeRandomlyAccessed for POSIX.
+
+* Added is_open() to async_io_handle. Made pseudo deleted file filtering optional in enumerate().
+
+* Stack backtraces are now retained per exception throw as well as per op schedule if BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+is defined (defaults to yes if NDEBUG is not defined). Also added MSVC and FreeBSD support, and exceptions returned by the future
+handle now provide stack backtraces.
+
+* Added virtual member functions link(), unlink() and atomic_relink() to async_io_handle, plus adjusted the engine to
+always call these instead of performing those operations unilaterally.
+
+* Added a race protection unit test.
+
+
+
+[/=================]
+[heading Boost 1.57 AFIO v1.22 stable branch tagged]
+[/=================]
+
+Fixed buffer underflow when decoding Win32 error codes to strings. Thanks to ariccio
+for reporting this.
+
+Relocated docs from ci.nedprod.com to http://boostgsoc13.github.io/boost.afio/
+
+Updated the stale CI test dashboard copy in the DocBook edition.
+
+
+[/=================]
+[heading Boost 1.56 AFIO v1.21 10th Aug 2014]
+[/=================]
+
+Finished getting a ThreadSanitizer (tsan) + UndefinedBehaviorSanitizer (ubsan) pass
+running per-commit on Travis CI (>= v1.2 was tsan clean, I just hadn't bothered getting
+a CI to verify it to be so per commit).
+
+Fixed bug where --lto wasn't turning on the optimiser for LTO output. Sorry.
+
+Added a benchmark testing for latency under concurrency loads.
+
+Added a new FAQ entry on AFIO execution latencies.
+
+Reorganised source code structure to fit modular Boost. AFIO is now a Boost v1.56
+module just like any other. Obviously this will break source code compatibility
+with all preceding Boosts.
+
+[/=================]
+[heading Boost 1.55 AFIO v1.21 23rd Mar 2014]
+[/=================]
+
+Fixed a bug in the custom unit testing framework which was throwing away any exceptions
+being thrown by the tests (thanks to Paul Kirth for finding this and reporting it).
+Fixing this bug revealed that enumerate() with a glob on Windows has never worked
+properly and the exception thrown by MSVC's checked iterators was hiding the problem,
+so fixed that bug too.
+
+Added async_io_dispatcher_base::post_op_filter() and async_io_dispatcher_base::post_readwrite_filter(),
+including documentation examples and integrating filters into the unit testing.
+post_readwrite_filter() ought to be particularly useful to those seeking deep ASIO
+integration. Thanks to Bjorn Reese for the long discussions leading up to this
+choice of improved ASIO support.
+
+During updating the benchmarks below now I have regained access to my developer
+workstation, discovered a severe performance regression in the v1.2 engine of
+around 27% over the v1.1 engine. Steps taken:
+
+1. The shared state in every async_io_op was a shared_ptr, now it is the
+underlying shared_future. Eliminated copies of shared_ptr, now we always
+use the shared_future in enqueued_task directly. This reduced regression to 18%.
+
+2. Removed more code from inside the TSX locks. This reduced regression to 16%.
+
+3. Removed the second TSX lock from complete_async_op(). This eliminated the
+regression and actually added 2% to the v1.1 engine.
+
+4. Removed the second TSX lock from chain_async_op(). This added a further 10%
+over the v1.1 engine, so we are now 12% faster which is about right given
+the v1.2 engine removed 15% of code.
+
+Added nested TSX transaction support.
+
+The CI shows that clang 3.1 now produces segfaulting binaries with this release,
+so rather than debug clang, I simply dropped clang 3.1 support. AFIO now requires
+clang 3.2 or better.
+
+[/=================]
+[heading Boost 1.55 AFIO v1.20 5th Feb 2014]
+[/=================]
+
+This is a major refactor of AFIO's core op dispatch engine to trim it down by
+about 15%. Key breaking differences from the v1.1 series of AFIO are as follows:
+
+* Replaced all use of packaged_task with enqueued_task, a custom implementation
+which makes possible many performance improvements throughout the engine.
+* thread_source::enqueue() now can take a preprepared enqueued_task.
+* thread_source::enqueue() now always returns a shared_future instead of a future.
+This has had knock on effects throughout AFIO, so many futures are now shared_future.
+* Completion handler spec has changed from:
+
+ pair<bool, shared_ptr<async_io_handle>> (*)(size_t id, shared_ptr<async_io_handle> h, exception_ptr *e)
+
+ to:
+
+ pair<bool, shared_ptr<async_io_handle>> (*)(size_t id, async_io_op preceding)
+
+ This substantially improves performance, simplifies the implementation, and lets
+ completion handlers more readily retrieve the error state of preceding operations
+ and react appropriately.
+* All restrictions on immediate completions have been removed. You can now do anything
+in an immediate completion that you can do in a normal completion.
+* async_io_op::h now always refers to a correct future i.e. the future is no longer
+lazily allocated.
+* Now that op futures are always correct, when_all(ops) has been drastically simplified
+to an implementation which literally assembles the futures into a list and passes
+them to boost::wait_for_all().
+* Added when_any(ops).
+
+[/=================]
+[heading Boost 1.55 AFIO v1.11]
+[/=================]
+
+Added --fast-build to test Jamfile to preserve my sanity attempting to work with
+AFIO on an Intel Atom 220 netbook.
+
+Fixed failure to auto-const an async_data_op_req<boost::asio::mutable_buffer>
+when used for writing. Thanks to Bjorn Reese for reporting this.
+
+Replaced use of std::runtime_error with std::invalid_argument where that makes
+sense. Thanks to Bjorn Reese for reporting this.
+
+Replaced throwing of std::ios_base::failure with std::system_error. Thanks to
+Bjorn Reese for suggesting and submitting a patch for this.
+
+async_io_dispatcher_base::enumerate() did not take a metadata_flags, and it
+was supposed to. Thanks to Bjorn Reese for reporting this.
+
+Added a unit compilation test to ensure that implicit construction from a
+single arg to the op convenience classes works as intended.
+
+Significantly optimised build system and added in precompiled headers support.
+Combined with --fast-build this provides an 8x build time improvement.
+
+boost::afio::stat_t::st_type() is now a boost::filesystem::file_type instead
+of replicating the POSIX file type codes. Thanks to Bjorn Reese for suggesting
+this.
+
+boost::afio::stat_t::st_mode() is now st_perms(). Also disabled unused fields in
+stat_t on Windows. Thanks to Bjorn Reese for suggesting this.
+
+[/=================]
+[heading Boost 1.55 AFIO v1.1 1st Nov 2013]
+[/=================]
+
+Immediate completions no longer hold the opslock, which meant the opslock could be
+changed from a recursive mutex to a spinlock. The new, more parallelised, behaviour
+illuminated a number of new race conditions in when_all() which have been fixed.
+
+Completely gutted dispatch engine and replaced with a new, almost entirely wait
+free implementation based on throwing atomics at the problem. If it weren't for the spin lock around the
+central ops hash table, AFIO would now be an entirely wait free design.
+
+In order to do something about that spin lock, replaced all locking in AFIO (apart
+from the directory file handle cache) with memory transactions instead. This
+does CPUID at runtime and will use Intel's TSX-NI memory transaction implementation
+if available, if not it falls back to a spin lock based emulation. On memory
+transaction capable CPUs, AFIO is now almost entirely wait free, apart from when
+it has to fetch memory from the kernel.
+
+Made AFIO usable as headers only.
+
+[/=================]
+[heading Boost 1.55 AFIO v1.0 27th Sep 2013]
+[/=================]
+
+First release for end of Google Summer of Code 2013.
+
+[endsect]
+
+
+
+
+[section:FAQ Frequently Asked Questions]
+
+[section:closure_performance What is the maximum throughput of AFIO's closure execution engine aka
+how many IOPS can I push with AFIO?]
+
+For v1.3 of the engine, maximum ops [*dispatch] throughput is approximately as follows, where the values for `call()` might be for
+use as a closure engine whereas the values for `completion()` might be for max filing system IOPS[footnote The phrase
+["might be] is important: a null closure benchmark will always have dispatch rate problems i.e. the closures being executed
+take less time to execute than the time to dispatch them, so these figures are best read as maximum dispatch rate, not
+maximum IOPS.]:
+
+[table:throughput Maximum null closure dispatch rate on a 3.5Ghz Intel Core i7 3770K for AFIO v1.3[footnote Benchmarks compiled using `address-model=64 link=static --lto release -j 4`.]
+[[Operating system][`call()` unchained][`call()` chained][`completion()` unchained][`completion()` chained][Raw ASIO]]
+[[Microsoft Windows 8 x64 with Visual Studio 2013][[role alignright 1148070]][[role alignright 704051]][[role alignright 1850090]][[role alignright 877899]][[role alignright 2750088]]]
+[[['Relative to ASIO]][[role alignright 42%]][[role alignright 26%]][[role alignright 67%]][[role alignright 32%]][[role alignright 100%]]]
+[[['Relative to AFIO v1.21]][[role alignright +1%]][[role alignright +16%]][[role alignright +14%]][[role alignright +36%]][]]
+[[['Relative to AFIO v1.1]][[role alignright +26%]][[role alignright +18%]][[role alignright +19%]][[role alignright +20%]][]]
+
+[[][][][][][]]
+
+[[Ubuntu 14.04 LTS Linux x64 with GCC 4.9.2][[role alignright 1149470]][[role alignright 1100650]][[role alignright 1629110]][[role alignright 1308240]][[role alignright 1927300]]]
+[[['Relative to ASIO]][[role alignright 60%]][[role alignright 57%]][[role alignright 85%]][[role alignright 68%]][[role alignright 100%]]]
+[[['Relative to AFIO v1.21]][[role alignright -11%]][[role alignright +8%]][[role alignright +10%]][[role alignright +9%]][]]
+[[['Relative to AFIO v1.1]][[role alignright +5%]][[role alignright +39%]][[role alignright +14%]][[role alignright +20%]][]]
+[[][][][][][]]
+
+]
+
+We hope that ~600k min IOPS surely ought to be enough to max out any SATA III SSD __dash__ it should even max out any mid range
+PCIe based SSD too, and if paired with a CPU with more cores, AFIO's almost wait free design ought to scale out fairly
+well too such that it may be able to max out even top end PCIe SSDs, some of which can push 10m IOPS now.
+
+[endsect] [/closure_performance]
+
+[section:closure_latency What is the latency of AFIO's closure execution engine aka
+how quickly can I push a single op with AFIO?]
+
+For v1.3 of the engine on a quad core hyperthreading 3.5Ghz Intel Core i7 3770K, I found
+the following latency graph which is in CPU cycles:
+
+[role aligncenter [$afio_latencies.png]]
+
+The solid lines represent average dispatch to execution and execution finish to completion
+notification latencies and are cumulative on the left scale, whilst the dotted lines represent
+minimum latencies and are cumulative on the right scale. As one can see, one can expect a
+minimum of around 6,000 CPU cycles between dispatch and the operation beginning, and a minimum
+of around 4,000 cycles between the operation completing and its op reference becoming signalled,
+thus making total minimum latency per op around 10,000 CPU cycles, or around 2.5 microseconds.
+
+Average latency is rather worse of course, but if you don't exceed CPU cores you can expect a
+60,000 CPU cycle total latency with a 95% confidence
+interval of plus/minus 600 CPU cycles, which is 15 microseconds plus/minus 0.15 microseconds.
+Given that a filing system on a PCIe SSD might have a 80 microsecond latency, AFIO could be adding
+20% to that, however do bear in mind that a thread sleep and wake costs 52 microseconds so
+we are operating pretty close to hardware limits here.
+
+[endsect] [/closure_latency]
+
+[section:stuck_ops I'm seeing ["WARNING: `~async_file_dispatcher_base()` detects stuck `async_io_op` in total of X ops] during
+process close. What does this mean?]
+
+This means that you scheduled ops with a dispatcher which did not complete in a timely fashion before you tried to destroy
+that dispatcher. This generally indicates a bug in your code, which can include:
+
+# An op is still running in some thread pool and you didn't use __afio_when_all__ or `get()` to wait for it to complete before trying
+to destroy the dispatcher.
+# An op's precondition never completed and therefore the op was never started.
+
+Tracking down the cause of the latter in particular is very similar to tracking down race conditions i.e. it's hard, and we
+as the authors of __boost_afio__ know just how hard! Try recompiling AFIO with the macro
+`BOOST_AFIO_OP_STACKBACKTRACEDEPTH` set to a reasonable depth like 8 will have AFIO take a stack backtrace for every op
+allocated which it then will print during the stuck ops warnings. This can be helpful to identify which ops exactly are
+stuck, and then you can figure out which preconditions of theirs are the cause.
+
+Note that `BOOST_AFIO_OP_STACKBACKTRACEDEPTH` has two implementations on POSIX, one based on glibc `backtrace()` and the other based
+on libunwind. The latter is more portable, but requires explicit linking with libunwind, so we have defaulted to the former.
+
+[endsect] [/stuck_ops]
+
+[section:vector_use Why did you use `std::vector<>` as the ops batch container instead of a generic iterator range?]
+
+# `std::vector<async_io_op>` is the closest thing to a variable length array in C++[footnote Ok, there is also
+the oft-forgotten `std::valarray<>` too, but its use as a generic container isn't recommended.], at least until C++ 14 where we
+will gain `std::dynarray<>` and dynamic array sizing.
+# `std::vector<async_io_op>` is well understood, particularly its performance during by-value copies and during
+`push_back()` which is by far the most common operation you do when preparing a batch.
+# `std::vector<async_io_op>` is very amenable to splitting the batch across threads (not that AFIO currently does this).
+# `std::vector<async_io_op>` is easily transportable through an ABI, whereas arbitrary container iterators would need type
+erasing (i.e. slow). As AFIO was developed initially as not header-only, this made a lot of sense initially.
+
+We are not opposed to the use of generic iterator ranges in an AFIO v2 if there is user demand for such a thing.
+
+[endsect] [/vector_use]
+
+[section:foreign_fd How do I configure my own file descriptor or HANDLE in AFIO?]
+
+Sometimes you receive a file descriptor or HANDLE from a third party piece of code and you need to insert it as-in
+into AFIO for use. The solution to this is very simple:
+
+# Subclass __afio_handle__ with a custom implementation for your file descriptor type. In particular, you probably
+will have custom close semantics (e.g. don't close, or invoke third party code to implement close).
+# Instantiate your custom handle implementation, and pass it into `async_file_io_dispatcher_base::adopt()`. This
+will immediately convert your custom handle type into an `async_io_op` suitable for supplying to `read()`, `write()`
+etc.
+# That's it, there is no more to it.
+
+[endsect] [/foreign_fd]
+
+[section:slow_compile Using AFIO really slows down my compile times. Can't you do something about that?]
+
+You'll find a huge amount depends on your compiler. Here are some build time benchmarks for my developer workstation:
+
+[table:build_time Single thread build times for AFIO v1.3 for various compilers and options on a 3.5Ghz Intel Core i7 3770K
+[[Build flags][Microsoft Windows 8.1 x64 with Visual Studio 2013][Ubuntu 14.04 LTS Linux x64 with GCC 4.9 and gold linker][Ubuntu 14.04 LTS Linux x64 with clang 3.4 and gold linker]]
+[ [`--link-test --fast-build debug`][[role green 0m55s]][3m53s][asio failure]]
+[ [`--link-test debug`][2m10s][10m26s][5m46s]]
+[ [`--link-test --lto debug`][5m37s][GCC ICE][5m42s]]
+[ [`--link-test pch=off debug`][7m17s][[role red 12m0s]][5m45s]]
+[[`--link-test --fast-build release`][1m10s][3m22s][asio failure]]
+[ [`--link-test release`][2m58s][9m57s][8m10s]]
+[ [`--link-test --lto release`][7m30s][[role red 13m0s]][8m11s]]
+[ [standalone alltests release][0m20s][0m51s][0m42s]]
+[[standalone multiabi alltests release][0m39s][1m34s][1m13s]]
+]
+
+[table:build_time Single thread build times for AFIO v1.2 for various compilers and options on a 3.5Ghz Intel Core i7 3770K
+[[Build flags][Microsoft Windows 8 x64 with Visual Studio 2013][Ubuntu 12.04 LTS Linux x64 with GCC 4.8 and gold linker][Ubuntu 12.04 LTS Linux x64 with clang 3.4 and gold linker]]
+[ [`--link-test --fast-build debug`][01m43s][03m31s[footnote ASIO has a link error without `link=static`]][fails]]
+[ [`--link-test debug`][02m40s][06m15s][04m24s]]
+[ [`--link-test --lto debug`][[role red 09m36s]][06m23s][04m32s]]
+[ [`--link-test pch=off debug`][06m12s][08m01s][04m26s]]
+[[`--link-test --fast-build release`][02m01s][03m15s[footnote ASIO has a link error without `link=static`]][fails]]
+[ [`--link-test release`][03m09s][06m28s][06m06s]]
+[ [`--link-test --lto release`][10m24s][07m08s][06m14s]]
+]
+
+You'll note that v1.3 is substantially faster than v1.2 to build on MSVC for some odd reason, despite considerably
+more metaprogramming, code, and tests. The rises in times like with GCC and clang make more sense.
+
+The benefit of `--fast-build` grows exponentially the less powerful the CPU: on my Intel Atom 220
+netbook `--fast-build` gets total build times under five minutes which is impressive compared to
+the twenty or so minutes without that flag.
+
+`--fast-build` works by defining `BOOST_AFIO_HEADERS_ONLY` to 0 for each compiland, and
+linking to the AFIO shared library instead. It also does the same for ASIO by defining `BOOST_ASIO_SEPARATE_COMPILATION`
+and `BOOST_ASIO_DYN_LINK` for each compiland, and links to the ASIO
+shared library instead (which is built via `boost/asio/impl/src.hpp`, see ASIO's docs).
+
+[endsect] [/slow_compile]
+
+[section:fatal_error_read Why do I get a fatal application exit with `FATAL EXCEPTION: Failed to read all buffers` when I read a file?]
+
+This is actually a safety checkpoint for your code: in complex, multi-process concurrent reading and writing of
+the same file, it is extremely difficult to coordinate changing file lengths with i/o in a way which
+doesn't introduce race conditions OR unacceptably low performance. AFIO therefore doesn't even
+try[footnote AFIO [*will] try to provide a synchronised, accurate file extent after fast portable
+file locking support has been added, but until then no.]
+and simply requires you the programmer to ALWAYS do i/o, whether reading or writing, within the extent
+of a file. In other words, if you're going to read 100 bytes from offset 100 in a file, that file
+better be at least 200 bytes long or it's going to fail with a fatal application exit.
+
+This will probably seem harsh to anyone using AFIO for the first time, because the following
+naive code will fatal exit the application if foo.txt is not 1024 bytes or longer:
+
+[readallof_example_bad]
+
+With synchronous i/o a read of 1024 bytes will read ['up to] 1024 bytes, returning the amount
+actually read via some mechanism. With AFIO, either you
+read [*all] 1024 bytes or you read nothing, in which case a normal exception is thrown with
+whatever error the operating system returned. If a ['partial] read happens, then AFIO fatal
+exits the application with the above message as it is probably a logic error in your code.
+
+You may now wonder how to easily not exceed file extents during i/o: for writing, see
+__afio_truncate__ to ensure a file's size before writing, or else open a file for append-only
+in which case all writes atomically occur at the end of the file. For reading, the following code
+is suggested:
+
+[readallof_example_single]
+
+If you're going to read many files from the same directory, it is far faster to open a
+handle to the containing directory and using enumerate to fetch the metadata asynchronously
+instead of using `direntry()` which is synchronous:
+
+[readallof_example_many]
+
+[endsect] [/fatal_error_read]
+
+[section:async_metadata `async_io_handle::direntry()` and `async_io_handle::lstat()` are
+both synchronous functions which block. How then can I get metadata about files and
+directories asynchronously?]
+
+This is easy, if not terribly obvious: call __afio_enumerate__ on the containing
+directory of the file you want metadata for with a shell glob exactly matching the
+file name and with the minimum metadata you are looking for. AFIO will then
+asynchronously fetch that metadata for you, returning it in the future `directory_entry`
+returned by `enumerate()`.
+
+[endsect] [/async_metadata]
+
+[section:deleting_open_files I thought on Windows it is impossible to delete a file which
+is still open for use. How does AFIO achieve this?]
+
+On POSIX you are allowed to call `unlink()` on a file which is open for use __dash__ indeed
+this is a very convenient way of creating an anonymous private temporary file whose storage will be
+deallocated on the last file handle close. Ordinarily Windows does not permit deletion or renaming of
+files still in use, but if all open handles to that file were opened with `FILE_SHARE_DELETE` then
+it ['can] permit renames and deletions of open files.
+
+There are however some gotchas with this, and it is worth listing them here. Firstly, Windows
+never allows you to really delete an open file, rather it is flagged as delete pending and its entry
+in its directory remains visible (though no new file handles may be opened to it) and when
+the very last open handle to the file in the system is closed, only then is it truly deleted. Well,
+actually only ['sort of truly deleted], because Windows only ['appears] to remove the file entry from the directory, but
+in fact that entry is merely hidden and actually still exists and attempting to create a file with the
+same name will return an access denied error. How long it silently exists for depends on a range
+of factors, but put it this way: if your code loops creating and deleting the same file name as you
+might when operating a lock file, you're going to see lots of random spurious access denied errors and
+truly dismal lock file performance compared to POSIX.
+
+Note that this lazy file deletion is different from ["file tunnelling] whereby a file created with
+the same name as a recently deleted file silently inherits its creation time and other metadata. I
+mention this because this feature also gets in the way of POSIX file semantics, but for other reasons.
+
+AFIO works around these un-POSIX file semantics by taking a dual step to deleting files. Firstly, it renames the file to a 128
+bit cryptographically strong random name prefixed by [".afiod] into as high up the directory hierarchy
+as it is able to, and only then does it request the deletion of the file. As AFIO always
+opens files with `FILE_SHARE_DELETE` permission enabled, with that flag Windows permits renaming
+and deletion, and because the name was changed to a very random name somewhere not in its origin
+directory before deletion, you don't see those unexpected and random errors when creating files
+with the same name as a recently deleted file as you do anywhere else on Windows. Because the file
+is probably not in its original containing directory any more, deletions of that directory will
+not fail with ["directory not empty] as they otherwise normally would and indeed do in most Windows
+programs. Handy eh?
+
+Unfortunately, there are additional problems with deleting directories caused by AFIO's caching
+of directory handles in a central table __dash__ this happens by default for all directories opened
+without write permissions and without the `file_flags::unique_directory_handle` flag. Explicit close
+handle requests for these cached directories are ignored by AFIO, and therefore AFIO may hold into existence
+a directory longer than might otherwise be anticipated with the only way of forcing actual deletion
+being the destruction of any reference to that directory, something difficult to achieve in larger
+more complex codebases. Nevertheless, as it is the system which does the deletion, you are guaranteed
+by Windows that the directory will eventually be cleaned up some day, possibly in weeks or months
+on a long lived system. If you don't want this, open directories with write permissions or the
+unique directory handle flag.
+
+[endsect] [/deleting_open_files]
+
+[section:multi_abi How do I reconfigure AFIO within the same translation unit to use a different ABI?]
+
+Here is how the unit test does it:
+
+```
+// Reduce unit testing
+#define BOOST_AFIO_RUNNING_IN_CI 1
+// Use the Boost.Test emulation in Boost.BindLib as Boost.Test isn't multi ABI capable.
+#define BOOST_AFIO_USE_BOOST_UNIT_TEST 0
+
+#define STRINGIZE2(a) #a
+#define STRINGIZE(a, b) STRINGIZE2(a ## b)
+
+// Make unit test names be different
+#define BOOST_CATCH_AUTO_TEST_CASE_NAME(name) STRINGIZE(1_, name)
+
+// A copy of AFIO + unit tests completely standalone apart from Boost.Filesystem
+#define BOOST_AFIO_USE_BOOST_THREAD 0
+#define BOOST_AFIO_USE_BOOST_FILESYSTEM 1
+#define ASIO_STANDALONE 1
+#include "test_all.cpp"
+#undef BOOST_AFIO_USE_BOOST_THREAD
+#undef BOOST_AFIO_USE_BOOST_FILESYSTEM
+#undef ASIO_STANDALONE
+
+// Force unit test utilities to be reincluded
+#undef BOOST_AFIO_TEST_FUNCTIONS_HPP
+#undef BOOST_CATCH_AUTO_TEST_CASE_NAME
+#define BOOST_CATCH_AUTO_TEST_CASE_NAME(name) STRINGIZE(2_, name)
+
+// A copy of AFIO + unit tests using Boost.Thread, Boost.Filesystem and Boost.ASIO
+#define BOOST_AFIO_USE_BOOST_THREAD 1
+#define BOOST_AFIO_USE_BOOST_FILESYSTEM 1
+// ASIO_STANDALONE undefined
+#include "test_all.cpp"
+```
+
+So, simply `#undef` and re-`#define` the configuration macros you want and reinclude `afio.hpp`. You
+can do this every time you switch between configuration in a translation unit, so you can return to
+a previously included translation BUT note that you cannot later include a new header not previously
+included during that configuration's previous existence. In other words, include all the headers you
+are ever going to use in that translation unit just after the first time you configure that configuration.
+
+The situation is very similar for including hard version dependencies. You cannot include a newer version
+of AFIO after including a previous version as some version of AFIO must be the most recent version in order
+for something to live at `boost::afio`. Just always include the newest version first.
+
+[endsect] [/multi_abi]
+
+
+[endsect] [/FAQ]
diff --git a/attic/doc/src/images/afio_latencies.pdf b/attic/doc/src/images/afio_latencies.pdf
new file mode 100644
index 00000000..95cce932
--- /dev/null
+++ b/attic/doc/src/images/afio_latencies.pdf
Binary files differ
diff --git a/attic/doc/src/images/afio_latencies.png b/attic/doc/src/images/afio_latencies.png
new file mode 100644
index 00000000..e52606b8
--- /dev/null
+++ b/attic/doc/src/images/afio_latencies.png
Binary files differ
diff --git a/attic/doc/src/images/afio_latencies_1.2.pdf b/attic/doc/src/images/afio_latencies_1.2.pdf
new file mode 100644
index 00000000..f02821de
--- /dev/null
+++ b/attic/doc/src/images/afio_latencies_1.2.pdf
Binary files differ
diff --git a/attic/doc/src/images/afio_latencies_1.2.png b/attic/doc/src/images/afio_latencies_1.2.png
new file mode 100644
index 00000000..56046543
--- /dev/null
+++ b/attic/doc/src/images/afio_latencies_1.2.png
Binary files differ
diff --git a/attic/doc/src/images/afio_latencies_1.3.pdf b/attic/doc/src/images/afio_latencies_1.3.pdf
new file mode 100644
index 00000000..95cce932
--- /dev/null
+++ b/attic/doc/src/images/afio_latencies_1.3.pdf
Binary files differ
diff --git a/attic/doc/src/images/afio_latencies_1.3.png b/attic/doc/src/images/afio_latencies_1.3.png
new file mode 100644
index 00000000..e52606b8
--- /dev/null
+++ b/attic/doc/src/images/afio_latencies_1.3.png
Binary files differ
diff --git a/attic/doc/src/images/boost.png b/attic/doc/src/images/boost.png
new file mode 100644
index 00000000..0714ca20
--- /dev/null
+++ b/attic/doc/src/images/boost.png
Binary files differ
diff --git a/attic/doc/src/images/boost_full.png b/attic/doc/src/images/boost_full.png
new file mode 100644
index 00000000..b4d51fcd
--- /dev/null
+++ b/attic/doc/src/images/boost_full.png
Binary files differ
diff --git a/attic/doc/src/images/boost_proposed.png b/attic/doc/src/images/boost_proposed.png
new file mode 100644
index 00000000..0714ca20
--- /dev/null
+++ b/attic/doc/src/images/boost_proposed.png
Binary files differ
diff --git a/attic/doc/src/images/boost_proposed.svg b/attic/doc/src/images/boost_proposed.svg
new file mode 100644
index 00000000..10fbdd9c
--- /dev/null
+++ b/attic/doc/src/images/boost_proposed.svg
@@ -0,0 +1,449 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ id="Layer_1"
+ width="444.1889"
+ height="146.81453"
+ viewBox="0 0 444.1889 146.81453"
+ overflow="visible"
+ enable-background="new 0 0 517 500"
+ xml:space="preserve"
+ inkscape:version="0.46"
+ sodipodi:docname="Proposed for Boost.svg"
+ inkscape:export-filename="C:\Users\Paul\Desktop\Proposed for Boost.png"
+ inkscape:export-xdpi="59.369999"
+ inkscape:export-ydpi="59.369999"
+ style="display:inline;overflow:visible"
+ sodipodi:version="0.32"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"><sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1440"
+ inkscape:window-height="845"
+ id="namedview11418"
+ showgrid="false"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:zoom="1.3350176"
+ inkscape:cx="148.34277"
+ inkscape:cy="59.740325"
+ inkscape:window-x="2320"
+ inkscape:window-y="37"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="layer7"><sodipodi:guide
+ id="guide11439"
+ position="-36.703636,107.11469"
+ orientation="0,1" /></sodipodi:namedview><metadata
+ id="metadata11422"><rdf:RDF><cc:Work
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
+ id="defs11420"><linearGradient
+ id="linearGradient3816"
+ inkscape:collect="always"><stop
+ id="stop3818"
+ offset="0"
+ style="stop-color:#000000;stop-opacity:1;" /><stop
+ id="stop3820"
+ offset="1"
+ style="stop-color:#000000;stop-opacity:0;" /></linearGradient><inkscape:perspective
+ id="perspective11424"
+ inkscape:persp3d-origin="258.5 : 166.66667 : 1"
+ inkscape:vp_z="517 : 250 : 1"
+ inkscape:vp_y="0 : 1000 : 0"
+ inkscape:vp_x="0 : 250 : 1"
+ sodipodi:type="inkscape:persp3d" /><radialGradient
+ r="58.375"
+ cy="246.375"
+ cx="63.75"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient11812"
+ xlink:href="#XMLID_5_"
+ inkscape:collect="always" /><linearGradient
+ y2="242.22659"
+ x2="110.314"
+ y1="242.22659"
+ x1="66.6689"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient11814"
+ xlink:href="#XMLID_7_"
+ inkscape:collect="always" /><linearGradient
+ y2="222.79201"
+ x2="78.625504"
+ y1="222.79201"
+ x1="39.3022"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient11816"
+ xlink:href="#XMLID_8_"
+ inkscape:collect="always" /><linearGradient
+ y2="262.375"
+ x2="72.826698"
+ y1="262.375"
+ x1="28.6553"
+ gradientUnits="userSpaceOnUse"
+ id="linearGradient11818"
+ xlink:href="#XMLID_6_"
+ inkscape:collect="always" /><radialGradient
+ gradientUnits="userSpaceOnUse"
+ r="58.375"
+ fy="246.375"
+ fx="63.75"
+ cy="246.375"
+ cx="63.75"
+ id="radialGradient3822"
+ xlink:href="#linearGradient3816"
+ inkscape:collect="always" /><radialGradient
+ r="58.375"
+ fy="246.375"
+ fx="63.75"
+ cy="246.375"
+ cx="63.75"
+ gradientUnits="userSpaceOnUse"
+ id="radialGradient3826"
+ xlink:href="#linearGradient3816"
+ inkscape:collect="always" /></defs>
+
+<g
+ transform="translate(3.5916937e-8,-170.41634)"
+ id="boost">
+
+</g>
+
+
+
+
+<g
+ style="display:none"
+ inkscape:label="Alpha Background"
+ id="layer12"
+ inkscape:groupmode="layer"><rect
+ style="fill:none;stroke:none;display:inline;overflow:visible"
+ id="rect11821"
+ width="444.1889"
+ height="146.81453"
+ x="-3.5916937e-08"
+ y="170.41634"
+ transform="translate(2.8192073e-6,-170.41634)" /></g><g
+ transform="translate(3.5916937e-8,-170.41634)"
+ style="display:inline"
+ inkscape:label="White Background"
+ id="layer11"
+ inkscape:groupmode="layer"><rect
+ y="171.16539"
+ x="16.479183"
+ height="146.81453"
+ width="444.1889"
+ id="rect11810"
+ style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none" /></g><g
+ transform="translate(3.5916937e-8,-170.41634)"
+ style="display:inline"
+ inkscape:label="Boost Logo"
+ id="layer7"
+ inkscape:groupmode="layer"><text
+ style="font-size:101.1230011px;fill:#4a6484;display:inline;overflow:visible;font-family:Denmark"
+ id="text11381"
+ font-size="101.123"
+ transform="matrix(0.9848,0,-0.1736,0.9848,128.1865,269.8252)">boost</text>
+
+
+
+
+
+
+
+
+
+<g
+ transform="matrix(0.98716824,0,0,0.93584122,0.8180256,15.55239)"
+ style="fill:url(#radialGradient3822);fill-opacity:1;display:inline;overflow:visible"
+ id="g3792">
+ <radialGradient
+ id="radialGradient3794"
+ cx="63.75"
+ cy="246.375"
+ r="58.375"
+ gradientUnits="userSpaceOnUse">
+ <stop
+ offset="0"
+ style="stop-color:#000000;fill-opacity:1;fill:url(#radialGradient3822)"
+ id="stop3796" />
+ <stop
+ offset="0.0641"
+ style="stop-color:#191919;fill-opacity:1;fill:url(#radialGradient3822)"
+ id="stop3798" />
+ <stop
+ offset="0.2539"
+ style="stop-color:#5E5E5E;fill-opacity:1;fill:url(#radialGradient3822)"
+ id="stop3800" />
+ <stop
+ offset="0.4336"
+ style="stop-color:#979797;fill-opacity:1;fill:url(#radialGradient3822)"
+ id="stop3802" />
+ <stop
+ offset="0.5979"
+ style="stop-color:#C4C4C4;fill-opacity:1;fill:url(#radialGradient3822)"
+ id="stop3804" />
+ <stop
+ offset="0.7439"
+ style="stop-color:#E4E4E4;fill-opacity:1;fill:url(#radialGradient3822)"
+ id="stop3806" />
+ <stop
+ offset="0.866"
+ style="stop-color:#F8F8F8;fill-opacity:1;fill:url(#radialGradient3822)"
+ id="stop3808" />
+ <stop
+ offset="0.9494"
+ style="stop-color:#FFFFFF;fill-opacity:1;fill:url(#radialGradient3822)"
+ id="stop3810" />
+ </radialGradient>
+ <circle
+ cx="63.75"
+ cy="246.375"
+ r="58.375"
+ id="circle3812"
+ sodipodi:cx="63.75"
+ sodipodi:cy="246.375"
+ sodipodi:rx="58.375"
+ sodipodi:ry="58.375"
+ style="opacity:0.3;fill:url(#radialGradient3826);fill-opacity:1"
+ d="m 122.125,246.375 c 0,32.23962 -26.135378,58.375 -58.375,58.375 C 31.510378,304.75 5.375,278.61462 5.375,246.375 5.375,214.13538 31.510378,188 63.75,188 c 32.239622,0 58.375,26.13538 58.375,58.375 z" />
+</g><g
+ id="right-center"
+ style="display:inline;overflow:visible">
+ <linearGradient
+ y2="242.22659"
+ x2="110.314"
+ y1="242.22659"
+ x1="66.6689"
+ gradientUnits="userSpaceOnUse"
+ id="XMLID_7_">
+ <stop
+ id="stop11400"
+ style="stop-color:#B1D2EC"
+ offset="0.0056" />
+ <stop
+ id="stop11402"
+ style="stop-color:#C8E6F6"
+ offset="1" />
+ </linearGradient>
+ <polygon
+ style="fill:url(#linearGradient11814)"
+ id="polygon11404"
+ points="77.164,223.15 66.669,241.262 78.307,261.304 99.112,261.21 110.314,242.047 99.106,223.15 " />
+ <path
+ style="fill:#ffffff"
+ id="path11406"
+ d="M 99.675,222.15 H 98.536 77.739 76.586 l -0.578,0.997 -9.914,17.112 -0.581,1.004 0.582,1.003 11.056,19.039 0.582,1.001 1.157,-0.005 19.655,-0.088 1.142,-0.005 0.576,-0.986 10.617,-18.162 0.594,-1.016 -0.601,-1.013 -10.617,-17.901 -0.581,-0.98 0,0 z m -31.85,19.112 9.914,-17.112 h 20.797 l 10.617,17.901 0,0 0,0 -10.617,18.162 -19.655,0.088 -11.056,-19.039 0,0 0,0 z" />
+</g><g
+ id="left-up"
+ style="display:inline;overflow:visible">
+ <linearGradient
+ y2="222.79201"
+ x2="78.625504"
+ y1="222.79201"
+ x1="39.3022"
+ gradientUnits="userSpaceOnUse"
+ id="XMLID_8_">
+ <stop
+ id="stop11410"
+ style="stop-color:#B1D2EC"
+ offset="0.0056" />
+ <stop
+ id="stop11412"
+ style="stop-color:#C8E6F6"
+ offset="1" />
+ </linearGradient>
+ <path
+ style="fill:url(#linearGradient11816)"
+ id="path11414"
+ d="m 50.211,207.025 -10.909,19.045 6.912,12.442 16.451,0.047 c 0,0 11.104,-18.828 11.656,-19.763 0.766,0 4.304,0 4.304,0 l -6.704,-11.771 h -21.71 z" />
+ <path
+ style="fill:#ffffff"
+ id="path11416"
+ d="M 72.503,206.025 H 71.34 50.79 49.631 l -0.576,1.006 -10.34,18.053 -0.561,0.979 0.548,0.986 6.353,11.436 0.569,1.026 1.173,0.003 15.292,0.044 1.146,0.003 0.583,-0.987 11.075,-18.778 h 2.012 3.441 l -1.703,-2.99 -5.565,-9.771 -0.575,-1.01 0,0 z M 40.45,226.078 50.79,208.025 h 20.55 l 5.565,9.771 0,0 0,0 H 73.75 l -11.655,19.762 -15.292,-0.044 -6.353,-11.436 0,0 0,0 z" />
+</g><g
+ id="left-down"
+ style="display:inline;overflow:visible">
+ <linearGradient
+ y2="262.375"
+ x2="72.826698"
+ y1="262.375"
+ x1="28.6553"
+ gradientUnits="userSpaceOnUse"
+ id="XMLID_6_">
+ <stop
+ id="stop11390"
+ style="stop-color:#8AACD6"
+ offset="0" />
+ <stop
+ id="stop11392"
+ style="stop-color:#A7C7E6"
+ offset="1" />
+ </linearGradient>
+ <polygon
+ style="fill:url(#linearGradient11818)"
+ id="polygon11394"
+ points="39.721,281.375 61.665,281.375 72.827,262.465 61.668,243.375 39.724,243.375 28.655,262.286 " />
+ <path
+ style="fill:#ffffff"
+ id="path11396"
+ d="M 62.242,242.375 H 61.094 40.297 39.15 l -0.579,0.99 -10.485,17.914 -0.589,1.005 0.585,1.008 10.485,18.086 0.578,0.997 h 1.152 20.797 1.142 l 0.58,-0.983 10.572,-17.913 0.597,-1.012 -0.592,-1.014 -10.572,-18.087 -0.579,-0.991 0,0 z m -32.43,19.914 10.485,-17.914 h 20.797 l 10.572,18.087 0,0 0,0 -10.572,17.913 H 40.297 l -10.485,-18.086 0,0 0,0 z" />
+</g><g
+ id="c_x2B__x2B__libs"
+ style="display:inline;overflow:visible">
+ <g
+ id="g11384">
+
+ <text
+ style="font-size:14.2159996px;letter-spacing:13.40499973;fill:#4a6484;font-family:Denmark"
+ id="text11386"
+ letter-spacing="13.405"
+ font-size="14.216"
+ transform="matrix(0.9698,0,-0.1736,0.9848,129.4995,290.7246)">C++ LIBRARIES</text>
+
+
+
+
+
+
+
+
+
+
+ </g>
+</g><text
+ sodipodi:linespacing="100%"
+ id="text2453"
+ y="210.11618"
+ x="158.79939"
+ style="font-size:36px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:100%;writing-mode:lr-tb;text-anchor:start;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Arial;-inkscape-font-specification:Arial"
+ xml:space="preserve"><tspan
+ sodipodi:role="line"
+ id="tspan2450"
+ x="158.79939"
+ y="210.11618">Proposed for</tspan></text>
+
+
+
+</g><g
+ style="display:none"
+ inkscape:label="Proposed For"
+ id="layer10"
+ inkscape:groupmode="layer"><g
+ id="g2932"
+ style="display:inline;overflow:visible"
+ transform="translate(2.7832904e-6,0)"><g
+ id="g2925"><g
+ id="g2914"
+ style="display:inline;overflow:visible"
+ transform="translate(2.7832904e-6,0)"><g
+ id="g2897"><g
+ id="g2911"
+ style="display:inline;overflow:visible"
+ transform="translate(2.8192073e-6,-170.41634)"><text
+ xml:space="preserve"
+ style="font-size:14.34384632px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Denmark;-inkscape-font-specification:Bitstream Vera Sans"
+ x="175.09654"
+ y="220.46455"
+ id="text11435"
+ transform="scale(1.0495629,0.95277758)"><tspan
+ sodipodi:role="line"
+ id="tspan11437"
+ x="175.09654"
+ y="220.46455"
+ style="font-style:italic;font-weight:normal;letter-spacing:6.09533262;fill:#ff0000;-inkscape-font-specification:Bitstream Vera Sans Oblique">PROPOSED FOR</tspan></text>
+
+
+
+
+
+
+
+</g></g></g></g></g></g><g
+ style="display:inline"
+ inkscape:label="Unofficial Extension"
+ id="layer2"
+ inkscape:groupmode="layer"><g
+ id="g3016"
+ style="display:inline;overflow:visible"
+ transform="translate(2.7832904e-6,0)"><g
+ id="g3018"
+ style="display:inline;overflow:visible"
+ transform="translate(137.07948,-18.749054)"><g
+ id="g3020"
+ style="display:inline;overflow:visible"
+ transform="translate(2.8192073e-6,-170.41634)">
+
+
+
+</g></g></g></g><g
+ style="display:none"
+ inkscape:label="For Use With"
+ id="layer1"
+ inkscape:groupmode="layer"><g
+ id="g3010"><g
+ transform="translate(2.7832904e-6,0)"
+ style="display:inline;overflow:visible"
+ id="g2902"><g
+ transform="translate(2.8192073e-6,-170.41634)"
+ style="display:inline;overflow:visible"
+ id="g2904"><text
+ transform="scale(1.0495629,0.95277758)"
+ id="text2906"
+ y="220.46455"
+ x="182.23338"
+ style="font-size:14.34384632px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Denmark;-inkscape-font-specification:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ style="font-style:italic;font-weight:normal;letter-spacing:6.09533262;fill:#ff0000;-inkscape-font-specification:Bitstream Vera Sans Oblique"
+ y="220.46455"
+ x="182.23338"
+ id="tspan2908"
+ sodipodi:role="line">FOR USE WITH</tspan></text>
+
+
+
+
+
+
+
+</g></g></g></g><g
+ transform="translate(3.5916937e-8,-170.41634)"
+ style="display:none"
+ inkscape:label="Powered By"
+ id="layer6"
+ inkscape:groupmode="layer"><text
+ transform="scale(1.0495629,0.95277758)"
+ id="text11497"
+ y="220.46455"
+ x="203.05875"
+ style="font-size:14.34384632px;font-style:italic;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#4a6484;fill-opacity:1;stroke:none;display:inline;font-family:Denmark;-inkscape-font-specification:Bitstream Vera Sans"
+ xml:space="preserve"><tspan
+ style="font-style:italic;font-weight:normal;letter-spacing:6.09533262;fill:#4a6484;fill-opacity:1;-inkscape-font-specification:Bitstream Vera Sans Oblique"
+ y="220.46455"
+ x="203.05875"
+ id="tspan11499"
+ sodipodi:role="line">POWERED BY</tspan></text>
+
+
+
+
+
+
+
+
+
+</g></svg> \ No newline at end of file
diff --git a/attic/doc/src/images/workshop_atomic_updates_insertions.png b/attic/doc/src/images/workshop_atomic_updates_insertions.png
new file mode 100644
index 00000000..47e941b0
--- /dev/null
+++ b/attic/doc/src/images/workshop_atomic_updates_insertions.png
Binary files differ
diff --git a/attic/doc/src/images/workshop_atomic_updates_insertions_1.3.png b/attic/doc/src/images/workshop_atomic_updates_insertions_1.3.png
new file mode 100644
index 00000000..47e941b0
--- /dev/null
+++ b/attic/doc/src/images/workshop_atomic_updates_insertions_1.3.png
Binary files differ
diff --git a/attic/doc/src/images/workshop_atomic_updates_lookups.png b/attic/doc/src/images/workshop_atomic_updates_lookups.png
new file mode 100644
index 00000000..7e528192
--- /dev/null
+++ b/attic/doc/src/images/workshop_atomic_updates_lookups.png
Binary files differ
diff --git a/attic/doc/src/images/workshop_atomic_updates_lookups_1.3.png b/attic/doc/src/images/workshop_atomic_updates_lookups_1.3.png
new file mode 100644
index 00000000..7e528192
--- /dev/null
+++ b/attic/doc/src/images/workshop_atomic_updates_lookups_1.3.png
Binary files differ
diff --git a/attic/doc/src/images/workshop_naive_insertions.png b/attic/doc/src/images/workshop_naive_insertions.png
new file mode 100644
index 00000000..7bf03f44
--- /dev/null
+++ b/attic/doc/src/images/workshop_naive_insertions.png
Binary files differ
diff --git a/attic/doc/src/images/workshop_naive_insertions_1.3.png b/attic/doc/src/images/workshop_naive_insertions_1.3.png
new file mode 100644
index 00000000..7bf03f44
--- /dev/null
+++ b/attic/doc/src/images/workshop_naive_insertions_1.3.png
Binary files differ
diff --git a/attic/doc/src/images/workshop_naive_lookups.png b/attic/doc/src/images/workshop_naive_lookups.png
new file mode 100644
index 00000000..e01cf39d
--- /dev/null
+++ b/attic/doc/src/images/workshop_naive_lookups.png
Binary files differ
diff --git a/attic/doc/src/images/workshop_naive_lookups_1.3.png b/attic/doc/src/images/workshop_naive_lookups_1.3.png
new file mode 100644
index 00000000..e01cf39d
--- /dev/null
+++ b/attic/doc/src/images/workshop_naive_lookups_1.3.png
Binary files differ
diff --git a/attic/doc/workshop.xlsx b/attic/doc/workshop.xlsx
new file mode 100644
index 00000000..f29d1003
--- /dev/null
+++ b/attic/doc/workshop.xlsx
Binary files differ
diff --git a/attic/example/.clang-format b/attic/example/.clang-format
new file mode 100644
index 00000000..975edaa5
--- /dev/null
+++ b/attic/example/.clang-format
@@ -0,0 +1,57 @@
+---
+Language: Cpp
+AccessModifierOffset: -2
+ConstructorInitializerIndentWidth: 4
+AlignEscapedNewlinesLeft: false
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AlwaysBreakTemplateDeclarations: false
+AlwaysBreakBeforeMultilineStrings: false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Allman
+BreakBeforeTernaryOperators: false
+BreakConstructorInitializersBeforeComma: true
+BinPackParameters: true
+ColumnLimit: 85
+CommentPragmas: '^!<'
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ContinuationIndentWidth: 0
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IndentCaseLabels: false
+IndentFunctionDeclarationAfterType: false
+IndentWidth: 2
+IndentWrappedFunctionNames: false
+MaxEmptyLinesToKeep: 2
+KeepEmptyLinesAtTheStartOfBlocks: true
+NamespaceIndentation: All
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: false
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakString: 1000
+PenaltyBreakFirstLessLess: 120
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+Standard: Cpp11
+SpaceAfterCStyleCast: true
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: Never
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInParentheses: false
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInContainerLiterals: true
+TabWidth: 8
+UseTab: Never
+...
+
diff --git a/attic/example/adopt_example.cpp b/attic/example/adopt_example.cpp
new file mode 100644
index 00000000..b18e9827
--- /dev/null
+++ b/attic/example/adopt_example.cpp
@@ -0,0 +1,57 @@
+#include "afio_pch.hpp"
+
+//[adopt_example
+struct test_handle : boost::afio::handle
+{
+ test_handle(boost::afio::dispatcher *parent) :
+ boost::afio::handle(parent,
+ boost::afio::file_flags::none) {}
+ virtual void close() override final
+ {
+ // Do nothing
+ }
+ virtual handle::open_states is_open() const override final
+ {
+ return handle::open_states::open;
+ }
+ virtual void *native_handle() const override final
+ {
+ return nullptr;
+ }
+ virtual boost::afio::path path(bool refresh=false) override final
+ {
+ return boost::afio::path();
+ }
+ virtual boost::afio::path path() const override final
+ {
+ return boost::afio::path();
+ }
+ virtual boost::afio::directory_entry direntry(boost::afio::metadata_flags
+ wanted=boost::afio::directory_entry::metadata_fastpath()) override final
+ {
+ return boost::afio::directory_entry();
+ }
+ virtual boost::afio::path target() override final
+ {
+ return boost::afio::path();
+ }
+ virtual void link(const boost::afio::path_req &req) override final
+ {
+ }
+ virtual void unlink() override final
+ {
+ }
+ virtual void atomic_relink(const boost::afio::path_req &req) override final
+ {
+ }
+};
+
+int main(void)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ auto dispatcher = boost::afio::make_dispatcher().get();
+ current_dispatcher_guard h(dispatcher);
+ auto foreignh=std::make_shared<test_handle>(dispatcher.get());
+ return 0;
+}
+//]
diff --git a/attic/example/barrier_example.cpp b/attic/example/barrier_example.cpp
new file mode 100644
index 00000000..46a739f2
--- /dev/null
+++ b/attic/example/barrier_example.cpp
@@ -0,0 +1,67 @@
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ //[barrier_example
+ // Assume that groups is 10,000 items long with item.first being randomly
+ // between 1 and 500. This example is adapted from the barrier() unit test.
+ //
+ // What we're going to do is this: for each item in groups, schedule item.first
+ // parallel ops and a barrier which completes only when the last of that
+ // parallel group completes. Chain the next group to only execute after the
+ // preceding group's barrier completes. Repeat until all groups have been executed.
+ std::shared_ptr<boost::afio::dispatcher> dispatcher=
+ boost::afio::make_dispatcher().get();
+ std::vector<std::pair<size_t, int>> groups;
+ boost::afio::atomic<size_t> callcount[10000];
+ memset(&callcount, 0, sizeof(callcount));
+
+ // This lambda is what each parallel op in each group will do: increment an atomic
+ // for that group.
+ auto inccount = [](boost::afio::atomic<size_t> *count){ (*count)++; };
+
+ // This lambda is called after each barrier completes, and it checks that exactly
+ // the right number of inccount lambdas were executed.
+ auto verifybarrier = [](boost::afio::atomic<size_t> *count, size_t shouldbe)
+ {
+ if (*count != shouldbe)
+ throw std::runtime_error("Count was not what it should have been!");
+ return true;
+ };
+
+ // For each group, dispatch ops and a barrier for them
+ boost::afio::future<> next;
+ bool isfirst = true;
+ for(auto &run : groups)
+ {
+ // Create a vector of run.first size of bound inccount lambdas
+ // This will be the batch issued for this group
+ std::vector<std::function<void()>> thisgroupcalls(run.first, std::bind(inccount, &callcount[run.second]));
+ std::vector<boost::afio::future<>> thisgroupcallops;
+ // If this is the first item, schedule without precondition
+ if (isfirst)
+ {
+ thisgroupcallops = dispatcher->call(thisgroupcalls);
+ isfirst = false;
+ }
+ else
+ {
+ // Create a vector of run.first size of preconditions exactly
+ // matching the number in this batch. Note that the precondition
+ // for all of these is the preceding verify op
+ std::vector<boost::afio::future<>> dependency(run.first, next);
+ thisgroupcallops = dispatcher->call(dependency, thisgroupcalls);
+ }
+ // barrier() is very easy: its number of output ops exactly matches its input
+ // but none of the output will complete until the last of the input completes
+ auto thisgroupbarriered = dispatcher->barrier(thisgroupcallops);
+ // Schedule a call of the verify lambda once barrier completes. Here we choose
+ // the first item of the barrier's return, but in truth any of them are good.
+ auto verify = dispatcher->call(thisgroupbarriered.front(), std::function<bool()>(std::bind(verifybarrier, &callcount[run.second], run.first)));
+ // Set the dependency for the next batch to be the just scheduled verify op
+ next = verify;
+ }
+ // next was the last op scheduled, so waiting on it waits on everything
+ when_all_p(next).wait();
+ //]
+}
diff --git a/attic/example/benchmark_asio.cpp b/attic/example/benchmark_asio.cpp
new file mode 100644
index 00000000..45de6e45
--- /dev/null
+++ b/attic/example/benchmark_asio.cpp
@@ -0,0 +1,47 @@
+#include "afio_pch.hpp"
+
+/* My Intel Core i7 3770K running Windows 8 x64: 2591360 closures/sec
+ My Intel Core i7 3770K running Linux x64: 1611040 closures/sec (4 threads)
+*/
+
+static boost::afio::atomic<size_t> togo(0);
+static int callback()
+{
+#if 0
+ Sleep(0);
+#endif
+ --togo;
+ return 1;
+};
+int main(void)
+{
+ using namespace boost::afio;
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ auto threadpool=process_threadpool();
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<3);
+
+ atomic<size_t> threads(0);
+#if 0
+ std::cout << "Attach profiler now and hit Return" << std::endl;
+ getchar();
+#endif
+ begin=chrono::high_resolution_clock::now();
+#pragma omp parallel
+ {
+ ++threads;
+ for(size_t n=0; n<5000000; n++)
+ {
+ ++togo;
+ threadpool->enqueue(callback);
+ }
+ }
+ while(togo)
+ this_thread::sleep_for(chrono::milliseconds(1));
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to execute " << (5000000*threads) << " closures which is " << (5000000*threads/diff.count()) << " closures/sec" << std::endl;
+ std::cout << "\nPress Return to exit ..." << std::endl;
+ getchar();
+ return 0;
+}
diff --git a/attic/example/benchmark_atomic_log.cpp b/attic/example/benchmark_atomic_log.cpp
new file mode 100644
index 00000000..ab046837
--- /dev/null
+++ b/attic/example/benchmark_atomic_log.cpp
@@ -0,0 +1,521 @@
+#include "afio_pch.hpp"
+
+/* On my Win8.1 x86 laptop Intel i5 540M @ 2.53Ghz on NTFS Samsung SSD:
+
+Benchmarking traditional file locks with 2 concurrent writers ...
+Waiting for threads to exit ...
+For 2 concurrent writers, achieved 1720.7 attempts per second with a success rate of 1335.24 writes per second which is a 77.5984% success rate.
+
+Benchmarking file locks via atomic append with 2 concurrent writers ...
+Waiting for threads to exit ...
+For 2 concurrent writers, achieved 2413.66 attempts per second with a success rate of 790.364 writes per second which is a 32.7455% success rate.
+Traditional locks were 1.6894 times faster.
+
+Without file_flags::temporary_file | file_flags::delete_on_close, traditional file locks were:
+
+Benchmarking traditional file locks with 2 concurrent writers ...
+Waiting for threads to exit ...
+For 2 concurrent writers, achieved 1266.4 attempts per second with a success rate of 809.013 writes per second which is a 63.883% success rate.
+
+*/
+
+//[benchmark_atomic_log
+int main(int argc, const char *argv[])
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ using BOOST_AFIO_V2_NAMESPACE::off_t;
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ double traditional_locks=0, atomic_log_locks=0;
+ try { filesystem::remove_all("testdir"); } catch(...) {}
+
+ size_t totalwriters=2, writers=totalwriters;
+ if(argc>1)
+ writers=totalwriters=atoi(argv[1]);
+ {
+ auto dispatcher = make_dispatcher().get();
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ auto mkdir1(dispatcher->dir(path_req::relative(mkdir, "1", file_flags::create)));
+ auto mkdir2(dispatcher->dir(path_req::relative(mkdir, "2", file_flags::create)));
+ auto mkdir3(dispatcher->dir(path_req::relative(mkdir, "3", file_flags::create)));
+ auto mkdir4(dispatcher->dir(path_req::relative(mkdir, "4", file_flags::create)));
+ auto mkdir5(dispatcher->dir(path_req::relative(mkdir, "5", file_flags::create)));
+ auto mkdir6(dispatcher->dir(path_req::relative(mkdir, "6", file_flags::create)));
+ auto mkdir7(dispatcher->dir(path_req::relative(mkdir, "7", file_flags::create)));
+ auto mkdir8(dispatcher->dir(path_req::relative(mkdir, "8", file_flags::create)));
+ auto statfs_(dispatcher->statfs(mkdir, fs_metadata_flags::All));
+ auto statfs(statfs_.get());
+ std::cout << "The filing system holding our test directory is " << statfs.f_fstypename << " and has features:" << std::endl;
+#define PRINT_FIELD(field, ...) \
+ std::cout << " f_flags." #field ": "; std::cout << statfs.f_flags.field __VA_ARGS__ << std::endl
+ PRINT_FIELD(rdonly);
+ PRINT_FIELD(noexec);
+ PRINT_FIELD(nosuid);
+ PRINT_FIELD(acls);
+ PRINT_FIELD(xattr);
+ PRINT_FIELD(compression);
+ PRINT_FIELD(extents);
+ PRINT_FIELD(filecompression);
+#undef PRINT_FIELD
+#define PRINT_FIELD(field, ...) \
+ std::cout << " f_" #field ": "; std::cout << statfs.f_##field __VA_ARGS__ << std::endl
+ PRINT_FIELD(bsize);
+ PRINT_FIELD(iosize);
+ PRINT_FIELD(blocks, << " (" << (statfs.f_blocks*statfs.f_bsize / 1024.0 / 1024.0 / 1024.0) << " Gb)");
+ PRINT_FIELD(bfree, << " (" << (statfs.f_bfree*statfs.f_bsize / 1024.0 / 1024.0 / 1024.0) << " Gb)");
+ PRINT_FIELD(bavail, << " (" << (statfs.f_bavail*statfs.f_bsize / 1024.0 / 1024.0 / 1024.0) << " Gb)");
+#undef PRINT_FIELD
+ }
+ if(1)
+ {
+ std::cout << "\nBenchmarking a single traditional lock file with " << writers << " concurrent writers ...\n";
+ std::vector<thread> threads;
+ atomic<bool> done(true);
+ atomic<size_t> attempts(0), successes(0);
+ for(size_t n=0; n<writers; n++)
+ {
+ threads.push_back(thread([&done, &attempts, &successes, n]{
+ try
+ {
+ // Create a dispatcher
+ auto dispatcher = make_dispatcher().get();
+ // Schedule opening the log file for writing log entries
+ auto logfile(dispatcher->file(path_req("testdir/log",
+ file_flags::create | file_flags::read_write)));
+ // Retrieve any errors which occurred
+ logfile.get();
+ // Wait until all threads are ready
+ while(done) { this_thread::yield(); }
+ while(!done)
+ {
+ // Traditional file locks are very simple: try to exclusively create the lock file.
+ // If you succeed, you have the lock.
+ auto lockfile(dispatcher->file(path_req("testdir/log.lock",
+ file_flags::create_only_if_not_exist | file_flags::write | file_flags::temporary_file | file_flags::delete_on_close)));
+ attempts.fetch_add(1, memory_order_relaxed);
+ // v1.4 of the AFIO engine will return error_code instead of exceptions for this
+ try { lockfile.get(); } catch(const system_error &e) { continue; }
+ std::string logentry("I am log writer "), mythreadid(to_string(n)), logentryend("!\n");
+ // Fetch the size
+ off_t where=logfile->lstat().st_size, entrysize=logentry.size()+mythreadid.size()+logentryend.size();
+ // Schedule extending the log file
+ auto extendlog(dispatcher->truncate(logfile, where+entrysize));
+ // Schedule writing the new entry
+ auto writetolog(dispatcher->write(make_io_req(extendlog, {logentry, mythreadid, logentryend}, where)));
+ writetolog.get();
+ extendlog.get();
+ successes.fetch_add(1, memory_order_relaxed);
+ }
+ }
+ catch(const system_error &e) { std::cerr << "ERROR: test exits via system_error code " << e.code().value() << "(" << e.what() << ")" << std::endl; abort(); }
+ catch(const std::exception &e) { std::cerr << "ERROR: test exits via exception (" << e.what() << ")" << std::endl; abort(); }
+ catch(...) { std::cerr << "ERROR: test exits via unknown exception" << std::endl; abort(); }
+ }));
+ }
+ auto begin=chrono::high_resolution_clock::now();
+ done=false;
+ std::this_thread::sleep_for(std::chrono::seconds(20));
+ done=true;
+ std::cout << "Waiting for threads to exit ..." << std::endl;
+ for(auto &i : threads)
+ i.join();
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "For " << writers << " concurrent writers, achieved " << (attempts/diff.count()) << " attempts per second with a "
+ "success rate of " << (successes/diff.count()) << " writes per second which is a " << (100.0*successes/attempts) << "% success rate." << std::endl;
+ traditional_locks=successes/diff.count();
+ }
+
+ if(1)
+ {
+ std::cout << "\nBenchmarking eight traditional lock files with " << writers << " concurrent writers ...\n";
+ std::vector<thread> threads;
+ atomic<bool> done(true);
+ atomic<size_t> attempts(0), successes(0);
+ for(size_t n=0; n<writers; n++)
+ {
+ threads.push_back(thread([&done, &attempts, &successes, n]{
+ try
+ {
+ // Create a dispatcher
+ auto dispatcher = make_dispatcher().get();
+ // Schedule opening the log file for writing log entries
+ auto logfile(dispatcher->file(path_req("testdir/log",
+ file_flags::create | file_flags::read_write)));
+ // Retrieve any errors which occurred
+ logfile.get();
+ // Wait until all threads are ready
+ while(done) { this_thread::yield(); }
+ while(!done)
+ {
+ // Parallel try to exclusively create all eight lock files
+ std::vector<path_req> lockfiles; lockfiles.reserve(8);
+ lockfiles.push_back(path_req("testdir/1/log.lock",
+ file_flags::create_only_if_not_exist | file_flags::write | file_flags::temporary_file | file_flags::delete_on_close));
+ lockfiles.push_back(path_req("testdir/2/log.lock",
+ file_flags::create_only_if_not_exist | file_flags::write | file_flags::temporary_file | file_flags::delete_on_close));
+ lockfiles.push_back(path_req("testdir/3/log.lock",
+ file_flags::create_only_if_not_exist | file_flags::write | file_flags::temporary_file | file_flags::delete_on_close));
+ lockfiles.push_back(path_req("testdir/4/log.lock",
+ file_flags::create_only_if_not_exist | file_flags::write | file_flags::temporary_file | file_flags::delete_on_close));
+ lockfiles.push_back(path_req("testdir/5/log.lock",
+ file_flags::create_only_if_not_exist | file_flags::write | file_flags::temporary_file | file_flags::delete_on_close));
+ lockfiles.push_back(path_req("testdir/6/log.lock",
+ file_flags::create_only_if_not_exist | file_flags::write | file_flags::temporary_file | file_flags::delete_on_close));
+ lockfiles.push_back(path_req("testdir/7/log.lock",
+ file_flags::create_only_if_not_exist | file_flags::write | file_flags::temporary_file | file_flags::delete_on_close));
+ lockfiles.push_back(path_req("testdir/8/log.lock",
+ file_flags::create_only_if_not_exist | file_flags::write | file_flags::temporary_file | file_flags::delete_on_close));
+ auto lockfile(dispatcher->file(lockfiles));
+ attempts.fetch_add(1, memory_order_relaxed);
+#if 1
+ // v1.4 of the AFIO engine will return error_code instead of exceptions for this
+ try { lockfile[7].get(); } catch(const system_error &e) { continue; }
+ try { lockfile[6].get(); } catch(const system_error &e) { continue; }
+ try { lockfile[5].get(); } catch(const system_error &e) { continue; }
+ try { lockfile[4].get(); } catch(const system_error &e) { continue; }
+ try { lockfile[3].get(); } catch(const system_error &e) { continue; }
+ try { lockfile[2].get(); } catch(const system_error &e) { continue; }
+ try { lockfile[1].get(); } catch(const system_error &e) { continue; }
+ try { lockfile[0].get(); } catch(const system_error &e) { continue; }
+#else
+ try
+ {
+ auto barrier(dispatcher->barrier(lockfile));
+ // v1.4 of the AFIO engine will return error_code instead of exceptions for this
+ for(size_t n=0; n<8; n++)
+ barrier[n].get();
+ }
+ catch(const system_error &e) { continue; }
+#endif
+ std::string logentry("I am log writer "), mythreadid(to_string(n)), logentryend("!\n");
+ // Fetch the size
+ off_t where=logfile->lstat().st_size, entrysize=logentry.size()+mythreadid.size()+logentryend.size();
+ // Schedule extending the log file
+ auto extendlog(dispatcher->truncate(logfile, where+entrysize));
+ // Schedule writing the new entry
+ auto writetolog(dispatcher->write(make_io_req(extendlog, {logentry, mythreadid, logentryend}, where)));
+ // Fetch errors from the last operation first to avoid sleep-wake cycling
+ writetolog.get();
+ extendlog.get();
+ successes.fetch_add(1, memory_order_relaxed);
+ }
+ }
+ catch(const system_error &e) { std::cerr << "ERROR: test exits via system_error code " << e.code().value() << "(" << e.what() << ")" << std::endl; abort(); }
+ catch(const std::exception &e) { std::cerr << "ERROR: test exits via exception (" << e.what() << ")" << std::endl; abort(); }
+ catch(...) { std::cerr << "ERROR: test exits via unknown exception" << std::endl; abort(); }
+ }));
+ }
+ auto begin=chrono::high_resolution_clock::now();
+ done=false;
+ std::this_thread::sleep_for(std::chrono::seconds(20));
+ done=true;
+ std::cout << "Waiting for threads to exit ..." << std::endl;
+ for(auto &i : threads)
+ i.join();
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "For " << writers << " concurrent writers, achieved " << (attempts/diff.count()) << " attempts per second with a "
+ "success rate of " << (successes/diff.count()) << " writes per second which is a " << (100.0*successes/attempts) << "% success rate." << std::endl;
+ }
+
+ // **** WARNING UNSUPPORTED UNDOCUMENTED API DO NOT USE IN YOUR CODE ****
+ if(1)
+ {
+ std::cout << "\nBenchmarking a ranged file lock with " << writers << " concurrent writers ...\n";
+ std::vector<thread> threads;
+ atomic<bool> done(true);
+ atomic<size_t> attempts(0), successes(0);
+ for(size_t n=0; n<writers; n++)
+ {
+ threads.push_back(thread([&done, &attempts, &successes, n]{
+ try
+ {
+ // Create a dispatcher
+ auto dispatcher = make_dispatcher().get();
+ // Schedule opening the log file for writing log entries
+ auto logfile(dispatcher->file(path_req("testdir/log",
+ file_flags::create | file_flags::read_write | file_flags::os_lockable)));
+ // Retrieve any errors which occurred
+ logfile.get();
+ // Wait until all threads are ready
+ while(done) { this_thread::yield(); }
+ while(!done)
+ {
+ attempts.fetch_add(1, memory_order_relaxed);
+ // **** WARNING UNSUPPORTED UNDOCUMENTED API DO NOT USE IN YOUR CODE ****
+ dispatcher->lock({logfile}).front().get();
+ std::string logentry("I am log writer "), mythreadid(to_string(n)), logentryend("!\n");
+ // Fetch the size
+ off_t where=logfile->lstat().st_size, entrysize=logentry.size()+mythreadid.size()+logentryend.size();
+ // Schedule extending the log file
+ auto extendlog(dispatcher->truncate(logfile, where+entrysize));
+ // Schedule writing the new entry
+ auto writetolog(dispatcher->write(make_io_req(extendlog, {logentry, mythreadid, logentryend}, where)));
+ writetolog.get();
+ extendlog.get();
+ successes.fetch_add(1, memory_order_relaxed);
+ dispatcher->lock({{logfile, nullptr}}).front().get();
+ }
+ }
+ catch(const system_error &e) { std::cerr << "ERROR: test exits via system_error code " << e.code().value() << "(" << e.what() << ")" << std::endl; abort(); }
+ catch(const std::exception &e) { std::cerr << "ERROR: test exits via exception (" << e.what() << ")" << std::endl; abort(); }
+ catch(...) { std::cerr << "ERROR: test exits via unknown exception" << std::endl; abort(); }
+ }));
+ }
+ auto begin=chrono::high_resolution_clock::now();
+ done=false;
+ std::this_thread::sleep_for(std::chrono::seconds(20));
+ done=true;
+ std::cout << "Waiting for threads to exit ..." << std::endl;
+ for(auto &i : threads)
+ i.join();
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "For " << writers << " concurrent writers, achieved " << (attempts/diff.count()) << " attempts per second with a "
+ "success rate of " << (successes/diff.count()) << " writes per second which is a " << (100.0*successes/attempts) << "% success rate." << std::endl;
+ }
+
+ if(1)
+ {
+ std::cout << "\nBenchmarking file locks via atomic append with " << writers << " concurrent writers ...\n";
+ std::vector<thread> threads;
+ atomic<bool> done(true);
+ atomic<size_t> attempts(0), successes(0);
+ for(size_t thread=0; thread<writers; thread++)
+ {
+ threads.push_back(std::thread([&done, &attempts, &successes, thread]{
+ try
+ {
+ // Create a dispatcher
+ auto dispatcher = make_dispatcher().get();
+ // Schedule opening the log file for writing log entries
+ auto logfile(dispatcher->file(path_req("testdir/log",
+ file_flags::create | file_flags::read_write)));
+ // Schedule opening the lock file for scanning and hole punching
+ auto lockfilez(dispatcher->file(path_req("testdir/log.lock",
+ file_flags::create | file_flags::read_write)));
+ // Schedule opening the lock file for atomic appending
+ auto lockfilea(dispatcher->file(path_req("testdir/log.lock",
+ file_flags::create | file_flags::write | file_flags::append)));
+ // Retrieve any errors which occurred
+ lockfilea.get(); lockfilez.get(); logfile.get();
+ while(!done)
+ {
+ // Each lock log entry is 16 bytes in length.
+ enum class message_code_t : uint8_t
+ {
+ unlock=0,
+ havelock=1,
+ rescind=2,
+ interest=3,
+ nominate=5
+ };
+#pragma pack(push, 1)
+ union message_t
+ {
+ char bytes[16];
+ struct
+ {
+ message_code_t code;
+ char __padding1[3];
+ uint32_t timestamp; // time_t
+ uint64_t uniqueid;
+ };
+ };
+#pragma pack(pop)
+ static_assert(sizeof(message_t)==16, "message_t is not 16 bytes long!");
+ auto gettime=[]{ return (uint32_t)(std::time(nullptr)-1420070400UL/* 1st Jan 2015*/); };
+ message_t temp, buffers[256];
+ off_t buffersoffset;
+ uint32_t nowtimestamp=gettime();
+ // TODO FIXME: If multiple machines are all accessing the lock file, nowtimestamp
+ // ought to be corrected for drift
+
+ // Step 1: Register my interest
+ memset(temp.bytes, 0, sizeof(temp));
+ temp.code=message_code_t::interest;
+ temp.timestamp=nowtimestamp;
+ temp.uniqueid=thread; // TODO FIXME: Needs to be a VERY random number to prevent collision.
+ dispatcher->write(make_io_req(lockfilea, temp.bytes, sizeof(temp), 0)).get();
+
+ // Step 2: Wait until my interest message appears, also figure out what interests precede
+ // mine and where my start of interest begins, and if someone currently has the lock
+ off_t startofinterest=dispatcher->extents(lockfilez).get().front().first;
+ off_t myuniqueid=(off_t)-1;
+ bool findPreceding=true;
+ std::vector<std::pair<bool, off_t>> preceding;
+ std::pair<bool, off_t> lockid;
+ auto iterate=[&]{
+ size_t validPrecedingCount=0;
+ off_t lockfilesize=lockfilez->lstat(metadata_flags::size).st_size;
+ buffersoffset=lockfilesize>sizeof(buffers) ? lockfilesize-sizeof(buffers) : 0;
+ //buffersoffset-=buffersoffset % sizeof(buffers[0]);
+ for(; !validPrecedingCount && buffersoffset>=startofinterest && buffersoffset<lockfilesize; buffersoffset-=sizeof(buffers))
+ {
+ size_t amount=(size_t)(lockfilesize-buffersoffset);
+ if(amount>sizeof(buffers))
+ amount=sizeof(buffers);
+ dispatcher->read(make_io_req(lockfilez, (void *) buffers, amount, buffersoffset)).get();
+ for(size_t n=amount/sizeof(buffers[0])-1; !validPrecedingCount && n<amount/sizeof(buffers[0]); n--)
+ {
+ // Early exit if messages have become stale
+ if(!buffers[n].timestamp || (buffers[n].timestamp<nowtimestamp && nowtimestamp-buffers[n].timestamp>20))
+ {
+ startofinterest=buffersoffset+n*sizeof(buffers[0]);
+ break;
+ }
+ // Find if he is locked or unlocked
+ if(lockid.second==(off_t) -1)
+ {
+ if(buffers[n].code==message_code_t::unlock)
+ lockid=std::make_pair(false, buffers[n].uniqueid);
+ else if(buffers[n].code==message_code_t::havelock)
+ lockid=std::make_pair(true, buffers[n].uniqueid);
+ }
+ // Am I searching for my interest?
+ if(myuniqueid==(off_t)-1)
+ {
+ if(!memcmp(buffers+n, &temp, sizeof(temp)))
+ myuniqueid=buffersoffset+n*sizeof(buffers[0]);
+ }
+ else if(findPreceding && (buffers[n].uniqueid<myuniqueid || buffersoffset+n*sizeof(buffers[0])<myuniqueid))
+ {
+ // We are searching for preceding claims now
+ if(buffers[n].code==message_code_t::rescind || buffers[n].code==message_code_t::unlock)
+ preceding.push_back(std::make_pair(false, buffers[n].uniqueid));
+ else if(buffers[n].code==message_code_t::nominate || buffers[n].code==message_code_t::havelock)
+ {
+ if(buffers[n].uniqueid<myuniqueid && preceding.end()==std::find(preceding.begin(), preceding.end(), std::make_pair(false, (off_t) buffers[n].uniqueid)))
+ {
+ preceding.push_back(std::make_pair(true, buffers[n].uniqueid));
+ validPrecedingCount++;
+ }
+ }
+ else if(buffers[n].code==message_code_t::interest)
+ {
+ if(buffersoffset+n*sizeof(buffers[0])<myuniqueid && preceding.end()==std::find(preceding.begin(), preceding.end(), std::make_pair(false, buffersoffset+n*sizeof(buffers[0]))))
+ {
+ preceding.push_back(std::make_pair(true, buffersoffset+n*sizeof(buffers[0])));
+ validPrecedingCount++;
+ }
+ }
+ }
+ }
+ }
+#if 0
+ std::cout << thread << ": myuniqueid=" << myuniqueid << " startofinterest=" << startofinterest << " size=" << lockfilez->lstat(metadata_flags::size).st_size << " lockid=" << lockid.first << "," << lockid.second << " preceding=";
+ for(auto &i : preceding)
+ std::cout << i.first << "," << i.second << ";";
+ std::cout << std::endl;
+#endif
+ if(findPreceding)
+ {
+ // Remove all rescinded interests preceding ours
+ preceding.erase(std::remove_if(preceding.begin(), preceding.end(), [](const std::pair<bool, off_t> &i){ return !i.first; }), preceding.end());
+ std::sort(preceding.begin(), preceding.end());
+ findPreceding=false;
+ }
+ };
+ do
+ {
+ lockid=std::make_pair(false, (off_t)-1);
+ iterate();
+ // Didn't find it, so sleep and retry, maybe st_size will have updated by then
+ if(myuniqueid==(off_t)-1) this_thread::sleep_for(chrono::milliseconds(1));
+ } while(myuniqueid==(off_t)-1);
+
+ // Step 3: If there is no lock and no interest precedes mine, claim the mutex. Else issue a nominate for myself,
+ // once per ten seconds.
+ {
+ nowtimestamp=0;
+ mutex m;
+ condition_variable c;
+ unique_lock<decltype(m)> l(m);
+ atomic<bool> fileChanged(false);
+ for(;;)
+ {
+ attempts.fetch_add(1, memory_order_relaxed);
+ temp.timestamp=gettime();
+ temp.uniqueid=myuniqueid;
+ if(preceding.empty())
+ {
+ temp.code=message_code_t::havelock;
+ dispatcher->write(make_io_req(lockfilea, temp.bytes, sizeof(temp), 0)).get();
+ // Zero the range between startofinterest and myuniqueid
+ if(startofinterest<myuniqueid)
+ {
+ std::vector<std::pair<off_t, off_t>> range={{startofinterest, myuniqueid-startofinterest}};
+ dispatcher->zero(lockfilez, range).get();
+ // std::cout << thread << ": lock taken for myuniqueid=" << myuniqueid << ", zeroing " << range.front().first << ", " << range.front().second << std::endl;
+ }
+ break;
+ }
+ else
+ {
+ auto lockfilechanged=[&]{
+ fileChanged=true;
+ c.notify_all();
+ };
+ // TODO FIXME: Put a modify watch on the lockfile instead of spinning
+ if(temp.timestamp-nowtimestamp>=10)
+ {
+ temp.code=message_code_t::nominate;
+ dispatcher->write(make_io_req(lockfilea, temp.bytes, sizeof(temp), 0)).get();
+ nowtimestamp=temp.timestamp;
+ }
+ //c.wait_for(l, chrono::milliseconds(1), [&fileChanged]{ return fileChanged==true; });
+ fileChanged=false;
+ preceding.clear();
+ findPreceding=true;
+ lockid=std::make_pair(false, (off_t)-1);
+ iterate();
+ }
+ }
+ }
+
+ // Step 4: I now have the lock, so do my thing
+ std::string logentry("I am log writer "), mythreadid(to_string(thread)), logentryend("!\n");
+ // Fetch the size
+ off_t where=logfile->lstat().st_size, entrysize=logentry.size()+mythreadid.size()+logentryend.size();
+ // Schedule extending the log file
+ auto extendlog(dispatcher->truncate(logfile, where+entrysize));
+ // Schedule writing the new entry
+ auto writetolog(dispatcher->write(make_io_req(extendlog, {logentry, mythreadid, logentryend}, where)));
+ // Fetch errors from the last operation first to avoid sleep-wake cycling
+ writetolog.get();
+ extendlog.get();
+ successes.fetch_add(1, memory_order_relaxed);
+// std::cout << thread << ": doing work for myuniqueid=" << myuniqueid << std::endl;
+// this_thread::sleep_for(chrono::milliseconds(250));
+
+ // Step 5: Release the lock
+ temp.code=message_code_t::unlock;
+ temp.timestamp=gettime();
+ temp.uniqueid=myuniqueid;
+// std::cout << thread << ": lock released for myuniqueid=" << myuniqueid << std::endl;
+ dispatcher->write(make_io_req(lockfilea, temp.bytes, sizeof(temp), 0)).get();
+ }
+ }
+ catch(const system_error &e) { std::cerr << "ERROR: test exits via system_error code " << e.code().value() << "(" << e.what() << ")" << std::endl; abort(); }
+ catch(const std::exception &e) { std::cerr << "ERROR: test exits via exception (" << e.what() << ")" << std::endl; abort(); }
+ catch(...) { std::cerr << "ERROR: test exits via unknown exception" << std::endl; abort(); }
+ }));
+ }
+ auto begin=chrono::high_resolution_clock::now();
+ done=false;
+ std::this_thread::sleep_for(std::chrono::seconds(20));
+ done=true;
+ std::cout << "Waiting for threads to exit ..." << std::endl;
+ for(auto &i : threads)
+ i.join();
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "For " << writers << " concurrent writers, achieved " << (attempts/diff.count()) << " attempts per second with a "
+ "success rate of " << (successes/diff.count()) << " writes per second which is a " << (100.0*successes/attempts) << "% success rate." << std::endl;
+ atomic_log_locks=successes/diff.count();
+ }
+ filesystem::remove_all("testdir");
+ std::cout << "Traditional locks were " << (traditional_locks/atomic_log_locks) << " times faster." << std::endl;
+ return 0;
+}
+//]
diff --git a/attic/example/benchmark_chained1.cpp b/attic/example/benchmark_chained1.cpp
new file mode 100644
index 00000000..f40bf1c9
--- /dev/null
+++ b/attic/example/benchmark_chained1.cpp
@@ -0,0 +1,48 @@
+#include "afio_pch.hpp"
+
+/* My Intel Core i7 3770K running Windows 8 x64: 726124 closures/sec
+ My Intel Core i7 3770K running Linux x64: 968005 closures/sec
+*/
+
+static std::pair<bool, std::shared_ptr<boost::afio::handle>> _callback(size_t, boost::afio::future<> op)
+{
+#if 0
+ // Simulate an i/o op with a context switch
+ Sleep(0);
+#endif
+ return std::make_pair(true, op.get_handle());
+};
+
+int main(void)
+{
+ using namespace boost::afio;
+ auto dispatcher=make_dispatcher().get();
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<3);
+
+ std::pair<async_op_flags, dispatcher::completion_t *> callback(async_op_flags::none, _callback);
+ atomic<size_t> threads(0);
+#if 0
+ std::cout << "Attach profiler now and hit Return" << std::endl;
+ getchar();
+#endif
+ begin=chrono::high_resolution_clock::now();
+#pragma omp parallel
+ {
+ future<> last;
+ threads++;
+ for(size_t n=0; n<500000; n++)
+ {
+ last=dispatcher->completion(last, callback);
+ }
+ }
+ while(dispatcher->wait_queue_depth())
+ this_thread::sleep_for(chrono::milliseconds(1));
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to execute " << (500000*threads) << " closures which is " << (500000*threads/diff.count()) << " chained closures/sec" << std::endl;
+ std::cout << "\nPress Return to exit ..." << std::endl;
+ getchar();
+ return 0;
+}
diff --git a/attic/example/benchmark_chained2.cpp b/attic/example/benchmark_chained2.cpp
new file mode 100644
index 00000000..d410076c
--- /dev/null
+++ b/attic/example/benchmark_chained2.cpp
@@ -0,0 +1,43 @@
+#include "afio_pch.hpp"
+
+/* My Intel Core i7 3770K running Windows 8 x64: 596318 closures/sec
+ My Intel Core i7 3770K running Linux x64: 794384 closures/sec
+*/
+
+static int callback()
+{
+#if 0
+ Sleep(0);
+#endif
+ return 1;
+};
+
+int main(void)
+{
+ using namespace boost::afio;
+ auto dispatcher=make_dispatcher().get();
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<3);
+
+ atomic<size_t> threads(0);
+ std::vector<std::function<int()>> callbacks(1, callback);
+ begin=chrono::high_resolution_clock::now();
+#pragma omp parallel
+ {
+ std::vector<future<>> preconditions(1);
+ threads++;
+ for(size_t n=0; n<500000; n++)
+ {
+ preconditions.front()=dispatcher->call(preconditions, callbacks).front();
+ }
+ }
+ while(dispatcher->wait_queue_depth())
+ this_thread::sleep_for(chrono::milliseconds(1));
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to execute " << (500000*threads) << " closures which is " << (500000*threads/diff.count()) << " chained closures/sec" << std::endl;
+ std::cout << "\nPress Return to exit ..." << std::endl;
+ getchar();
+ return 0;
+}
diff --git a/attic/example/benchmark_latency.cpp b/attic/example/benchmark_latency.cpp
new file mode 100644
index 00000000..f11422fd
--- /dev/null
+++ b/attic/example/benchmark_latency.cpp
@@ -0,0 +1,162 @@
+#include "afio_pch.hpp"
+#include <thread>
+
+#define ITERATIONS 10000
+#define CONCURRENCY 32
+
+// Optional
+//#define MULTIPLIER 1000000 // output number of microseconds instead of seconds
+#define MULTIPLIER 3900000000ULL // output number of CPU clocks instead of seconds
+
+typedef decltype(boost::afio::chrono::high_resolution_clock::now()) time_point;
+size_t id_offset;
+static time_point points[100000];
+static time_point::duration overhead, timesliceoverhead, sleepoverhead;
+static std::pair<bool, std::shared_ptr<boost::afio::handle>> _callback(size_t id, boost::afio::future<> op)
+{
+ using namespace boost::afio;
+ points[id-id_offset]=chrono::high_resolution_clock::now();
+ return std::make_pair(true, op.get_handle());
+};
+
+int main(void)
+{
+ using namespace boost::afio;
+ auto dispatcher=make_dispatcher().get();
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ {
+ size_t total1=0, total2=0, total3=0;
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<1)
+ {
+ auto now2=chrono::high_resolution_clock::now();
+ this_thread::yield();
+ auto now3=chrono::high_resolution_clock::now();
+ timesliceoverhead+=now3-now2;
+ total1++;
+ }
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<2)
+ {
+ auto now2=chrono::high_resolution_clock::now();
+ this_thread::sleep_for(chrono::nanoseconds(1));
+ auto now3=chrono::high_resolution_clock::now();
+ sleepoverhead+=now3-now2;
+ total3++;
+ }
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<3)
+ {
+ auto now1=chrono::high_resolution_clock::now();
+ auto now2=chrono::high_resolution_clock::now();
+ overhead+=now2-now1;
+ total2++;
+ }
+ overhead=time_point::duration(overhead.count()/total2);
+ sleepoverhead=time_point::duration(sleepoverhead.count()/total3);
+ timesliceoverhead=time_point::duration(timesliceoverhead.count()/total1);
+ std::cout << "Timing overhead is calculated to be " << chrono::duration_cast<secs_type>(overhead).count() << " seconds." << std::endl;
+ std::cout << "OS context switch overhead is calculated to be " << chrono::duration_cast<secs_type>(timesliceoverhead).count() << " seconds." << std::endl;
+ std::cout << "OS sleep overhead is calculated to be " << chrono::duration_cast<secs_type>(sleepoverhead).count() << " seconds." << std::endl;
+ }
+
+ std::pair<async_op_flags, dispatcher::completion_t *> callback(async_op_flags::none, _callback);
+ std::ofstream csv("afio_latencies.csv");
+ csv << "Timing overhead is calculated to be," << chrono::duration_cast<secs_type>(overhead).count()
+#ifdef MULTIPLIER
+ * MULTIPLIER
+#endif
+ << std::endl;
+ csv << "OS context switch overhead is calculated to be," << chrono::duration_cast<secs_type>(timesliceoverhead).count()
+#ifdef MULTIPLIER
+ * MULTIPLIER
+#endif
+ << std::endl << std::endl;
+ csv << "OS sleep overhead is calculated to be," << chrono::duration_cast<secs_type>(sleepoverhead).count()
+#ifdef MULTIPLIER
+ * MULTIPLIER
+#endif
+ << std::endl << std::endl;
+ csv << "Concurrency,Handler Min,Handler Max,Handler Average,Handler Stddev,Complete Min,Complete Max,Complete Average,Complete Stddev" << std::endl;
+ for(size_t concurrency=0; concurrency<CONCURRENCY; concurrency++)
+ {
+ future<> last[CONCURRENCY];
+ time_point begin[CONCURRENCY], handled[CONCURRENCY], end[CONCURRENCY];
+ atomic<bool> waiter;
+ double handler[ITERATIONS], complete[ITERATIONS];
+ std::vector<std::thread> threads;
+ threads.reserve(CONCURRENCY);
+ size_t iterations=(concurrency>=8) ? ITERATIONS/10 : ITERATIONS;
+ std::cout << "Running " << iterations << " iterations of concurrency " << concurrency+1 << " ..." << std::endl;
+ for(size_t n=0; n<iterations; n++)
+ {
+ threads.clear();
+ waiter.store(true);
+ atomic<size_t> threads_ready(0);
+ for(size_t c=0; c<=concurrency; c++)
+ {
+ threads.push_back(std::thread([&, c]{
+ ++threads_ready;
+ while(waiter)
+#ifdef BOOST_SMT_PAUSE
+ BOOST_SMT_PAUSE
+#endif
+ ;
+ begin[c]=chrono::high_resolution_clock::now();
+ last[c]=dispatcher->completion(future<>(), callback);
+ last[c].get();
+ end[c]=chrono::high_resolution_clock::now();
+ handled[c]=points[last[c].id()-id_offset];
+ }));
+ }
+ while(threads_ready<=concurrency)
+#ifdef BOOST_SMT_PAUSE
+ BOOST_SMT_PAUSE
+#endif
+ ;
+ waiter.store(false);
+ for(auto &i: threads)
+ i.join();
+ for(size_t c=0; c<=concurrency; c++)
+ {
+ handler[n]=chrono::duration_cast<secs_type>(handled[c]-begin[c]-overhead).count();
+ complete[n]=chrono::duration_cast<secs_type>(end[c]-handled[c]-overhead).count();
+ }
+ }
+ for(size_t n=0; n<=concurrency; n++)
+ if(last[n].id()>id_offset) id_offset=last[n].id();
+ double minHandler=1<<30, maxHandler=0, totalHandler=0, minComplete=1<<30, maxComplete=0, totalComplete=0;
+ for(size_t n=0; n<iterations; n++)
+ {
+ if(handler[n]<minHandler) minHandler=handler[n];
+ if(handler[n]>maxHandler) maxHandler=handler[n];
+ if(complete[n]<minHandler) minComplete=complete[n];
+ if(complete[n]>maxHandler) maxComplete=complete[n];
+ totalHandler+=handler[n];
+ totalComplete+=complete[n];
+ }
+ totalHandler/=iterations;
+ totalComplete/=iterations;
+ double varianceHandler=0, varianceComplete=0;
+ for(size_t n=0; n<iterations; n++)
+ {
+ varianceHandler+=pow(handler[n]-totalHandler, 2);
+ varianceComplete+=pow(complete[n]-totalComplete, 2);
+ }
+ varianceHandler/=iterations;
+ varianceComplete/=iterations;
+ varianceHandler=sqrt(varianceHandler);
+ varianceComplete=sqrt(varianceComplete);
+#ifdef MULTIPLIER
+ minHandler*=MULTIPLIER;
+ maxHandler*=MULTIPLIER;
+ totalHandler*=MULTIPLIER;
+ varianceHandler*=MULTIPLIER;
+ minComplete*=MULTIPLIER;
+ maxComplete*=MULTIPLIER;
+ totalComplete*=MULTIPLIER;
+ varianceComplete*=MULTIPLIER;
+#endif
+ csv << concurrency+1 << "," << minHandler << "," << maxHandler << "," << totalHandler << "," << varianceHandler << ","
+ << minComplete << "," << maxComplete << "," << totalComplete << "," << varianceComplete << std::endl;
+ }
+ return 0;
+}
diff --git a/attic/example/benchmark_unchained1.cpp b/attic/example/benchmark_unchained1.cpp
new file mode 100644
index 00000000..2f9b6769
--- /dev/null
+++ b/attic/example/benchmark_unchained1.cpp
@@ -0,0 +1,43 @@
+#include "afio_pch.hpp"
+
+/* My Intel Core i7 3770K running Windows 8 x64: 1555990 closures/sec
+ My Intel Core i7 3770K running Linux x64: 1432810 closures/sec
+*/
+
+static std::pair<bool, std::shared_ptr<boost::afio::handle>> callback(size_t, boost::afio::future<> op)
+{
+#if 0
+ // Simulate an i/o op with a context switch
+ Sleep(0);
+#endif
+ return std::make_pair(true, op.get_handle());
+};
+
+int main(void)
+{
+ using namespace boost::afio;
+ auto dispatcher=make_dispatcher().get();
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<3);
+
+ std::vector<future<>> preconditions;
+ std::vector<std::pair<async_op_flags, dispatcher::completion_t *>> callbacks(1,
+ std::make_pair(async_op_flags::none, callback));
+#if 0
+ std::cout << "Attach profiler now and hit Return" << std::endl;
+ getchar();
+#endif
+ begin=chrono::high_resolution_clock::now();
+#pragma omp parallel for
+ for(int n=0; n<5000000; n++)
+ dispatcher->completion(preconditions, callbacks);
+ while(dispatcher->wait_queue_depth())
+ this_thread::sleep_for(chrono::milliseconds(1));
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to execute 5,000,000 closures which is " << (5000000/diff.count()) << " unchained closures/sec" << std::endl;
+ std::cout << "\nPress Return to exit ..." << std::endl;
+ getchar();
+ return 0;
+}
diff --git a/attic/example/benchmark_unchained2.cpp b/attic/example/benchmark_unchained2.cpp
new file mode 100644
index 00000000..db9affe0
--- /dev/null
+++ b/attic/example/benchmark_unchained2.cpp
@@ -0,0 +1,37 @@
+#include "afio_pch.hpp"
+
+/* My Intel Core i7 3770K running Windows 8 x64: 911963 closures/sec
+ My Intel Core i7 3770K running Linux x64: 1094780 closures/sec
+*/
+
+static int callback()
+{
+#if 0
+ Sleep(0);
+#endif
+ return 1;
+};
+
+int main(void)
+{
+ using namespace boost::afio;
+ auto dispatcher=make_dispatcher().get();
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<3);
+
+ std::vector<future<>> preconditions;
+ std::vector<std::function<int()>> callbacks(1, callback);
+ begin=chrono::high_resolution_clock::now();
+#pragma omp parallel for
+ for(int n=0; n<5000000; n++)
+ dispatcher->call(preconditions, callbacks);
+ while(dispatcher->wait_queue_depth())
+ this_thread::sleep_for(chrono::milliseconds(1));
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to execute 5,000,000 closures which is " << (5000000/diff.count()) << " unchained closures/sec" << std::endl;
+ std::cout << "\nPress Return to exit ..." << std::endl;
+ getchar();
+ return 0;
+}
diff --git a/attic/example/call_example.cpp b/attic/example/call_example.cpp
new file mode 100644
index 00000000..d4a72642
--- /dev/null
+++ b/attic/example/call_example.cpp
@@ -0,0 +1,25 @@
+//#define BOOST_RESULT_OF_USE_DECLTYPE 1
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ //[call_example
+ // Create a dispatcher instance
+ auto dispatcher=boost::afio::make_dispatcher().get();
+
+ // Schedule an asynchronous call of some function with some bound set of arguments
+ auto helloworld=dispatcher->call(boost::afio::future<>() /* no precondition */, [](std::string text) -> int {
+ std::cout << text << std::endl;
+ return 42;
+ }, std::string("Hello world"));
+
+ // Schedule as asynchronous call of some function to occur only after helloworld completes
+ auto addtovalue=dispatcher->call(helloworld, [&helloworld]() -> int {
+ return helloworld.get()+1;
+ });
+
+ // Print the result returned by the future for the lambda, which will be 43
+ std::cout << "addtovalue() returned " << addtovalue.get() << std::endl;
+ //]
+ return 0;
+}
diff --git a/attic/example/closure_execution_afio_io_example.cpp b/attic/example/closure_execution_afio_io_example.cpp
new file mode 100644
index 00000000..96ac4808
--- /dev/null
+++ b/attic/example/closure_execution_afio_io_example.cpp
@@ -0,0 +1,61 @@
+#include "afio_pch.hpp"
+
+//[closure_execution_afio_example
+#include <vector>
+
+int main()
+{
+ const int ary_size = 10;
+
+ //set up a file to read from
+ std::ofstream out_file("somefile.dat", std::ios::binary);
+ for (int i = 0; i < ary_size; ++i)
+ {
+ out_file.write(reinterpret_cast<const char*>(&i), sizeof(i));
+ }
+ out_file.close();
+
+
+ //set up the afio dispatcher
+ auto dispatcher = boost::afio::make_dispatcher().get();
+
+ //set up an array to hold our integers
+ int ary[ary_size];
+
+ //schedule the file open
+ auto opened_file = dispatcher->file(boost::afio::path_req("somefile.dat",
+ boost::afio::file_flags::read));
+
+ //set up vectors for the individual read operations, and the work on each integer
+ std::vector<boost::afio::future<>> read_ops(ary_size);
+ std::vector<std::function<void()>> vec_func(ary_size);
+ for (int i = 0; i < ary_size; ++i)
+ {
+ read_ops[i] = dispatcher->read(boost::afio::io_req<int>(opened_file,
+ &ary[i], sizeof(int), i*sizeof(int)));
+
+ vec_func[i] = std::bind([](int* a){ *a *= 2 ; }, &ary[i]);
+ }
+
+ // schedule the work to be done after reading in an integer
+ auto work = dispatcher->call(read_ops, vec_func);
+
+ //schedule the file to be closed after reads are finished
+ auto closed_file = dispatcher->close(dispatcher->barrier(read_ops).front());
+
+ // make sure work has completed before trying to print data from the array
+ boost::afio::when_all_p(work.begin(), work.end()).wait();
+
+ //verify the out put is as expected: "0, 2, 4, 6, 8, 10, 12, 14, 16, 18"
+ for (int i = 0; i < ary_size; ++i)
+ {
+ std::cout << ary[i];
+ if(i == ary_size-1)
+ std::cout << std::endl;
+ else
+ std::cout << ", ";
+ }
+
+ return 0;
+}
+//]
diff --git a/attic/example/closure_execution_traditional_io_example.cpp b/attic/example/closure_execution_traditional_io_example.cpp
new file mode 100644
index 00000000..62483edc
--- /dev/null
+++ b/attic/example/closure_execution_traditional_io_example.cpp
@@ -0,0 +1,53 @@
+#include "afio_pch.hpp"
+
+//[closure_execution_traditional_example
+#include <iostream>
+#include <fstream>
+
+int main()
+{
+
+ const int ary_size = 10;
+
+ // set up a file to read from
+ std::ofstream out_file("somefile.dat", std::ios::binary);
+ for (int i = 0; i < ary_size; ++i)
+ {
+ out_file.write(reinterpret_cast<const char*>(&i), sizeof(i));
+ }
+ out_file.close();
+
+ //setup an array of integers
+ int ary[ary_size];
+ //file open
+ std::ifstream file("somefile.dat");
+
+ //read in ints to ary
+ if (file)
+ {
+ for (int i = 0; i < ary_size; ++i)
+ {
+ file.read((char*) &ary[i], sizeof(ary[i]));
+ }
+ //close file
+ file.close();
+
+
+ //do some work with the array of ints
+ for (int i = 0; i < ary_size; ++i)
+ ary[i] *= 2;
+ }
+
+ //verify the out put is as expected: "0, 2, 4, 6, 8, 10, 12, 14, 16, 18"
+ for (int i = 0; i < ary_size; ++i)
+ {
+ std::cout << ary[i];
+ if(i == ary_size-1)
+ std::cout << std::endl;
+ else
+ std::cout << ", ";
+ }
+
+ return 0;
+}
+//]
diff --git a/attic/example/completion_example1.cpp b/attic/example/completion_example1.cpp
new file mode 100644
index 00000000..706a5e44
--- /dev/null
+++ b/attic/example/completion_example1.cpp
@@ -0,0 +1,58 @@
+//#define BOOST_RESULT_OF_USE_DECLTYPE 1
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ //[completion_example1
+ // Create a dispatcher instance
+ std::shared_ptr<boost::afio::dispatcher> dispatcher=
+ boost::afio::make_dispatcher().get();
+
+ // Completion handlers are the lowest level completion routine available, and therefore the least
+ // overhead but at the cost of considerable extra programmer effort. You almost certainly want
+ // to use the call() member function instead.
+
+ // First create some callable entity ...
+ auto completer=[](
+ /* These are always the standard parameters */
+ size_t id, boost::afio::future<> precondition,
+ /* From now on user defined parameters */
+ std::string text)
+ /* This is always the return type */
+ -> std::pair<bool, std::shared_ptr<boost::afio::handle>>
+ {
+ /* id is the unique, non-zero integer id of this op.
+ precondition is the op you supplied as precondition. As it will by definition
+ have completed by now, you can fetch from its h member variable a shared pointer
+ to the shared stl_future containing either the output handle or the error state.
+ */
+ std::cout << text << std::endl;
+
+ // Return whether this completion has completed now or is it deferred,
+ // along with the handle we pass onto any completions completing on this op
+ // Note that op.get() by default rethrows any exception contained by the op.
+ // Normally this is highly desirable.
+ return std::make_pair(true, precondition.get_handle());
+ };
+
+ // Bind any user defined parameters to create a proper boost::afio::dispatcher::completion_t
+ std::function<boost::afio::dispatcher::completion_t> boundf=
+ std::bind(completer,
+ /* The standard parameters */
+ std::placeholders::_1, std::placeholders::_2,
+ /* Any values for the user defined parameters. Remember ALWAYS to pass by value! */
+ std::string("Hello world"));
+
+ // Schedule an asynchronous call of the completion with some bound set of arguments
+ boost::afio::future<> helloworld=
+ dispatcher->completion(boost::afio::future<>() /* no precondition */,
+ std::make_pair(boost::afio::async_op_flags::none, boundf));
+
+ // Create a boost::stl_future<> representing the ops passed to when_all_p()
+ boost::afio::stl_future<std::vector<std::shared_ptr<boost::afio::handle>>> stl_future
+ =boost::afio::when_all_p(helloworld);
+ // ... and wait for it to complete
+ stl_future.wait();
+ //]
+ return 0;
+}
diff --git a/attic/example/completion_example2.cpp b/attic/example/completion_example2.cpp
new file mode 100644
index 00000000..513eea77
--- /dev/null
+++ b/attic/example/completion_example2.cpp
@@ -0,0 +1,77 @@
+//#define BOOST_RESULT_OF_USE_DECLTYPE 1
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ //[completion_example2
+ // Create a dispatcher instance
+ std::shared_ptr<boost::afio::dispatcher> dispatcher=
+ boost::afio::make_dispatcher().get();
+
+ // One thing direct programming of completion handlers can do which call() cannot is immediate
+ // completions. These run immediately after the precondition finishes by the thread worker
+ // which executed the precondition rather than being appended to the FIFO queue. This can
+ // be useful for ensuring data is still cache-local for example.
+
+ // Create the completion, using the standard form
+ auto completion=[](std::shared_ptr<boost::afio::dispatcher> dispatcher,
+ /* These are always the standard parameters */
+ size_t id, boost::afio::future<> precondition)
+ /* This is always the return type */
+ -> std::pair<bool, std::shared_ptr<boost::afio::handle>>
+ {
+ std::cout << "I am completion" << std::endl;
+
+ // Create some callable entity which will do the actual completion. It can be
+ // anything you like, but you need a minimum of its integer id.
+ auto completer=[](std::shared_ptr<boost::afio::dispatcher> dispatcher,
+ size_t id, boost::afio::future<> op) -> int
+ {
+ try
+ {
+ std::cout << "I am completer" << std::endl;
+
+ // Do stuff, returning the handle you want passed onto dependencies.
+ // Note that op.get() rethrows any exception in op. Normally you want this.
+ dispatcher->complete_async_op(id, op.get_handle());
+ }
+ catch(...)
+ {
+ // In non-deferred completions AFIO traps exceptions for you. Here, you must
+ // do it by hand and tell AFIO about what exception state to return.
+ boost::afio::exception_ptr e(boost::afio::current_exception());
+ dispatcher->complete_async_op(id, e);
+ }
+ return 0;
+ };
+ // Bind the id and handle to completer, and enqueue for later asynchronous execution.
+ std::async(std::launch::async, completer, dispatcher, id, precondition);
+
+ // Indicate we are not done yet
+ return std::make_pair(false, precondition.get_handle());
+ };
+
+ // Bind any user defined parameters to create a proper boost::afio::dispatcher::completion_t
+ std::function<boost::afio::dispatcher::completion_t> boundf=
+ std::bind(completion, dispatcher,
+ /* The standard parameters */
+ std::placeholders::_1, std::placeholders::_2);
+
+ // Schedule an asynchronous call of the completion
+ boost::afio::future<> op=
+ dispatcher->completion(boost::afio::future<>() /* no precondition */,
+ std::make_pair(
+ /* Complete boundf immediately after its precondition (in this
+ case as there is no precondition that means right now before
+ completion() returns) */
+ boost::afio::async_op_flags::immediate,
+ boundf));
+
+ // Create a boost::stl_future<> representing the ops passed to when_all_p()
+ boost::afio::stl_future<std::vector<std::shared_ptr<boost::afio::handle>>> stl_future
+ =boost::afio::when_all_p(op);
+ // ... and wait for it to complete
+ stl_future.wait();
+ //]
+ return 0;
+}
diff --git a/attic/example/determine_legal_filenames.cpp b/attic/example/determine_legal_filenames.cpp
new file mode 100644
index 00000000..667b3f36
--- /dev/null
+++ b/attic/example/determine_legal_filenames.cpp
@@ -0,0 +1,35 @@
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ using namespace boost::afio;
+ auto dispatcher=boost::afio::make_dispatcher().get();
+
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ try
+ {
+ for(size_t n=1; n<256; n++)
+ {
+ char cs[2]={ (char) n, 0 };
+ path p(cs);
+ try
+ {
+ auto mkfile(dispatcher->file(path_req::relative(mkdir, p, boost::afio::file_flags::create)));
+ mkfile.get();
+ auto rmfile(dispatcher->close(dispatcher->rmfile(mkfile)));
+ std::cout << "Character " << n << " (" << p << ") is permitted on this operating system." << std::endl;
+ }
+ catch(...)
+ {
+ std::cout << "Character " << n << " (" << p << ") failed on this operating system." << std::endl;
+ }
+ }
+ }
+ catch(...)
+ {
+ std::cerr << boost::current_exception_diagnostic_information(true) << std::endl;
+ throw;
+ }
+ auto rmdir(dispatcher->close(dispatcher->rmdir(mkdir)));
+ rmdir.get();
+}
diff --git a/attic/example/enumerate_example.cpp b/attic/example/enumerate_example.cpp
new file mode 100644
index 00000000..35bac7a5
--- /dev/null
+++ b/attic/example/enumerate_example.cpp
@@ -0,0 +1,43 @@
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ //[enumerate_example
+ boost::afio::current_dispatcher_guard h(boost::afio::make_dispatcher().get());
+
+ // Schedule an opening of the root directory
+ boost::afio::future<> rootdir(boost::afio::async_dir("/"));
+
+ std::pair<std::vector<boost::afio::directory_entry>, bool> list;
+ // This is used to reset the enumeration to the start
+ bool restart=true;
+ do
+ {
+ // Schedule an enumeration of an open directory handle
+ boost::afio::future<std::pair<std::vector<boost::afio::directory_entry>, bool>>
+ enumeration(boost::afio::async_enumerate(rootdir,
+ /* This is the maximum entries to enumerate. Note
+ the use of compatibility_maximum() which is the
+ same value your libc uses. The problem with smaller
+ enumerations is that the directory contents can change
+ out from underneath you more frequently. */
+ boost::afio::directory_entry::compatibility_maximum(),
+ /* True if to reset enumeration */
+ restart));
+ restart=false;
+
+ // People using AFIO often forget that futures can be waited
+ // on normally without needing to wait on the op handle
+ list=enumeration.get();
+ for(boost::afio::directory_entry &i : list.first)
+ {
+#ifdef WIN32
+ std::wcout << i.name();
+#else
+ std::cout << i.name();
+#endif
+ std::cout << " type " << static_cast<int>(i.st_type()) << std::endl;
+ }
+ } while(list.second);
+ //]
+}
diff --git a/attic/example/filecopy_example.cpp b/attic/example/filecopy_example.cpp
new file mode 100644
index 00000000..e48a9c36
--- /dev/null
+++ b/attic/example/filecopy_example.cpp
@@ -0,0 +1,185 @@
+#include "afio_pch.hpp"
+
+static uint32_t crc32(const void* data, size_t length, uint32_t previousCrc32 = 0)
+{
+ const uint32_t Polynomial = 0xEDB88320;
+ uint32_t crc = ~previousCrc32;
+ unsigned char* current = (unsigned char*) data;
+ while (length--)
+ {
+ crc ^= *current++;
+ for (unsigned int j = 0; j < 8; j++)
+ crc = (crc >> 1) ^ (-int(crc & 1) & Polynomial);
+ }
+ return ~crc; // same as crc ^ 0xFFFFFFFF
+}
+
+//[filecopy_example
+namespace {
+ using namespace boost::afio;
+ using boost::afio::off_t;
+
+ // Keep memory buffers around
+ // A special allocator of highly efficient file i/o memory
+ typedef std::vector<char, utils::page_allocator<char>> file_buffer_type;
+ static std::vector<std::unique_ptr<file_buffer_type>> buffers;
+
+ // Parallel copy files in sources into dest, concatenating
+ stl_future<std::vector<handle_ptr>> async_concatenate_files(
+ atomic<off_t> &written, off_t &totalbytes,
+ dispatcher_ptr dispatcher,
+ boost::afio::filesystem::path dest, std::vector<boost::afio::filesystem::path> sources,
+ size_t chunk_size=1024*1024 /* 1Mb */)
+ {
+ // Schedule the opening of the output file for writing
+ auto oh = async_file(dest, file_flags::create | file_flags::write);
+ // Schedule the opening of all the input files for reading as a batch
+ std::vector<path_req> ihs_reqs; ihs_reqs.reserve(sources.size());
+ for(auto &&source : sources)
+ ihs_reqs.push_back(path_req(source, file_flags::read
+ |file_flags::will_be_sequentially_accessed));
+ auto ihs=dispatcher->file(ihs_reqs);
+ // Retrieve any error from opening the output
+ oh.get();
+ // Wait for the input file handles to open so we can get their sizes
+ // (plus any failures to open)
+ when_all_p(ihs).get();
+
+ // Need to figure out the sizes of the sources so we can resize output
+ // correctly. We also need to allocate scratch buffers for each source.
+ std::vector<std::tuple<off_t, off_t>> offsets;
+ offsets.reserve(ihs.size());
+ off_t offset=0, max_individual=0;
+ for(auto &ih : ihs)
+ {
+ // Get the file's size in bytes
+ off_t bytes=ih->direntry(metadata_flags::size).st_size();
+ if(bytes>max_individual) max_individual=bytes;
+ //std::cout << "File " << ih->path() << " size " << bytes << " to offset " << offset << std::endl;
+ // Push the offset to write at, amount to write, and a scratch buffer
+ offsets.push_back(std::make_tuple(offset, bytes));
+ buffers.push_back(detail::make_unique<file_buffer_type>(chunk_size));
+ offset+=bytes;
+ }
+ // Schedule resizing output to correct size, retrieving errors
+ totalbytes=offset;
+ auto ohresize=async_truncate(oh, offset);
+ when_all_p(ohresize).get();
+
+ // Schedule the parallel processing of all input files, sequential per file,
+ // but only after the output file has been resized
+ std::vector<future<>> lasts;
+ for(auto &i : ihs)
+ lasts.push_back(dispatcher->depends(ohresize, i));
+ for(off_t o=0; o<max_individual; o+=chunk_size)
+ {
+ for(size_t idx=0; idx<ihs_reqs.size(); idx++)
+ {
+ auto offset=std::get<0>(offsets[idx]), bytes=std::get<1>(offsets[idx]);
+ auto &buffer=buffers[idx];
+ if(o<bytes)
+ {
+ off_t thischunk=bytes-o;
+ if(thischunk>chunk_size) thischunk=chunk_size;
+ //std::cout << "Writing " << thischunk << " from offset " << o << " in " << lasts[idx]->path() << std::endl;
+ // Schedule a filling of buffer from offset o after last has completed
+ auto readchunk = async_read(lasts[idx], buffer->data(), (size_t)thischunk, o);
+ // Schedule a writing of buffer to offset offset+o after readchunk is ready
+ // Note the call to dispatcher->depends() to make sure the write only occurs
+ // after the read completes
+ auto writechunk = async_write(depends(readchunk, ohresize), buffer->data(), (size_t)thischunk, offset + o);
+ // Schedule incrementing written after write has completed
+ auto incwritten = writechunk.then([&written, thischunk](future<> f) {
+ written += thischunk;
+ return f;
+ });
+ // Don't do next read until written is incremented
+ lasts[idx]=dispatcher->depends(incwritten, readchunk);
+ }
+ }
+ }
+ // Having scheduled all the reads and write, return a stl_future which returns when
+ // they're done
+ return when_all_p(lasts);
+ }
+}
+
+int main(int argc, const char *argv[])
+{
+ using namespace boost::afio;
+ using boost::afio::off_t;
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ if(argc<3)
+ {
+ std::cerr << "ERROR: Need to specify destination path and source paths"
+ << std::endl;
+ return 1;
+ }
+ try
+ {
+ atomic<off_t> written(0);
+ off_t totalbytes=0;
+ std::shared_ptr<boost::afio::dispatcher> dispatcher=
+ boost::afio::make_dispatcher().get();
+ // Set a dispatcher as current for this thread
+ boost::afio::current_dispatcher_guard guard(dispatcher);
+
+ boost::afio::filesystem::path dest=argv[1];
+ std::vector<boost::afio::filesystem::path> sources;
+ std::cout << "Concatenating into " << dest << " the files ";
+ for(int n=2; n<argc; ++n)
+ {
+ sources.push_back(argv[n]);
+ std::cout << sources.back();
+ if(n<argc-1) std::cout << ", ";
+ }
+ std::cout << " ..." << std::endl;
+
+ auto begin=chrono::steady_clock::now();
+ auto h=async_concatenate_files(written, totalbytes, dispatcher, dest, sources);
+ // Print progress once a second until it's done
+ while(future_status::timeout==h.wait_for(boost::afio::chrono::seconds(1)))
+ {
+ std::cout << "\r" << (100*written)/totalbytes << "% complete (" << written
+ << " out of " << totalbytes << " @ " << (written/chrono::duration_cast<secs_type>(
+ chrono::steady_clock::now()-begin).count()/1024/1024) << "Mb/sec) ..." << std::flush;
+ }
+ // Retrieve any errors
+ h.get();
+ std::cout << std::endl;
+ }
+ catch(...)
+ {
+ std::cerr << "ERROR: " << boost::current_exception_diagnostic_information(true)
+ << std::endl;
+ return 1;
+ }
+ // Make sure output really is input concatenated
+ std::cout << "CRC checking that the output exactly matches the inputs ..." << std::endl;
+ uint32_t crc1 = 0, crc2 = 0;
+ off_t offset = 0;
+ std::ifstream o(argv[1], std::ifstream::in);
+ for (int n = 2; n < argc; n++)
+ {
+ std::ifstream i(argv[n], std::ifstream::in);
+ i.seekg(0, i.end);
+ std::vector<char> buffer1((size_t)i.tellg()), buffer2((size_t)i.tellg());
+ i.seekg(0, i.beg);
+ o.read(buffer1.data(), buffer1.size());
+ i.read(buffer2.data(), buffer2.size());
+ bool quiet = false;
+ for (size_t n = 0; n<buffer1.size(); n += 64)
+ {
+ crc1 = crc32(buffer1.data() + n, 64, crc1);
+ crc2 = crc32(buffer2.data() + n, 64, crc2);
+ if (crc1 != crc2 && !quiet)
+ {
+ std::cerr << "ERROR: Offset " << offset+n << " not copied correctly!" << std::endl;
+ quiet = true;
+ }
+ }
+ offset += buffer1.size();
+ }
+ return 0;
+}
+//]
diff --git a/attic/example/filedir_example.cpp b/attic/example/filedir_example.cpp
new file mode 100644
index 00000000..45439ed5
--- /dev/null
+++ b/attic/example/filedir_example.cpp
@@ -0,0 +1,74 @@
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ //[filedir_example
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ using BOOST_AFIO_V2_NAMESPACE::rmdir;
+ std::shared_ptr<boost::afio::dispatcher> dispatcher =
+ boost::afio::make_dispatcher().get();
+ current_dispatcher_guard h(dispatcher);
+
+ // Free function
+ try
+ {
+ // Schedule creating a directory called testdir
+ auto mkdir(async_dir("testdir", boost::afio::file_flags::create));
+ // Schedule creating a file called testfile in testdir only when testdir has been created
+ auto mkfile(async_file(mkdir, "testfile", boost::afio::file_flags::create));
+ // Schedule creating a symbolic link called linktodir to the item referred to by the precondition
+ // i.e. testdir. Note that on Windows you can only symbolic link directories.
+ auto mklink(async_symlink(mkdir, "linktodir", mkdir, boost::afio::file_flags::create));
+
+ // Schedule deleting the symbolic link only after when it has been created
+ auto rmlink(async_rmsymlink(mklink));
+ // Schedule deleting the file only after when it has been created
+ auto rmfile(async_close(async_rmfile(mkfile)));
+ // Schedule waiting until both the preceding operations have finished
+ auto barrier(dispatcher->barrier({ rmlink, rmfile }));
+ // Schedule deleting the directory only after the barrier completes
+ auto rmdir(async_rmdir(depends(barrier.front(), mkdir)));
+ // Check ops for errors
+ boost::afio::when_all_p(mkdir, mkfile, mklink, rmlink, rmfile, rmdir).wait();
+ }
+ catch (...)
+ {
+ std::cerr << boost::current_exception_diagnostic_information(true) << std::endl;
+ throw;
+ }
+
+ // Batch
+ try
+ {
+ // Schedule creating a directory called testdir
+ auto mkdir(dispatcher->dir(std::vector<boost::afio::path_req>(1,
+ boost::afio::path_req("testdir", boost::afio::file_flags::create))).front());
+ // Schedule creating a file called testfile in testdir only when testdir has been created
+ auto mkfile(dispatcher->file(std::vector<boost::afio::path_req>(1,
+ boost::afio::path_req::relative(mkdir, "testfile",
+ boost::afio::file_flags::create))).front());
+ // Schedule creating a symbolic link called linktodir to the item referred to by the precondition
+ // i.e. testdir. Note that on Windows you can only symbolic link directories. Note that creating
+ // symlinks must *always* be as an absolute path, as that is how they are stored.
+ auto mklink(dispatcher->symlink(std::vector<boost::afio::path_req>(1,
+ boost::afio::path_req::absolute(mkdir, "testdir/linktodir",
+ boost::afio::file_flags::create))).front());
+
+ // Schedule deleting the symbolic link only after when it has been created
+ auto rmlink(dispatcher->close(std::vector<future<>>(1, dispatcher->rmsymlink(mklink))).front());
+ // Schedule deleting the file only after when it has been created
+ auto rmfile(dispatcher->close(std::vector<future<>>(1, dispatcher->rmfile(mkfile))).front());
+ // Schedule waiting until both the preceding operations have finished
+ auto barrier(dispatcher->barrier({rmlink, rmfile}));
+ // Schedule deleting the directory only after the barrier completes
+ auto rmdir(dispatcher->rmdir(std::vector<path_req>(1, dispatcher->depends(barrier.front(), mkdir))).front());
+ // Check ops for errors
+ boost::afio::when_all_p(mkdir, mkfile, mklink, rmlink, rmfile, rmdir).wait();
+ }
+ catch(...)
+ {
+ std::cerr << boost::current_exception_diagnostic_information(true) << std::endl;
+ throw;
+ }
+ //]
+}
diff --git a/attic/example/filter_example.cpp b/attic/example/filter_example.cpp
new file mode 100644
index 00000000..464f47e8
--- /dev/null
+++ b/attic/example/filter_example.cpp
@@ -0,0 +1,36 @@
+#include "afio_pch.hpp"
+
+//[filter_example
+using namespace boost::afio;
+
+// This function will be called for every file opened
+static void open_file_filter(detail::OpType, future<> &op) noexcept
+{
+ std::cout << "File handle " << op->native_handle() << " opened!" << std::endl;
+}
+
+// This function will be called for every read and write performed
+static void readwrite_filter(detail::OpType optype, handle *h,
+ const detail::io_req_impl<true> &req, boost::afio::off_t offset, size_t buffer_idx,
+ size_t buffers, const boost::system::error_code &ec, size_t bytes_transferred)
+{
+ std::cout << "File handle " << h->native_handle() << (detail::OpType::read==optype ?
+ " read " : " wrote ") << bytes_transferred << " bytes at offset " << offset << std::endl;
+}
+
+int main(void)
+{
+ dispatcher_ptr dispatcher=
+ make_dispatcher().get();
+
+ // Install filters BEFORE scheduling any ops as the filter APIs are NOT
+ // threadsafe. This filters all file opens.
+ dispatcher->post_op_filter({ std::make_pair(detail::OpType::file /* just file opens */,
+ std::function<dispatcher::filter_t>(open_file_filter)) });
+
+ // This filters all reads and writes
+ dispatcher->post_readwrite_filter({ std::make_pair(detail::OpType::Unknown /* all */,
+ std::function<dispatcher::filter_readwrite_t>(readwrite_filter)) });
+ return 0;
+}
+//]
diff --git a/attic/example/find_in_files_afio.cpp b/attic/example/find_in_files_afio.cpp
new file mode 100644
index 00000000..a1427fd2
--- /dev/null
+++ b/attic/example/find_in_files_afio.cpp
@@ -0,0 +1,351 @@
+#include "afio_pch.hpp"
+#include <deque>
+#include <regex>
+#include <chrono>
+#include <mutex>
+#include <future>
+#include <initializer_list>
+#include "boost/exception/diagnostic_information.hpp"
+#include "boost/../libs/afio/test/Aligned_Allocator.hpp"
+
+/* My Intel Core i7 3770K running Windows 8 x64 with 7200rpm drive, using
+Sysinternals RAMMap to clear disc cache (http://technet.microsoft.com/en-us/sysinternals/ff700229.aspx)
+
+Warm cache:
+92 files matched out of 36734 files which was 7031545071 bytes.
+The search took 2.5173 seconds which was 14592.6 files per second or 2663.89 Mb/sec.
+
+Cold cache:
+91 files matched out of 36422 files which was 6108537728 bytes.
+The search took 388.74 seconds which was 93.6925 files per second or 14.9857 Mb/sec.
+
+Warm cache mmaps:
+92 files matched out of 36734 files which was 7031400686 bytes.
+The search took 1.02974 seconds which was 35673.1 files per second or 6512.01 Mb/sec.
+
+Cold cache mmaps:
+91 files matched out of 36422 files which was 6109258426 bytes.
+The search took 242.76 seconds which was 150.033 files per second or 24 Mb/sec.
+*/
+
+#define USE_MMAPS
+
+//[find_in_files_afio
+using namespace boost::afio;
+
+// Often it's easiest for a lot of nesting callbacks to carry state via a this pointer
+class find_in_files
+{
+public:
+ std::promise<int> finished;
+ std::regex regexpr; // The precompiled regular expression
+ dispatcher_ptr dispatcher;
+ recursive_mutex opslock;
+ std::deque<future<>> ops; // For exception gathering
+ std::atomic<size_t> bytesread, filesread, filesmatched, scheduled, completed;
+ std::vector<std::pair<boost::afio::filesystem::path, size_t>> filepaths;
+
+ // Signals finish once all scheduled ops have completed
+ void docompleted(size_t inc)
+ {
+ size_t c=(completed+=inc);
+ if(c==scheduled)
+ finished.set_value(0);
+ };
+ // Adds ops to the list of scheduled
+ void doscheduled(std::initializer_list<future<>> list)
+ {
+ scheduled+=list.size();
+ //boost::lock_guard<decltype(opslock)> lock(opslock);
+ //ops.insert(ops.end(), list.begin(), list.end());
+ }
+ void doscheduled(std::vector<future<>> list)
+ {
+ scheduled+=list.size();
+ //boost::lock_guard<decltype(opslock)> lock(opslock);
+ //ops.insert(ops.end(), list.begin(), list.end());
+ }
+ void dosearch(handle_ptr h, const char *buffer, size_t length)
+ {
+ // Search the buffer for the regular expression
+ if(std::regex_search(buffer, regexpr))
+ {
+#pragma omp critical
+ {
+ std::cout << h->path() << std::endl;
+ }
+ ++filesmatched;
+ }
+ ++filesread;
+ bytesread+=length;
+ }
+ // A file searching completion, called when each file read completes
+ std::pair<bool, handle_ptr> file_read(size_t id,
+ future<> op, std::shared_ptr<std::vector<char,
+ detail::aligned_allocator<char, 4096, false>>> _buffer, size_t length)
+ {
+ handle_ptr h(op.get_handle());
+ //std::cout << "R " << h->path() << std::endl;
+ char *buffer=_buffer->data();
+ buffer[length]=0;
+ dosearch(h, buffer, length);
+ docompleted(2);
+ // Throw away the buffer now rather than later to keep memory consumption down
+ _buffer->clear();
+ return std::make_pair(true, h);
+ }
+ // A file reading completion, called when each file open completes
+ std::pair<bool, handle_ptr> file_opened(size_t id,
+ future<> op, size_t length)
+ {
+ handle_ptr h(op.get_handle());
+ //std::cout << "F " << h->path() << std::endl;
+ if (length)
+ {
+#ifdef USE_MMAPS
+ auto map(h->map_file());
+ if (map)
+ {
+ dosearch(h, (const char *)map->addr, length);
+ }
+ else
+#endif
+ {
+ // Allocate a sufficient 4Kb aligned buffer
+ size_t _length = (4095 + length)&~4095;
+ auto buffer = std::make_shared < std::vector<char,
+ detail::aligned_allocator<char, 4096, false >> >(_length + 1);
+ // Schedule a read of the file
+ auto read = dispatcher->read(make_io_req(
+ dispatcher->op_from_scheduled_id(id), buffer->data(), _length, 0));
+ auto read_done = dispatcher->completion(read,
+ std::make_pair(async_op_flags::none/*regex search might be slow*/,
+ std::function<dispatcher::completion_t>(
+ std::bind(&find_in_files::file_read, this, std::placeholders::_1,
+ std::placeholders::_2, buffer, length))));
+ doscheduled({ read, read_done });
+ }
+ }
+ docompleted(2);
+ return std::make_pair(true, h);
+ }
+ // An enumeration parsing completion, called when each directory enumeration completes
+ std::pair<bool, handle_ptr> dir_enumerated(size_t id,
+ future<> op,
+ std::shared_ptr<future<std::pair<std::vector<directory_entry>, bool>>> listing)
+ {
+ handle_ptr h(op.get_handle());
+ future<> lastdir, thisop(dispatcher->op_from_scheduled_id(id));
+ // Get the entries from the ready stl_future
+ std::vector<directory_entry> entries(std::move(listing->get().first));
+ //std::cout << "E " << h->path() << std::endl;
+ // For each of the directories schedule an open and enumeration
+#if 0
+ // Algorithm 1
+ {
+ std::vector<path_req> dir_reqs; dir_reqs.reserve(entries.size());
+ for(auto &entry : entries)
+ {
+ if((entry.st_type()&S_IFDIR)==S_IFDIR)
+ {
+ dir_reqs.push_back(path_req(thisop, h->path()/entry.name()));
+ }
+ }
+ if(!dir_reqs.empty())
+ {
+ std::vector<std::pair<async_op_flags, std::function<dispatcher::completion_t>>> dir_openedfs(dir_reqs.size(), std::make_pair(async_op_flags::None, std::bind(&find_in_files::dir_opened, this, std::placeholders::_1, std::placeholders::_2)));
+ auto dir_opens=dispatcher->dir(dir_reqs);
+ doscheduled(dir_opens);
+ auto dir_openeds=dispatcher->completion(dir_opens, dir_openedfs);
+ doscheduled(dir_openeds);
+ // Hold back some of the concurrency
+ lastdir=dir_openeds.back();
+ }
+ }
+#else
+ // Algorithm 2
+ // The Windows NT kernel filing system driver gets upset with too much concurrency
+ // when used with OSDirect so throttle directory enumerations to enforce some depth first traversal.
+ {
+ std::pair<async_op_flags, std::function<dispatcher::completion_t>> dir_openedf=std::make_pair(async_op_flags::none, std::bind(&find_in_files::dir_opened, this,
+ std::placeholders::_1, std::placeholders::_2));
+ for(auto &entry : entries)
+ {
+ if(entry.st_type()==
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ boost::afio::filesystem::file_type::directory_file)
+#else
+ boost::afio::filesystem::file_type::directory)
+#endif
+ {
+ auto dir_open=dispatcher->dir(path_req::absolute(lastdir, h->path()/entry.name()));
+ auto dir_opened=dispatcher->completion(dir_open, dir_openedf);
+ doscheduled({ dir_open, dir_opened });
+ lastdir=dir_opened;
+ }
+ }
+ }
+#endif
+
+ // For each of the files schedule an open and search
+#if 0
+ // Algorithm 1
+ {
+ std::vector<path_req> file_reqs; file_reqs.reserve(entries.size());
+ std::vector<std::pair<async_op_flags, std::function<dispatcher::completion_t>>> file_openedfs; file_openedfs.reserve(entries.size());
+ for(auto &entry : entries)
+ {
+ if((entry.st_type()&S_IFREG)==S_IFREG)
+ {
+ size_t length=(size_t)entry.st_size();
+ if(length)
+ {
+ file_flags flags=file_flags::read;
+#ifdef USE_MMAPS
+ if(length>16384) flags=flags|file_flags::os_mmap;
+#endif
+ file_reqs.push_back(path_req(lastdir, h->path()/entry.name(), flags));
+ file_openedfs.push_back(std::make_pair(async_op_flags::None, std::bind(&find_in_files::file_opened, this, std::placeholders::_1, std::placeholders::_2, length)));
+ }
+ }
+ }
+ auto file_opens=dispatcher->file(file_reqs);
+ doscheduled(file_opens);
+ auto file_openeds=dispatcher->completion(file_opens, file_openedfs);
+ doscheduled(file_openeds);
+ }
+#else
+ // Algorithm 2
+ {
+ for(auto &entry : entries)
+ {
+ if(entry.st_type()==
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ boost::afio::filesystem::file_type::regular_file)
+#else
+ boost::afio::filesystem::file_type::regular)
+#endif
+ {
+ size_t length=(size_t)entry.st_size();
+ if(length)
+ {
+ file_flags flags=file_flags::read;
+ auto file_open=dispatcher->file(path_req::absolute(lastdir, h->path()/entry.name(), flags));
+ auto file_opened=dispatcher->completion(file_open,
+ std::make_pair(async_op_flags::none,
+ std::function<dispatcher::completion_t>(
+ std::bind(&find_in_files::file_opened, this,
+ std::placeholders::_1, std::placeholders::_2, length))));
+ doscheduled({ file_open, file_opened });
+ lastdir=file_opened;
+ }
+ }
+ }
+ }
+#endif
+ docompleted(2);
+ return std::make_pair(true, h);
+ }
+ // A directory enumerating completion, called once per directory open in the tree
+ std::pair<bool, handle_ptr> dir_opened(size_t id,
+ future<> op)
+ {
+ handle_ptr h(op.get_handle());
+ //std::cout << "D " << h->path() << std::endl;
+ // Now we have an open directory handle, schedule an enumeration
+ auto enumeration=dispatcher->enumerate(enumerate_req(
+ dispatcher->op_from_scheduled_id(id), metadata_flags::size, 1000));
+ future<> enumeration_op(enumeration);
+ auto listing=std::make_shared<future<std::pair<std::vector<directory_entry>,
+ bool>>>(std::move(enumeration));
+ auto enumeration_done=dispatcher->completion(enumeration_op,
+ make_pair(async_op_flags::none,
+ std::function<dispatcher::completion_t>(
+ std::bind(&find_in_files::dir_enumerated, this,
+ std::placeholders::_1, std::placeholders::_2, listing))));
+ doscheduled({enumeration, enumeration_done});
+ docompleted(2);
+ // Complete only if not the cur dir opened
+ return std::make_pair(true, h);
+ };
+ void dowait()
+ {
+ // Prepare finished
+ auto finished_waiter=finished.get_future();
+#if 0 // Disabled to maximise performance
+ // Wait till done, retrieving any exceptions as they come in to keep memory consumption down
+ std::future_status status;
+ do
+ {
+ status=finished_waiter.wait_for(std::chrono::milliseconds(1000));
+ std::cout << "\nDispatcher has " << dispatcher->count() << " fds open and " << dispatcher->wait_queue_depth() << " ops outstanding." << std::endl;
+ std::vector<future<>> batch; batch.reserve(10000);
+ {
+ boost::lock_guard<decltype(opslock)> lock(opslock);
+ while(status==std::future_status::timeout ? (ops.size()>5000) : !ops.empty())
+ {
+ batch.push_back(std::move(ops.front()));
+ ops.pop_front();
+ }
+ }
+ // Retrieve any exceptions
+ std::cout << "Processed " << batch.size() << " ops for exception state." << std::endl;
+ if(!batch.empty())
+ when_all_p(batch.begin(), batch.end()).wait();
+ } while(status==std::future_status::timeout);
+#else
+ finished_waiter.wait();
+#endif
+ }
+ // Constructor, which starts the ball rolling
+ find_in_files(const char *_regexpr) : regexpr(_regexpr),
+ // Create an AFIO dispatcher that bypasses any filing system buffers
+ dispatcher(make_dispatcher("file:///", file_flags::will_be_sequentially_accessed/*|file_flags::os_direct*/).get()),
+ bytesread(0), filesread(0), filesmatched(0), scheduled(0), completed(0)
+ {
+ filepaths.reserve(50000);
+
+ // Schedule the recursive enumeration of the current directory
+ std::cout << "\n\nStarting directory enumerations ..." << std::endl;
+ auto cur_dir=dispatcher->dir(path_req(""));
+ auto cur_dir_opened=dispatcher->completion(cur_dir, std::make_pair(async_op_flags::none,
+ std::function<dispatcher::completion_t>(
+ std::bind(&find_in_files::dir_opened, this,
+ std::placeholders::_1, std::placeholders::_2))));
+ doscheduled({cur_dir, cur_dir_opened});
+ dowait();
+ }
+};
+
+int main(int argc, const char *argv[])
+{
+ using std::placeholders::_1; using std::placeholders::_2;
+ using namespace boost::afio;
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ if(argc<2)
+ {
+ std::cerr << "ERROR: Specify a regular expression to search all files in the current directory." << std::endl;
+ return 1;
+ }
+ // Prime SpeedStep
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<1);
+ try
+ {
+ begin=chrono::high_resolution_clock::now();
+ find_in_files finder(argv[1]);
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "\n" << finder.filesmatched << " files matched out of " << finder.filesread
+ << " files which was " << finder.bytesread << " bytes." << std::endl;
+ std::cout << "The search took " << diff.count() << " seconds which was " << finder.filesread/diff.count()
+ << " files per second or " << (finder.bytesread/diff.count()/1024/1024) << " Mb/sec." << std::endl;
+ }
+ catch(...)
+ {
+ std::cerr << boost::current_exception_diagnostic_information(true) << std::endl;
+ return 1;
+ }
+ return 0;
+}
+//]
diff --git a/attic/example/find_in_files_iostreams.cpp b/attic/example/find_in_files_iostreams.cpp
new file mode 100644
index 00000000..0114ec57
--- /dev/null
+++ b/attic/example/find_in_files_iostreams.cpp
@@ -0,0 +1,109 @@
+#include "afio_pch.hpp"
+#include <iostream>
+#include <fstream>
+#include <regex>
+#include <chrono>
+#if BOOST_AFIO_USE_BOOST_FILESYSTEM
+#include "boost/filesystem/fstream.hpp"
+#endif
+
+/* My Intel Core i7 3770K running Windows 8 x64 with 7200rpm drive, using
+Sysinternals RAMMap to clear disc cache (http://technet.microsoft.com/en-us/sysinternals/ff700229.aspx)
+
+Single threaded, warm cache:
+92 files matched out of 39279 files which was 7093894518 bytes.
+The search took 8.32834 seconds which was 4716.3 files per second or 812.318 Mb/sec.
+
+Single threaded, cold cache:
+91 files matched out of 38967 files which was 6170927489 bytes.
+The search took 369.046 seconds which was 105.588 files per second or 15.9467 Mb/sec.
+
+OpenMP, warm cache:
+92 files matched out of 38943 files which was 7092655881 bytes.
+The search took 3.73611 seconds which was 10423.4 files per second or 1810.46 Mb/sec.
+
+OpenMP, cold cache:
+91 files matched out of 38886 files which was 6170656567 bytes.
+The search took 741.131 seconds which was 52.4684 files per second or 7.94029 Mb/sec.
+*/
+
+//[find_in_files_iostreams
+int main(int argc, const char *argv[])
+{
+ using namespace std;
+ namespace filesystem = boost::afio::filesystem;
+#if BOOST_AFIO_USE_BOOST_FILESYSTEM
+ using boost::filesystem::ifstream;
+#endif
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ if(argc<2)
+ {
+ cerr << "ERROR: Specify a regular expression to search all files in the current directory." << endl;
+ return 1;
+ }
+ // Prime SpeedStep
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<1);
+ size_t bytesread=0, filesread=0, filesmatched=0;
+ try
+ {
+ begin=chrono::high_resolution_clock::now();
+
+ // Generate a list of all files here and below the current working directory
+ vector<filesystem::path> filepaths;
+ for(auto it=filesystem::recursive_directory_iterator("."); it!=filesystem::recursive_directory_iterator(); ++it)
+ {
+ if(it->status().type()!=
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ filesystem::file_type::regular_file)
+#else
+ filesystem::file_type::regular)
+#endif
+ continue;
+ filepaths.push_back(it->path());
+ }
+
+ // Compile the regular expression, and have OpenMP parallelise the loop
+ regex regexpr(argv[1]);
+#pragma omp parallel for schedule(dynamic)
+ for(int n=0; n<(int) filepaths.size(); n++)
+ {
+ // Open the file
+ ifstream s(filepaths[n], ifstream::binary);
+ s.exceptions(fstream::failbit | fstream::badbit); // Turn on exception throwing
+ // Get its length
+ s.seekg(0, ios::end);
+ size_t length=(size_t) s.tellg();
+ s.seekg(0, ios::beg);
+ // Allocate a sufficient buffer, avoiding the byte clearing vector would do
+ unique_ptr<char[]> buffer(new char[length+1]);
+ // Read in the file, terminating with zero
+ s.read(buffer.get(), length);
+ buffer.get()[length]=0;
+ // Search the buffer for the regular expression
+ if(regex_search(buffer.get(), regexpr))
+ {
+#pragma omp critical
+ {
+ cout << filepaths[n] << endl;
+ }
+ filesmatched++;
+ }
+ filesread++;
+ bytesread+=length;
+ }
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ cout << "\n" << filesmatched << " files matched out of " << filesread << " files which was "
+ << bytesread << " bytes." << endl;
+ cout << "The search took " << diff.count() << " seconds which was " << filesread/diff.count()
+ << " files per second or " << (bytesread/diff.count()/1024/1024) << " Mb/sec." << endl;
+ }
+ catch(...)
+ {
+ cerr << boost::current_exception_diagnostic_information(true) << endl;
+ return 1;
+ }
+ return 0;
+}
+//]
diff --git a/attic/example/readallof_example.cpp b/attic/example/readallof_example.cpp
new file mode 100644
index 00000000..c7882c88
--- /dev/null
+++ b/attic/example/readallof_example.cpp
@@ -0,0 +1,54 @@
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ using namespace boost::afio;
+ auto dispatcher=make_dispatcher().get();
+ current_dispatcher_guard h(dispatcher);
+
+{
+ //[readallof_example_bad
+ char input[1024];
+ auto file_opened = file("foo.txt");
+ read(file_opened, input, 0);
+ //]
+}
+
+{
+ //[readallof_example_many
+ char input[1024];
+ // Schedule enumerating the containing directory, but only for foo.txt
+ auto dir_opened = async_dir(""); // "" means current directory in AFIO
+ auto file_enumed = async_enumerate(dir_opened, metadata_flags::size,
+ 2, true, "foo.txt");
+ // Schedule in parallel opening the file
+ auto file_opened = async_file(dir_opened, "foo.txt");
+ auto file_read = file_enumed.then(
+ [&input](future<std::pair<std::vector<directory_entry>, bool>> &f) {
+ // Get the directory_entry for the first result
+ directory_entry &de = f.get().first.front();
+ // Schedule a file read once we know the file size
+ return async_read(f, (void *)input,
+ (size_t)de.st_size(), // won't block
+ 0);
+ });
+ file_read.get();
+ //]
+}
+
+{
+ //[readallof_example_single
+ char input[1024];
+ // Open the file synchronously
+ auto file_opened = file("foo.txt");
+ // Fetch ONLY the size metadata. Blocks because it's synchronous!
+ directory_entry de = file_opened->direntry(metadata_flags::size);
+ // Read now we know the file size
+ read(file_opened,
+ (void *) input,
+ (size_t) de.st_size(), // doesn't block, as size was fetched before.
+ 0);
+ //]
+}
+ return 0;
+}
diff --git a/attic/example/readwrite_example.cpp b/attic/example/readwrite_example.cpp
new file mode 100644
index 00000000..5a94c561
--- /dev/null
+++ b/attic/example/readwrite_example.cpp
@@ -0,0 +1,78 @@
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ try
+ {
+ //[readwrite_example
+ namespace afio = BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+
+ // Set a dispatcher as current for this thread
+ afio::current_dispatcher_guard h(afio::make_dispatcher().get());
+
+ // Schedule an opening of a file called example_file.txt
+ afio::future<> openfile = afio::async_file("example_file.txt",
+ afio::file_flags::create | afio::file_flags::read_write);
+
+ // Something a bit surprising for many people is that writing off
+ // the end of a file in AFIO does NOT extend the file and writes
+ // which go past the end will simply fail instead. Why not?
+ // Simple: that's the convention with async file i/o, because
+ // synchronising multiple processes concurrently adjusting a
+ // file's length has significant overhead which is wasted if you
+ // don't need that functionality. Luckily, there is an easy
+ // workaround: either open a file for append-only access, in which
+ // case all writes extend the file for you, or else you explicitly
+ // extend files before writing, like this:
+ afio::future<> resizedfile = afio::async_truncate(openfile, 12);
+
+ // Config a write gather. You could do this of course as a batch
+ // of writes, but a write gather has optimised host OS support in most
+ // cases, so it's one syscall instead of many.
+ std::vector<asio::const_buffer> buffers;
+ buffers.push_back(asio::const_buffer("He", 2));
+ buffers.push_back(asio::const_buffer("ll", 2));
+ buffers.push_back(asio::const_buffer("o ", 2));
+ buffers.push_back(asio::const_buffer("Wo", 2));
+ buffers.push_back(asio::const_buffer("rl", 2));
+ buffers.push_back(asio::const_buffer("d\n", 2));
+ // Schedule the write gather to offset zero after the resize file
+ afio::future<> written(afio::async_write(resizedfile, buffers, 0));
+
+ // Have the compiler config the exact same write gather as earlier for you
+ // The compiler assembles an identical sequence of ASIO write gather
+ // buffers for you
+ std::vector<std::string> buffers2={ "He", "ll", "o ", "Wo", "rl", "d\n" };
+ // Schedule this to occur after the previous write completes
+ afio::future<> written2(afio::async_write(written, buffers2, 0));
+
+ // Schedule making sure the previous batch has definitely reached physical storage
+ // This won't complete until the write is on disc
+ afio::future<> stored(afio::async_sync(written2));
+
+ // Schedule filling this array from the file. Note how convenient std::array
+ // is and completely replaces C style char buffer[bytes]
+ std::array<char, 12> buffer;
+ afio::future<> read(afio::async_read(stored, buffer, 0));
+
+ // Schedule the closing and deleting of example_file.txt after the contents read
+ afio::future<> deletedfile(afio::async_rmfile(afio::async_close(read)));
+
+ // Wait until the buffer has been filled, checking all steps for errors
+ afio::when_all_p(openfile, resizedfile, written, written2, stored, read).get(); /*< waits for file open, resize, write, sync and read to complete, throwing any exceptions encountered >*/
+
+ // There is actually a io_req<std::string> specialisation you
+ // can use to skip this bit by reading directly into a string ...
+ std::string contents(buffer.begin(), buffer.end());
+ std::cout << "Contents of file is '" << contents << "'" << std::endl;
+
+ // Check remaining ops for errors
+ deletedfile.get();
+ //]
+ }
+ catch(const BOOST_AFIO_V2_NAMESPACE::system_error &e) { std::cerr << "ERROR: program exits via system_error code " << e.code().value() << " (" << e.what() << ")" << std::endl; return 1; }
+ catch(const std::exception &e) { std::cerr << "ERROR: program exits via exception (" << e.what() << ")" << std::endl; return 1; }
+ catch(...) { std::cerr << "ERROR: program exits via " << boost::current_exception_diagnostic_information(true) << std::endl; return 1; }
+ return 0;
+}
diff --git a/attic/example/readwrite_example_traditional.cpp b/attic/example/readwrite_example_traditional.cpp
new file mode 100644
index 00000000..56f82113
--- /dev/null
+++ b/attic/example/readwrite_example_traditional.cpp
@@ -0,0 +1,46 @@
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ //[readwrite_example_traditional
+
+ try
+ {
+ // Open a file called example_file.txt
+ std::fstream openfile("example_file.txt"); /*< opens file >*/
+ // Turn on exception throwing
+ openfile.exceptions(std::fstream::failbit | std::fstream::badbit);
+
+ // Do a write gather. STL iostreams will buffer the writes
+ // and coalesce them into a single syscall
+ openfile << "He"; /*< writes >*/
+ openfile << "ll";
+ openfile << "o ";
+ openfile << "Wo";
+ openfile << "rl";
+ openfile << "d\n";
+
+ // Make sure the previous batch has definitely reached physical storage
+ // This won't complete until the write is on disc
+ openfile.sync(); /*< syncs >*/
+
+ // Fill this array from the file
+ std::array<char, 12> buffer;
+ openfile.seekg(0, std::ios::beg);
+ openfile.read(buffer.data(), buffer.size()); /*< reads >*/
+
+ // Close the file and delete it
+ openfile.close(); /*< closes file >*/
+ boost::afio::filesystem::remove("example_file.txt"); /*< deletes file >*/
+
+ // Convert the read array into a string
+ std::string contents(buffer.begin(), buffer.end());
+ std::cout << "Contents of file is '" << contents << "'" << std::endl;
+ }
+ catch(...)
+ {
+ std::cerr << boost::current_exception_diagnostic_information(true) << std::endl;
+ throw;
+ }
+ //]
+}
diff --git a/attic/example/statfs_example.cpp b/attic/example/statfs_example.cpp
new file mode 100644
index 00000000..122c07e0
--- /dev/null
+++ b/attic/example/statfs_example.cpp
@@ -0,0 +1,18 @@
+#include "afio_pch.hpp"
+
+int main(void)
+{
+ //[statfs_example
+ boost::afio::current_dispatcher_guard h(boost::afio::make_dispatcher().get());
+
+ // Open the root directory
+ boost::afio::handle_ptr rootdir(boost::afio::dir("/"));
+
+ // Ask the filing system of the root directory how much free space there is
+ boost::afio::statfs_t statfs(boost::afio::statfs(rootdir,
+ boost::afio::fs_metadata_flags::bsize|boost::afio::fs_metadata_flags::bfree));
+
+ std::cout << "Your root filing system has "
+ << (statfs.f_bfree*statfs.f_bsize/1024.0/1024.0/1024.0) << " Gb free." << std::endl;
+ //]
+}
diff --git a/attic/example/workshop_atomic_updates_afio.cpp b/attic/example/workshop_atomic_updates_afio.cpp
new file mode 100644
index 00000000..f36ea1c5
--- /dev/null
+++ b/attic/example/workshop_atomic_updates_afio.cpp
@@ -0,0 +1,2 @@
+#include "afio_pch.hpp"
+#include "workshop_atomic_updates_afio.ipp"
diff --git a/attic/example/workshop_atomic_updates_afio.ipp b/attic/example/workshop_atomic_updates_afio.ipp
new file mode 100644
index 00000000..c4daafe4
--- /dev/null
+++ b/attic/example/workshop_atomic_updates_afio.ipp
@@ -0,0 +1,323 @@
+//[workshop_atomic_updates_afio_interface
+namespace afio = BOOST_AFIO_V2_NAMESPACE;
+namespace filesystem = BOOST_AFIO_V2_NAMESPACE::filesystem;
+using BOOST_OUTCOME_V1_NAMESPACE::lightweight_futures::shared_future;
+
+class data_store
+{
+ afio::dispatcher_ptr _dispatcher;
+ afio::handle_ptr _store;
+public:
+ // Type used for read streams
+ using istream = std::shared_ptr<std::istream>;
+ // Type used for write streams
+ using ostream = std::shared_ptr<std::ostream>;
+ // Type used for lookup
+ using lookup_result_type = shared_future<istream>;
+ // Type used for write
+ using write_result_type = shared_future<ostream>;
+
+ // Disposition flags
+ static constexpr size_t writeable = (1<<0);
+
+ // Open a data store at path
+ data_store(size_t flags = 0, afio::path path = "store");
+
+ // Look up item named name for reading, returning an istream for the item
+ shared_future<istream> lookup(std::string name) noexcept;
+ // Look up item named name for writing, returning an ostream for that item
+ shared_future<ostream> write(std::string name) noexcept;
+};
+//]
+
+//[workshop_atomic_updates_afio3]
+namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+using BOOST_OUTCOME_V1_NAMESPACE::empty;
+using BOOST_AFIO_V2_NAMESPACE::error_code;
+using BOOST_AFIO_V2_NAMESPACE::generic_category;
+
+// A special allocator of highly efficient file i/o memory
+using file_buffer_type = std::vector<char, afio::utils::page_allocator<char>>;
+
+// An iostream which reads directly from a memory mapped AFIO file
+struct idirectstream : public std::istream
+{
+ struct directstreambuf : public std::streambuf
+ {
+ afio::handle_ptr h; // Holds the file open
+ std::shared_ptr<file_buffer_type> buffer;
+ afio::handle::mapped_file_ptr mfp;
+ // From a mmap
+ directstreambuf(afio::handle_ptr _h, afio::handle::mapped_file_ptr _mfp, size_t length) : h(std::move(_h)), mfp(std::move(_mfp))
+ {
+ // Set the get buffer this streambuf is to use
+ setg((char *) mfp->addr, (char *) mfp->addr, (char *) mfp->addr + length);
+ }
+ // From a malloc
+ directstreambuf(afio::handle_ptr _h, std::shared_ptr<file_buffer_type> _buffer, size_t length) : h(std::move(_h)), buffer(std::move(_buffer))
+ {
+ // Set the get buffer this streambuf is to use
+ setg(buffer->data(), buffer->data(), buffer->data() + length);
+ }
+ };
+ std::unique_ptr<directstreambuf> buf;
+ template<class U> idirectstream(afio::handle_ptr h, U &&buffer, size_t length) : std::istream(new directstreambuf(std::move(h), std::forward<U>(buffer), length)), buf(static_cast<directstreambuf *>(rdbuf()))
+ {
+ }
+ virtual ~idirectstream() override
+ {
+ // Reset the stream before deleting the buffer
+ rdbuf(nullptr);
+ }
+};
+
+// An iostream which writes to an AFIO file in 4Kb pages
+struct odirectstream : public std::ostream
+{
+ struct directstreambuf : public std::streambuf
+ {
+ using int_type = std::streambuf::int_type;
+ using traits_type = std::streambuf::traits_type;
+ afio::future<> lastwrite; // the last async write performed
+ afio::off_t offset; // offset of next write
+ file_buffer_type buffer; // a page size on this machine
+ file_buffer_type lastbuffer;
+ directstreambuf(afio::handle_ptr _h) : lastwrite(std::move(_h)), offset(0), buffer(afio::utils::page_sizes().front())
+ {
+ // Set the put buffer this streambuf is to use
+ setp(buffer.data(), buffer.data() + buffer.size());
+ }
+ virtual ~directstreambuf() override
+ {
+ try
+ {
+ // Flush buffers and wait until last write completes
+ // Schedule an asynchronous write of the buffer to storage
+ size_t thisbuffer = pptr() - pbase();
+ if(thisbuffer)
+ lastwrite = afio::async_write(afio::async_truncate(lastwrite, offset+thisbuffer), buffer.data(), thisbuffer, offset);
+ lastwrite.get();
+ // TODO: On Windows do I need to close and reopen the file to flush metadata before
+ // the rename or does the rename do it for me?
+ // Get handle to the parent directory
+ auto dirh(lastwrite->container());
+ // Atomically rename "tmpXXXXXXXXXXXXXXXX" to "0"
+ lastwrite->atomic_relink(afio::path_req::relative(dirh, "0"));
+#ifdef __linux__
+ // Journalled Linux filing systems don't need this, but if you enabled afio::file_flags::always_sync
+ // you might want to issue this too.
+ afio::sync(dirh);
+#endif
+ }
+ catch(...)
+ {
+ }
+ }
+ virtual int_type overflow(int_type c) override
+ {
+ size_t thisbuffer=pptr()-pbase();
+ if(thisbuffer>=buffer.size())
+ sync();
+ if(c!=traits_type::eof())
+ {
+ *pptr()=(char)c;
+ pbump(1);
+ return traits_type::to_int_type(c);
+ }
+ return traits_type::eof();
+ }
+ virtual int sync() override
+ {
+ // Wait for the last write to complete, propagating any exceptions
+ lastwrite.get();
+ size_t thisbuffer=pptr()-pbase();
+ if(thisbuffer > 0)
+ {
+ // Detach the current buffer and replace with a fresh one to allow the kernel to steal the page
+ lastbuffer=std::move(buffer);
+ buffer.resize(lastbuffer.size());
+ setp(buffer.data(), buffer.data() + buffer.size());
+ // Schedule an extension of physical storage by an extra page
+ lastwrite = afio::async_truncate(lastwrite, offset + thisbuffer);
+ // Schedule an asynchronous write of the buffer to storage
+ lastwrite=afio::async_write(lastwrite, lastbuffer.data(), thisbuffer, offset);
+ offset+=thisbuffer;
+ }
+ return 0;
+ }
+ };
+ std::unique_ptr<directstreambuf> buf;
+ odirectstream(afio::handle_ptr h) : std::ostream(new directstreambuf(std::move(h))), buf(static_cast<directstreambuf *>(rdbuf()))
+ {
+ }
+ virtual ~odirectstream() override
+ {
+ // Reset the stream before deleting the buffer
+ rdbuf(nullptr);
+ }
+};
+//]
+
+//[workshop_atomic_updates_afio1]
+namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+using BOOST_OUTCOME_V1_NAMESPACE::empty;
+using BOOST_AFIO_V2_NAMESPACE::error_code;
+using BOOST_AFIO_V2_NAMESPACE::generic_category;
+
+static bool is_valid_name(const std::string &name) noexcept
+{
+ static const std::string banned("<>:\"/\\|?*\0", 10);
+ if(std::string::npos!=name.find_first_of(banned))
+ return false;
+ // No leading period
+ return name[0]!='.';
+}
+
+// Keep a cache of crypto strong random names
+static std::string random_name()
+{
+ static struct random_names_type
+ {
+ std::vector<std::string> names;
+ size_t idx;
+ random_names_type(size_t count) : names(count), idx(0)
+ {
+ for(size_t n=0; n<count; n++)
+ names[n]=afio::utils::random_string(16); // 128 bits
+ }
+ std::string get()
+ {
+ if(idx==names.size())
+ idx=0;
+ return names[idx++];
+ }
+ } random_names(10000);
+ return random_names.get();
+}
+
+data_store::data_store(size_t flags, afio::path path)
+{
+ // Make a dispatcher for the local filesystem URI, masking out write flags on all operations if not writeable
+ _dispatcher=afio::make_dispatcher("file:///", afio::file_flags::none, !(flags & writeable) ? afio::file_flags::write : afio::file_flags::none).get();
+ // Set the dispatcher for this thread, and open a handle to the store directory
+ afio::current_dispatcher_guard h(_dispatcher);
+ _store=afio::dir(std::move(path), afio::file_flags::create); // throws if there was an error
+ // Precalculate the cache of random names
+ random_name();
+}
+
+shared_future<data_store::istream> data_store::lookup(std::string name) noexcept
+{
+ if(!is_valid_name(name))
+ return error_code(EINVAL, generic_category());
+ try
+ {
+ name.append("/0");
+ // Schedule the opening of the file for reading
+ afio::future<> h(afio::async_file(_store, name, afio::file_flags::read));
+ // When it completes, call this continuation
+ return h.then([](afio::future<> &_h) -> shared_future<data_store::istream> {
+ // If file didn't open, return the error or exception immediately
+ BOOST_OUTCOME_PROPAGATE(_h);
+ size_t length=(size_t) _h->lstat(afio::metadata_flags::size).st_size;
+ // Is a memory map more appropriate?
+ if(length>=128*1024)
+ {
+ afio::handle::mapped_file_ptr mfp;
+ if((mfp=_h->map_file()))
+ {
+ data_store::istream ret(std::make_shared<idirectstream>(_h.get_handle(), std::move(mfp), length));
+ return ret;
+ }
+ }
+ // Schedule the reading of the file into a buffer
+ auto buffer=std::make_shared<file_buffer_type>(length);
+ afio::future<> h(afio::async_read(_h, buffer->data(), length, 0));
+ // When the read completes call this continuation
+ return h.then([buffer, length](const afio::future<> &h) -> shared_future<data_store::istream> {
+ // If read failed, return the error or exception immediately
+ BOOST_OUTCOME_PROPAGATE(h);
+ data_store::istream ret(std::make_shared<idirectstream>(h.get_handle(), buffer, length));
+ return ret;
+ });
+ });
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+}
+//]
+
+//[workshop_atomic_updates_afio2]
+shared_future<data_store::ostream> data_store::write(std::string name) noexcept
+{
+ if(!is_valid_name(name))
+ return error_code(EINVAL, generic_category());
+ try
+ {
+ // Schedule the opening of the directory
+ afio::future<> dirh(afio::async_dir(_store, name, afio::file_flags::create));
+#ifdef __linux__
+ // Flush metadata on Linux only. This will be a noop unless we created a new directory
+ // above, and if we don't flush the new key directory it and its contents may not appear
+ // in the store directory after a suddenly power loss, even if it and its contents are
+ // all on physical storage.
+ dirh.then([this](const afio::future<> &h) { async_sync(_store); });
+#endif
+ // Make a crypto strong random file name
+ std::string randomname("tmp");
+ randomname.append(random_name());
+ // Schedule the opening of the file for writing
+ afio::future<> h(afio::async_file(dirh, randomname, afio::file_flags::create | afio::file_flags::write
+ | afio::file_flags::hold_parent_open // handle should keep a handle_ptr to its parent dir
+ /*| afio::file_flags::always_sync*/ // writes don't complete until upon physical storage
+ ));
+ // When it completes, call this continuation
+ return h.then([](const afio::future<> &h) -> shared_future<data_store::ostream> {
+ // If file didn't open, return the error or exception immediately
+ BOOST_OUTCOME_PROPAGATE(h);
+ // Create an ostream which directly uses the file.
+ data_store::ostream ret(std::make_shared<odirectstream>(h.get_handle()));
+ return std::move(ret);
+ });
+ }
+ catch (...)
+ {
+ return std::current_exception();
+ }
+}
+//]
+
+int main(void)
+{
+ // To write a key-value item called "dog"
+ {
+ data_store ds;
+ auto dogh = ds.write("dog").get();
+ auto &dogs = *dogh;
+ dogs << "I am a dog";
+ }
+
+ // To retrieve a key-value item called "dog"
+ {
+ data_store ds;
+ auto dogh = ds.lookup("dog");
+ if (dogh.empty())
+ std::cerr << "No item called dog" << std::endl;
+ else if(dogh.has_error())
+ std::cerr << "ERROR: Looking up dog returned error " << dogh.get_error().message() << std::endl;
+ else if (dogh.has_exception())
+ {
+ std::cerr << "FATAL: Looking up dog threw exception" << std::endl;
+ std::terminate();
+ }
+ else
+ {
+ std::string buffer;
+ *dogh.get() >> buffer;
+ std::cout << "Item dog has value " << buffer << std::endl;
+ }
+ }
+ return 0;
+}
diff --git a/attic/example/workshop_benchmark.cpp b/attic/example/workshop_benchmark.cpp
new file mode 100644
index 00000000..e3f2d58d
--- /dev/null
+++ b/attic/example/workshop_benchmark.cpp
@@ -0,0 +1,151 @@
+#include "afio_pch.hpp"
+
+#ifdef _DEBUG
+#define ITEMS 64
+#else
+#define ITEMS 65536
+#endif
+#define PARALLELISM 256 // up to max fds on your platform
+
+namespace iostreams {
+#include "workshop_naive.ipp"
+}
+namespace naive {
+#include "workshop_naive_afio.ipp"
+}
+namespace atomic_updates {
+#include "workshop_atomic_updates_afio.ipp"
+}
+namespace final {
+#include "workshop_final_afio.ipp"
+#include "../detail/SpookyV2.cpp"
+}
+
+
+namespace filesystem = BOOST_AFIO_V2_NAMESPACE::filesystem;
+namespace chrono = BOOST_AFIO_V2_NAMESPACE::chrono;
+using BOOST_AFIO_V2_NAMESPACE::ratio;
+
+// From http://burtleburtle.net/bob/rand/smallprng.html
+typedef unsigned int u4;
+typedef struct ranctx { u4 a; u4 b; u4 c; u4 d; } ranctx;
+
+#define rot(x,k) (((x)<<(k))|((x)>>(32-(k))))
+static u4 ranval(ranctx *x) {
+ u4 e = x->a - rot(x->b, 27);
+ x->a = x->b ^ rot(x->c, 17);
+ x->b = x->c + x->d;
+ x->c = x->d + e;
+ x->d = e + x->a;
+ return x->d;
+}
+
+static void raninit(ranctx *x, u4 seed) {
+ u4 i;
+ x->a = 0xf1ea5eed, x->b = x->c = x->d = seed;
+ for (i = 0; i < 20; ++i) {
+ (void) ranval(x);
+ }
+}
+
+static std::vector<std::pair<ranctx, unsigned>> items;
+
+void prepare()
+{
+ ranctx gen;
+ raninit(&gen, 0x78adbcff);
+ items.clear();
+ items.reserve(ITEMS);
+ for (size_t n = 0; n < ITEMS; n++)
+ {
+ unsigned t=ranval(&gen);
+ items.push_back(std::make_pair(gen, t));
+ }
+}
+
+template<class data_store> void benchmark(const char *filename, const char *desc, bool parallel_writes)
+{
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ std::vector<std::tuple<size_t, double, double>> results;
+ prepare();
+ for(size_t n=1; n<=ITEMS; n<<=1)
+ {
+ std::cout << "Benchmarking " << desc << " insertion of " << n << " records ... ";
+ if(filesystem::exists("store"))
+ filesystem::remove_all("store");
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<1);
+ data_store store(data_store::writeable);
+ size_t write_parallelism = parallel_writes ? PARALLELISM : 1;
+ std::vector<typename data_store::write_result_type> ops(write_parallelism);
+ begin=chrono::high_resolution_clock::now();
+ for (size_t m = 0; m < n; m += write_parallelism)
+ {
+ int todo = (int)(n < write_parallelism ? n : write_parallelism);
+#pragma omp parallel for
+ for (int o = 0; o < todo; o++)
+ ops[o] = store.write(std::to_string(m + (size_t)o));
+#pragma omp parallel for
+ for (int o = 0; o < todo; o++)
+ {
+ *ops[o].get() << items[m + (size_t)o].second;
+ ops[o].get()->flush();
+ }
+ // Some dumping of the writes should have happened by now
+#pragma omp parallel for
+ for (int o = 0; o < todo; o++)
+ ops[o]=typename data_store::write_result_type();
+ }
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin).count();
+ std::cout << (n/diff) << " items/sec" << std::endl;
+ results.push_back(std::make_tuple(n, n/diff, 0));
+ }
+ auto resultsit = results.begin();
+ for (size_t n = 1; n <= ITEMS; n <<= 1)
+ {
+ std::cout << "Benchmarking " << desc << " lookup of " << n << " records ... ";
+ data_store store;
+ std::vector<typename data_store::lookup_result_type> ops(PARALLELISM);
+ auto begin = chrono::high_resolution_clock::now();
+ for (size_t m = 0; m < n; m += PARALLELISM)
+ {
+ int todo = n < PARALLELISM ? n : PARALLELISM;
+#pragma omp parallel for
+ for (int o = 0; o < todo; o++)
+ ops[o] = store.lookup(std::to_string(m + (size_t) o));
+ // Some readahead should have happened by now
+#pragma omp parallel for
+ for (int o = 0; o < todo; o++)
+ {
+ auto s(ops[o].get());
+ unsigned t;
+ *s >> t;
+ if (t != items[m+(size_t) o].second)
+ std::cerr << "ERROR: Item " << m << " has incorrect contents!" << std::endl;
+ ops[o]=typename data_store::lookup_result_type();
+ }
+ }
+ auto end = chrono::high_resolution_clock::now();
+ auto diff = chrono::duration_cast<secs_type>(end - begin).count();
+ std::cout << (n / diff) << " items/sec" << std::endl;
+ std::get<2>(*resultsit++) = n / diff;
+ }
+ std::ofstream csvh(filename);
+ csvh << "Items,Inserts/sec,Lookups/sec" << std::endl;
+ for(auto &i : results)
+ csvh << std::get<0>(i) << "," << std::get<1>(i) << "," << std::get<2>(i) << std::endl;
+}
+
+int main(void)
+{
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<3);
+
+ //benchmark<iostreams::data_store>("iostreams.csv", "STL iostreams", true);
+ //benchmark<naive::data_store>("afio_naive.csv", "AFIO naive", true);
+ //benchmark<atomic_updates::data_store>("afio_atomic.csv", "AFIO atomic update", true);
+ benchmark<final::data_store>("afio_final.csv", "AFIO single file", true);
+ return 0;
+} \ No newline at end of file
diff --git a/attic/example/workshop_final_afio.cpp b/attic/example/workshop_final_afio.cpp
new file mode 100644
index 00000000..c3fcceb9
--- /dev/null
+++ b/attic/example/workshop_final_afio.cpp
@@ -0,0 +1,2 @@
+#include "afio_pch.hpp"
+#include "workshop_final_afio.ipp"
diff --git a/attic/example/workshop_final_afio.ipp b/attic/example/workshop_final_afio.ipp
new file mode 100644
index 00000000..655b8367
--- /dev/null
+++ b/attic/example/workshop_final_afio.ipp
@@ -0,0 +1,665 @@
+#include "../detail/SpookyV2.h"
+
+//[workshop_final_interface
+namespace afio = BOOST_AFIO_V2_NAMESPACE;
+namespace filesystem = BOOST_AFIO_V2_NAMESPACE::filesystem;
+using BOOST_OUTCOME_V1_NAMESPACE::outcome;
+using BOOST_OUTCOME_V1_NAMESPACE::lightweight_futures::shared_future;
+
+class data_store
+{
+ struct _ostream;
+ friend struct _ostream;
+ afio::dispatcher_ptr _dispatcher;
+ // The small blob store keeps non-memory mappable blobs at 32 byte alignments
+ afio::handle_ptr _small_blob_store, _small_blob_store_append, _small_blob_store_ecc;
+ // The large blob store keeps memory mappable blobs at 4Kb alignments
+ afio::handle_ptr _large_blob_store, _large_blob_store_append, _large_blob_store_ecc;
+ // The index is where we keep the map of keys to blobs
+ afio::handle_ptr _index_store, _index_store_append, _index_store_ecc;
+ struct index;
+ std::unique_ptr<index> _index;
+public:
+ // Type used for read streams
+ using istream = std::shared_ptr<std::istream>;
+ // Type used for write streams
+ using ostream = std::shared_ptr<std::ostream>;
+ // Type used for lookup
+ using lookup_result_type = shared_future<istream>;
+ // Type used for write
+ using write_result_type = outcome<ostream>;
+
+ // Disposition flags
+ static constexpr size_t writeable = (1<<0);
+
+ // Open a data store at path
+ data_store(size_t flags = 0, afio::path path = "store");
+
+ // Look up item named name for reading, returning an istream for the item
+ shared_future<istream> lookup(std::string name) noexcept;
+ // Look up item named name for writing, returning an ostream for that item
+ outcome<ostream> write(std::string name) noexcept;
+};
+//]
+
+//[workshop_final1]
+namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+using BOOST_AFIO_V2_NAMESPACE::error_code;
+using BOOST_AFIO_V2_NAMESPACE::generic_category;
+using BOOST_OUTCOME_V1_NAMESPACE::outcome;
+
+// A special allocator of highly efficient file i/o memory
+using file_buffer_type = std::vector<char, afio::utils::page_allocator<char>>;
+
+// Serialisation helper types
+#pragma pack(push, 1)
+struct ondisk_file_header // 20 bytes
+{
+ union
+ {
+ afio::off_t length; // Always 32 in byte order of whoever wrote this
+ char endian[8];
+ };
+ afio::off_t index_offset_begin; // Hint to the length of the store when the index was last written
+ unsigned int time_count; // Racy monotonically increasing count
+};
+struct ondisk_record_header // 28 bytes - ALWAYS ALIGNED TO 32 BYTE FILE OFFSET
+{
+ afio::off_t magic : 16; // 0xad magic
+ afio::off_t kind : 2; // 0 for zeroed space, 1,2 for blob, 3 for index
+ afio::off_t _spare : 6;
+ afio::off_t length : 40; // Size of the object (including this preamble, regions, key values) (+8)
+ unsigned int age; // file header time_count when this was added (+12)
+ uint64 hash[2]; // 128-bit SpookyHash of the object (from below onwards) (+28)
+ // ondisk_index_regions
+ // ondisk_index_key_value (many until length)
+};
+struct ondisk_index_regions // 12 + regions_size * 32
+{
+ afio::off_t thisoffset; // this index only valid if equals this offset
+ unsigned int regions_size; // count of regions with their status (+28)
+ struct ondisk_index_region
+ {
+ afio::off_t offset; // offset to this region
+ ondisk_record_header r; // copy of the header at the offset to avoid a disk seek
+ } regions[1];
+};
+struct ondisk_index_key_value // 8 + sizeof(key)
+{
+ unsigned int region_index; // Index into regions
+ unsigned int name_size; // Length of key
+ char name[1]; // Key string (utf-8)
+};
+#pragma pack(pop)
+
+struct data_store::index
+{
+ struct region
+ {
+ enum kind_type { zeroed=0, small_blob=1, large_blob=2, index=3 } kind;
+ afio::off_t offset, length;
+ uint64 hash[2];
+ region(ondisk_index_regions::ondisk_index_region *r) : kind(static_cast<kind_type>(r->r.kind)), offset(r->offset), length(r->r.length) { memcpy(hash, r->r.hash, sizeof(hash)); }
+ bool operator<(const region &o) const noexcept { return offset<o.offset; }
+ bool operator==(const region &o) const noexcept { return offset==o.offset && length==o.length; }
+ };
+ afio::off_t offset_loaded_from; // Offset this index was loaded from
+ unsigned int last_time_count; // Header time count
+ std::vector<region> regions;
+ std::unordered_map<std::string, size_t> key_to_region;
+ index() : offset_loaded_from(0) { }
+//]
+//[workshop_final2]
+ struct last_good_ondisk_index_info
+ {
+ afio::off_t offset;
+ std::unique_ptr<char[]> buffer;
+ size_t size;
+ last_good_ondisk_index_info() : offset(0), size(0) { }
+ };
+ // Finds the last good index in the store
+ outcome<last_good_ondisk_index_info> find_last_good_ondisk_index(afio::handle_ptr h) noexcept
+ {
+ last_good_ondisk_index_info ret;
+ error_code ec;
+ try
+ {
+ // Read the front of the store file to get the index offset hint
+ ondisk_file_header header;
+ afio::read(h, header, 0);
+ afio::off_t offset=0;
+ if(header.length==32)
+ offset=header.index_offset_begin;
+ else if(header.endian[0]==32) // wrong endian
+ return error_code(ENOEXEC, generic_category());
+ else // corrupted
+ return error_code(ENOTSUP, generic_category());
+ last_time_count=header.time_count;
+ // Fetch the valid extents
+ auto valid_extents(afio::extents(h));
+ auto valid_extents_it=valid_extents.begin();
+ // Iterate the records starting from index offset hint, keeping track of last known good index
+ bool done=true;
+ do
+ {
+ afio::off_t linear_scanning=0;
+ ondisk_record_header record;
+ afio::off_t file_length=h->lstat(afio::metadata_flags::size).st_size;
+ for(; offset<file_length;)
+ {
+ // Round to 32 byte boundary
+ offset&=~31ULL;
+ // Find my valid extent
+ while(offset>=valid_extents_it->first+valid_extents_it->second)
+ {
+ if(valid_extents.end()==++valid_extents_it)
+ {
+ valid_extents=afio::extents(h);
+ valid_extents_it=valid_extents.begin();
+ }
+ }
+ // Is this offset within a valid extent? If not, bump it.
+ if(offset<valid_extents_it->first)
+ offset=valid_extents_it->first;
+ afio::read(ec, h, record, offset);
+ if(ec) return ec;
+ // If this does not contain a valid record, linear scan
+ // until we find one
+ if(record.magic!=0xad || (record.length & 31))
+ {
+start_linear_scan:
+ if(!linear_scanning)
+ {
+ std::cerr << "WARNING: Corrupt record detected at " << offset << ", linear scanning ..." << std::endl;
+ linear_scanning=offset;
+ }
+ offset+=32;
+ continue;
+ }
+ // Is this the first good record after a corrupted section?
+ if(linear_scanning)
+ {
+ std::cerr << "NOTE: Found valid record after linear scan at " << offset << std::endl;
+ std::cerr << " Removing invalid data between " << linear_scanning << " and " << offset << std::endl;
+ // Rewrite a valid record to span the invalid section
+ ondisk_record_header temp={0};
+ temp.magic=0xad;
+ temp.length=offset-linear_scanning;
+ temp.age=last_time_count;
+ afio::write(ec, h, temp, linear_scanning);
+ // Deallocate the physical storage for the invalid section
+ afio::zero(ec, h, {{linear_scanning+12, offset-linear_scanning-12}});
+ linear_scanning=0;
+ }
+ // If not an index, skip entire record
+ if(record.kind!=3 /*index*/)
+ {
+ // If this record length is wrong, start a linear scan
+ if(record.length>file_length-offset)
+ offset+=32;
+ else
+ offset+=record.length;
+ continue;
+ }
+ std::unique_ptr<char[]> buffer;
+ // If record.length is corrupt, this will throw bad_alloc
+ try
+ {
+ buffer.reset(new char[(size_t) record.length-sizeof(header)]);
+ }
+ catch(...)
+ {
+ // Either we are out of memory, or it's a corrupted record
+ // TODO: Try a ECC heal pass here
+ // If this record length is wrong, start a linear scan
+ if(record.length>file_length-offset)
+ goto start_linear_scan;
+ else
+ offset+=record.length;
+ continue;
+ }
+ afio::read(ec, h, buffer.get(), (size_t) record.length-sizeof(header), offset+sizeof(header));
+ if(ec)
+ return ec;
+ uint64 hash[2]={0, 0};
+ SpookyHash::Hash128(buffer.get(), (size_t) record.length-sizeof(header), hash, hash+1);
+ // Is this record correct?
+ if(!memcmp(hash, record.hash, sizeof(hash)))
+ {
+ // Is this index not conflicted? If not, it's the new most recent index
+ ondisk_index_regions *r=(ondisk_index_regions *) buffer.get();
+ if(r->thisoffset==offset)
+ {
+ ret.buffer=std::move(buffer);
+ ret.size=(size_t) record.length-sizeof(header);
+ ret.offset=offset;
+ }
+ }
+ offset+=record.length;
+ }
+ if(ret.offset) // we have a valid most recent index so we're done
+ done=true;
+ else if(header.index_offset_begin>sizeof(header))
+ {
+ // Looks like the end of the store got trashed.
+ // Reparse from the very beginning
+ offset=32;
+ header.index_offset_begin=0;
+ }
+ else
+ {
+ // No viable records at all, or empty store.
+ done=true;
+ }
+ } while(!done);
+ return ret;
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+ }
+//]
+//[workshop_final3]
+ // Loads the index from the store
+ outcome<void> load(afio::handle_ptr h) noexcept
+ {
+ // If find_last_good_ondisk_index() returns error or exception, return those, else
+ // initialise ondisk_index_info to monad.get()
+ BOOST_OUTCOME_AUTO(ondisk_index_info, find_last_good_ondisk_index(h));
+ error_code ec;
+ try
+ {
+ offset_loaded_from=0;
+ regions.clear();
+ key_to_region.clear();
+ ondisk_index_regions *r=(ondisk_index_regions *) ondisk_index_info.buffer.get();
+ regions.reserve(r->regions_size);
+ for(size_t n=0; n<r->regions_size; n++)
+ regions.push_back(region(r->regions+n));
+ ondisk_index_key_value *k=(ondisk_index_key_value *)(r+r->regions_size), *end=(ondisk_index_key_value *)(ondisk_index_info.buffer.get()+ondisk_index_info.size);
+ while(k<end)
+ key_to_region.insert(std::make_pair(std::string(k->name, k->name_size), k->region_index));
+ offset_loaded_from=ondisk_index_info.offset;
+ return {};
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+ }
+//]
+//[workshop_final4]
+ // Writes the index to the store
+ outcome<void> store(afio::handle_ptr rwh, afio::handle_ptr appendh) noexcept
+ {
+ error_code ec;
+ std::vector<ondisk_index_regions::ondisk_index_region> ondisk_regions;
+ ondisk_regions.reserve(65536);
+ for(auto &i : regions)
+ {
+ ondisk_index_regions::ondisk_index_region r;
+ r.offset=i.offset;
+ r.r.kind=i.kind;
+ r.r.length=i.length;
+ memcpy(r.r.hash, i.hash, sizeof(i.hash));
+ ondisk_regions.push_back(r);
+ }
+
+ size_t bytes=0;
+ for(auto &i : key_to_region)
+ bytes+=8+i.first.size();
+ std::vector<char> ondisk_key_values(bytes);
+ ondisk_index_key_value *kv=(ondisk_index_key_value *) ondisk_key_values.data();
+ for(auto &i : key_to_region)
+ {
+ kv->region_index=(unsigned int) i.second;
+ kv->name_size=(unsigned int) i.first.size();
+ memcpy(kv->name, i.first.c_str(), i.first.size());
+ kv=(ondisk_index_key_value *)(((char *) kv) + 8 + i.first.size());
+ }
+
+ struct
+ {
+ ondisk_record_header header;
+ ondisk_index_regions header2;
+ } h;
+ h.header.magic=0xad;
+ h.header.kind=3; // writing an index
+ h.header._spare=0;
+ h.header.length=sizeof(h.header)+sizeof(h.header2)+sizeof(ondisk_regions.front())*(regions.size()-1)+bytes;
+ h.header.age=last_time_count;
+ // hash calculated later
+ // thisoffset calculated later
+ h.header2.regions_size=(unsigned int) regions.size();
+ // Pad zeros to 32 byte multiple
+ std::vector<char> zeros((h.header.length+31)&~31ULL);
+ h.header.length+=zeros.size();
+
+ // Create the gather buffer sequence
+ std::vector<asio::const_buffer> buffers(4);
+ buffers[0]=asio::const_buffer(&h, 36);
+ buffers[1]=asio::const_buffer(ondisk_regions.data(), sizeof(ondisk_regions.front())*ondisk_regions.size());
+ buffers[2]=asio::const_buffer(ondisk_key_values.data(), ondisk_key_values.size());
+ if(zeros.empty())
+ buffers.pop_back();
+ else
+ buffers[3]=asio::const_buffer(zeros.data(), zeros.size());
+ file_buffer_type reread(sizeof(h));
+ ondisk_record_header *reread_header=(ondisk_record_header *) reread.data();
+ bool success=false;
+ do
+ {
+ // Is this index stale?
+ BOOST_OUTCOME_AUTO(ondisk_index_info, find_last_good_ondisk_index(rwh));
+ if(ondisk_index_info.offset!=offset_loaded_from)
+ {
+ // A better conflict resolution strategy might check to see if deltas
+ // are compatible, but for the sake of brevity we always report conflict
+ return error_code(EDEADLK, generic_category());
+ }
+ // Take the current length of the store file. Any index written will be at or after this.
+ h.header2.thisoffset=appendh->lstat(afio::metadata_flags::size).st_size;
+ memset(h.header.hash, 0, sizeof(h.header.hash));
+ // Hash the end of the first gather buffer and all the remaining gather buffers
+ SpookyHash::Hash128(asio::buffer_cast<const char *>(buffers[0])+24, asio::buffer_size(buffers[0])-24, h.header.hash, h.header.hash+1);
+ SpookyHash::Hash128(asio::buffer_cast<const char *>(buffers[1]), asio::buffer_size(buffers[1]), h.header.hash, h.header.hash+1);
+ SpookyHash::Hash128(asio::buffer_cast<const char *>(buffers[2]), asio::buffer_size(buffers[2]), h.header.hash, h.header.hash+1);
+ if(buffers.size()>3)
+ SpookyHash::Hash128(asio::buffer_cast<const char *>(buffers[3]), asio::buffer_size(buffers[3]), h.header.hash, h.header.hash+1);
+ // Atomic append the record
+ afio::write(ec, appendh, buffers, 0);
+ if(ec) return ec;
+ // Reread the record
+ afio::read(ec, rwh, reread.data(), reread.size(), h.header2.thisoffset);
+ if(ec) return ec;
+ // If the record doesn't match it could be due to a lag in st_size between open handles,
+ // so retry until success or stale index
+ } while(memcmp(reread_header->hash, h.header.hash, sizeof(h.header.hash)));
+
+ // New index has been successfully written. Update the hint at the front of the file.
+ // This update is racy of course, but as it's merely a hint we don't care.
+ ondisk_file_header file_header;
+ afio::read(ec, rwh, file_header, 0);
+ if(!ec && file_header.index_offset_begin<h.header2.thisoffset)
+ {
+ file_header.index_offset_begin=h.header2.thisoffset;
+ file_header.time_count++;
+ afio::write(ec, rwh, file_header, 0);
+ }
+ offset_loaded_from=h.header2.thisoffset;
+ last_time_count=file_header.time_count;
+ return {};
+ }
+//]
+//[workshop_final5]
+ // Reloads the index if needed
+ outcome<void> refresh(afio::handle_ptr h) noexcept
+ {
+ static afio::off_t last_size;
+ error_code ec;
+ afio::off_t size=h->lstat(afio::metadata_flags::size).st_size;
+ // Has the size changed? If so, need to check the hint
+ if(size>last_size)
+ {
+ last_size=size;
+ ondisk_file_header header;
+ afio::read(ec, h, header, 0);
+ if(ec) return ec;
+ // If the hint is moved, we are stale
+ if(header.index_offset_begin>offset_loaded_from)
+ return load(h);
+ }
+ return {};
+ }
+};
+//]
+
+//[workshop_final6]
+namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+using BOOST_AFIO_V2_NAMESPACE::error_code;
+using BOOST_AFIO_V2_NAMESPACE::generic_category;
+
+// A special allocator of highly efficient file i/o memory
+using file_buffer_type = std::vector<char, afio::utils::page_allocator<char>>;
+
+// An iostream which reads directly from a memory mapped AFIO file
+struct idirectstream : public std::istream
+{
+ struct directstreambuf : public std::streambuf
+ {
+ afio::handle_ptr h; // Holds the file open
+ std::shared_ptr<file_buffer_type> buffer;
+ // From a mmap
+ directstreambuf(afio::handle_ptr _h, char *addr, size_t length) : h(std::move(_h))
+ {
+ // Set the get buffer this streambuf is to use
+ setg(addr, addr, addr + length);
+ }
+ // From a malloc
+ directstreambuf(afio::handle_ptr _h, std::shared_ptr<file_buffer_type> _buffer, size_t length) : h(std::move(_h)), buffer(std::move(_buffer))
+ {
+ // Set the get buffer this streambuf is to use
+ setg(buffer->data(), buffer->data(), buffer->data() + length);
+ }
+ };
+ std::unique_ptr<directstreambuf> buf;
+ template<class U> idirectstream(afio::handle_ptr h, U &&buffer, size_t length) : std::istream(new directstreambuf(std::move(h), std::forward<U>(buffer), length)), buf(static_cast<directstreambuf *>(rdbuf()))
+ {
+ }
+ virtual ~idirectstream() override
+ {
+ // Reset the stream before deleting the buffer
+ rdbuf(nullptr);
+ }
+};
+//]
+//[workshop_final7]
+// An iostream which buffers all the output, then commits on destruct
+struct data_store::_ostream : public std::ostream
+{
+ struct ostreambuf : public std::streambuf
+ {
+ using int_type = std::streambuf::int_type;
+ using traits_type = std::streambuf::traits_type;
+ data_store *ds;
+ std::string name;
+ file_buffer_type buffer;
+ ostreambuf(data_store *_ds, std::string _name) : ds(_ds), name(std::move(_name)), buffer(afio::utils::page_sizes().front())
+ {
+ // Set the put buffer this streambuf is to use
+ setp(buffer.data(), buffer.data() + buffer.size());
+ }
+ virtual ~ostreambuf() override
+ {
+ try
+ {
+ ondisk_index_regions::ondisk_index_region r;
+ r.r.magic=0xad;
+ r.r.kind=1; // small blob
+ r.r.length=sizeof(r.r)+buffer.size();
+ r.r.length=(r.r.length+31)&~31ULL; // pad to 32 byte multiple
+ r.r.age=ds->_index->last_time_count;
+ memset(r.r.hash, 0, sizeof(r.r.hash));
+ SpookyHash::Hash128(buffer.data(), (size_t)(r.r.length-sizeof(r.r)), r.r.hash, r.r.hash+1);
+
+ // Create the gather buffer sequence and atomic append the blob
+ std::vector<asio::const_buffer> buffers(2);
+ buffers[0]=asio::const_buffer((char *) &r.r, sizeof(r.r));
+ buffers[1]=asio::const_buffer(buffer.data(), (size_t)(r.r.length-sizeof(r.r)));
+ error_code ec;
+ auto offset=ds->_small_blob_store_append->lstat(afio::metadata_flags::size).st_size;
+ afio::write(ec, ds->_small_blob_store_append, buffers, 0);
+ if(ec)
+ abort(); // should really do something better here
+
+ // Find out where my blob ended up
+ ondisk_record_header header;
+ do
+ {
+ afio::read(ec, ds->_small_blob_store_append, header, offset);
+ if(ec) abort();
+ if(header.kind==1 /*small blob*/ && !memcmp(header.hash, r.r.hash, sizeof(header.hash)))
+ {
+ r.offset=offset;
+ break;
+ }
+ offset+=header.length;
+ } while(offset<ds->_small_blob_store_append->lstat(afio::metadata_flags::size).st_size);
+
+ for(;;)
+ {
+ // Add my blob to the regions
+ ds->_index->regions.push_back(&r);
+ // Add my key to the key index
+ ds->_index->key_to_region[name]=ds->_index->regions.size()-1;
+ // Commit the index, and if successful exit
+ if(!ds->_index->store(ds->_index_store, ds->_index_store_append))
+ return;
+ // Reload the index and retry
+ if(ds->_index->load(ds->_index_store))
+ abort();
+ }
+ }
+ catch(...)
+ {
+ }
+ }
+ virtual int_type overflow(int_type c) override
+ {
+ size_t thisbuffer=pptr()-pbase();
+ if(thisbuffer>=buffer.size())
+ sync();
+ if(c!=traits_type::eof())
+ {
+ *pptr()=(char)c;
+ pbump(1);
+ return traits_type::to_int_type(c);
+ }
+ return traits_type::eof();
+ }
+ virtual int sync() override
+ {
+ buffer.resize(buffer.size()*2);
+ setp(buffer.data() + buffer.size()/2, buffer.data() + buffer.size());
+ return 0;
+ }
+ };
+ std::unique_ptr<ostreambuf> buf;
+ _ostream(data_store *ds, std::string name) : std::ostream(new ostreambuf(ds, std::move(name))), buf(static_cast<ostreambuf *>(rdbuf()))
+ {
+ }
+ virtual ~_ostream() override
+ {
+ // Reset the stream before deleting the buffer
+ rdbuf(nullptr);
+ }
+};
+//]
+
+//[workshop_final8]
+data_store::data_store(size_t flags, afio::path path)
+{
+ // Make a dispatcher for the local filesystem URI, masking out write flags on all operations if not writeable
+ _dispatcher=afio::make_dispatcher("file:///", afio::file_flags::none, !(flags & writeable) ? afio::file_flags::write : afio::file_flags::none).get();
+ // Set the dispatcher for this thread, and create/open a handle to the store directory
+ afio::current_dispatcher_guard h(_dispatcher);
+ auto dirh(afio::dir(std::move(path), afio::file_flags::create)); // throws if there was an error
+
+ // The small blob store keeps non-memory mappable blobs at 32 byte alignments
+ _small_blob_store_append=afio::file(dirh, "small_blob_store", afio::file_flags::create | afio::file_flags::append); // throws if there was an error
+ _small_blob_store=afio::file(dirh, "small_blob_store", afio::file_flags::read_write); // throws if there was an error
+ _small_blob_store_ecc=afio::file(dirh, "small_blob_store.ecc", afio::file_flags::create | afio::file_flags::read_write); // throws if there was an error
+
+ // The large blob store keeps memory mappable blobs at 4Kb alignments
+ // TODO
+
+ // The index is where we keep the map of keys to blobs
+ _index_store_append=afio::file(dirh, "index", afio::file_flags::create | afio::file_flags::append); // throws if there was an error
+ _index_store=afio::file(dirh, "index", afio::file_flags::read_write); // throws if there was an error
+ _index_store_ecc=afio::file(dirh, "index.ecc", afio::file_flags::create | afio::file_flags::read_write); // throws if there was an error
+ // Is this store just created?
+ if(!_index_store_append->lstat(afio::metadata_flags::size).st_size)
+ {
+ ondisk_file_header header;
+ header.length=32;
+ header.index_offset_begin=32;
+ header.time_count=0;
+ // This is racy, but the file format doesn't care
+ afio::write(_index_store_append, header, 0);
+ }
+ _index.reset(new index);
+}
+
+shared_future<data_store::istream> data_store::lookup(std::string name) noexcept
+{
+ try
+ {
+ BOOST_OUTCOME_PROPAGATE(_index->refresh(_index_store));
+ auto it=_index->key_to_region.find(name);
+ if(_index->key_to_region.end()==it)
+ return error_code(ENOENT, generic_category()); // not found
+ auto &r=_index->regions[it->second];
+ afio::off_t offset=r.offset+24, length=r.length-24;
+ // Schedule the reading of the file into a buffer
+ auto buffer=std::make_shared<file_buffer_type>((size_t) length);
+ afio::future<> h(afio::async_read(_small_blob_store, buffer->data(), (size_t) length, offset));
+ // When the read completes call this continuation
+ return h.then([buffer, length](const afio::future<> &h) -> shared_future<data_store::istream> {
+ // If read failed, return the error or exception immediately
+ BOOST_OUTCOME_PROPAGATE(h);
+ data_store::istream ret(std::make_shared<idirectstream>(h.get_handle(), buffer, (size_t) length));
+ return ret;
+ });
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+}
+
+outcome<data_store::ostream> data_store::write(std::string name) noexcept
+{
+ try
+ {
+ return std::make_shared<_ostream>(this, std::move(name));
+ }
+ catch (...)
+ {
+ return std::current_exception();
+ }
+}
+//]
+
+int main(void)
+{
+ // To write a key-value item called "dog"
+ {
+ data_store ds;
+ auto dogh = ds.write("dog").get();
+ auto &dogs = *dogh;
+ dogs << "I am a dog";
+ }
+
+ // To retrieve a key-value item called "dog"
+ {
+ data_store ds;
+ auto dogh = ds.lookup("dog");
+ if (dogh.empty())
+ std::cerr << "No item called dog" << std::endl;
+ else if(dogh.has_error())
+ std::cerr << "ERROR: Looking up dog returned error " << dogh.get_error().message() << std::endl;
+ else if (dogh.has_exception())
+ {
+ std::cerr << "FATAL: Looking up dog threw exception" << std::endl;
+ std::terminate();
+ }
+ else
+ {
+ std::string buffer;
+ *dogh.get() >> buffer;
+ std::cout << "Item dog has value " << buffer << std::endl;
+ }
+ }
+ return 0;
+}
diff --git a/attic/example/workshop_naive.cpp b/attic/example/workshop_naive.cpp
new file mode 100644
index 00000000..225d09b7
--- /dev/null
+++ b/attic/example/workshop_naive.cpp
@@ -0,0 +1,2 @@
+#include "afio_pch.hpp"
+#include "workshop_naive.ipp"
diff --git a/attic/example/workshop_naive.ipp b/attic/example/workshop_naive.ipp
new file mode 100644
index 00000000..0739095b
--- /dev/null
+++ b/attic/example/workshop_naive.ipp
@@ -0,0 +1,126 @@
+//[workshop_naive_interface
+namespace filesystem = BOOST_AFIO_V2_NAMESPACE::filesystem;
+using BOOST_OUTCOME_V1_NAMESPACE::outcome;
+
+class data_store
+{
+ filesystem::path _store_path;
+ bool _writeable;
+public:
+ // Type used for read streams
+ using istream = std::shared_ptr<std::istream>;
+ // Type used for write streams
+ using ostream = std::shared_ptr<std::ostream>;
+ // Type used for lookup
+ using lookup_result_type = outcome<istream>;
+ // Type used for write
+ using write_result_type = outcome<ostream>;
+
+ // Disposition flags
+ static constexpr size_t writeable = (1<<0);
+
+ // Open a data store at path
+ data_store(size_t flags = 0, filesystem::path path = "store");
+
+ // Look up item named name for reading, returning a std::istream for the item if it exists
+ outcome<istream> lookup(std::string name) noexcept;
+ // Look up item named name for writing, returning an ostream for that item
+ outcome<ostream> write(std::string name) noexcept;
+};
+//]
+
+//[workshop_naive]
+using BOOST_OUTCOME_V1_NAMESPACE::empty;
+using BOOST_AFIO_V2_NAMESPACE::error_code;
+using BOOST_AFIO_V2_NAMESPACE::generic_category;
+
+static bool is_valid_name(const std::string &name) noexcept
+{
+ static const std::string banned("<>:\"/\\|?*\0", 10);
+ if(std::string::npos!=name.find_first_of(banned))
+ return false;
+ // No leading period
+ return name[0]!='.';
+}
+
+static std::shared_ptr<std::fstream> name_to_fstream(const filesystem::path &store_path, std::string name, std::ios::openmode mode)
+{
+ auto to_lookup(store_path / name);
+ return std::make_shared<std::fstream>(to_lookup.native(), mode);
+}
+
+data_store::data_store(size_t flags, filesystem::path path) : _store_path(std::move(path)), _writeable(flags & writeable)
+{
+}
+
+outcome<data_store::istream> data_store::lookup(std::string name) noexcept
+{
+ if(!is_valid_name(name))
+ return error_code(EINVAL, generic_category());
+ try
+ {
+ istream ret(name_to_fstream(_store_path, std::move(name), std::ios::in | std::ios::binary));
+ if(!ret->fail())
+ return std::move(ret);
+ return empty;
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+}
+
+outcome<data_store::ostream> data_store::write(std::string name) noexcept
+{
+ if(!_writeable)
+ return error_code(EROFS, generic_category());
+ if(!is_valid_name(name))
+ return error_code(EINVAL, generic_category());
+ try
+ {
+ if (!filesystem::exists(_store_path))
+ filesystem::create_directory(_store_path);
+ ostream ret(name_to_fstream(_store_path, std::move(name), std::ios::out | std::ios::binary));
+ return std::move(ret);
+ }
+ catch (...)
+ {
+ return std::current_exception();
+ }
+}
+//]
+
+int main(void)
+{
+ //[workshop_use_naive]
+ // To write a key-value item called "dog"
+ {
+ data_store ds;
+ auto dogh = ds.write("dog").get();
+ auto &dogs = *dogh;
+ dogs << "I am a dog";
+ }
+
+ // To retrieve a key-value item called "dog"
+ {
+ data_store ds;
+ auto dogh = ds.lookup("dog");
+ if (dogh.empty())
+ std::cerr << "No item called dog" << std::endl;
+ else if(dogh.has_error())
+ std::cerr << "ERROR: Looking up dog returned error " << dogh.get_error().message() << std::endl;
+ else if (dogh.has_exception())
+ {
+ std::cerr << "FATAL: Looking up dog threw exception" << std::endl;
+ std::terminate();
+ }
+ else
+ {
+ std::string buffer;
+ *dogh.get() >> buffer;
+ std::cout << "Item dog has value " << buffer << std::endl;
+ }
+ }
+ //]
+ return 0;
+}
diff --git a/attic/example/workshop_naive_afio.cpp b/attic/example/workshop_naive_afio.cpp
new file mode 100644
index 00000000..a807b6c8
--- /dev/null
+++ b/attic/example/workshop_naive_afio.cpp
@@ -0,0 +1,2 @@
+#include "afio_pch.hpp"
+#include "workshop_naive_afio.ipp"
diff --git a/attic/example/workshop_naive_afio.ipp b/attic/example/workshop_naive_afio.ipp
new file mode 100644
index 00000000..6100ed96
--- /dev/null
+++ b/attic/example/workshop_naive_afio.ipp
@@ -0,0 +1,269 @@
+//[workshop_naive_afio_interface
+namespace afio = BOOST_AFIO_V2_NAMESPACE;
+namespace filesystem = BOOST_AFIO_V2_NAMESPACE::filesystem;
+using BOOST_OUTCOME_V1_NAMESPACE::outcome;
+
+class data_store
+{
+ afio::dispatcher_ptr _dispatcher;
+ afio::handle_ptr _store;
+public:
+ // Type used for read streams
+ using istream = std::shared_ptr<std::istream>;
+ // Type used for write streams
+ using ostream = std::shared_ptr<std::ostream>;
+ // Type used for lookup
+ using lookup_result_type = outcome<istream>;
+ // Type used for write
+ using write_result_type = outcome<ostream>;
+
+ // Disposition flags
+ static constexpr size_t writeable = (1<<0);
+
+ // Open a data store at path
+ data_store(size_t flags = 0, afio::path path = "store");
+
+ // Look up item named name for reading, returning a std::istream for the item if it exists
+ outcome<istream> lookup(std::string name) noexcept;
+ // Look up item named name for writing, returning an ostream for that item
+ outcome<ostream> write(std::string name) noexcept;
+};
+//]
+
+//[workshop_naive_afio2]
+namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+using BOOST_OUTCOME_V1_NAMESPACE::empty;
+using BOOST_AFIO_V2_NAMESPACE::error_code;
+using BOOST_AFIO_V2_NAMESPACE::generic_category;
+
+// A special allocator of highly efficient file i/o memory
+using file_buffer_type = std::vector<char, afio::utils::page_allocator<char>>;
+
+// An iostream which reads directly from a memory mapped AFIO file
+struct idirectstream : public std::istream
+{
+ struct directstreambuf : public std::streambuf
+ {
+ afio::handle_ptr h; // Holds the file open and therefore mapped
+ file_buffer_type buffer;
+ afio::handle::mapped_file_ptr mfp;
+ directstreambuf(error_code &ec, afio::handle_ptr _h) : h(std::move(_h))
+ {
+ // Get the size of the file. If greater than 128Kb mmap it
+ size_t length=(size_t) h->lstat(afio::metadata_flags::size).st_size;
+ char *p=nullptr;
+ if(length>=128*1024)
+ {
+ if((mfp=h->map_file()))
+ p = (char *) mfp->addr;
+ }
+ if(!p)
+ {
+ buffer.resize(length);
+ afio::read(ec, h, buffer.data(), length, 0);
+ p=buffer.data();
+ }
+ // Set the get buffer this streambuf is to use
+ setg(p, p, p + length);
+ }
+ };
+ std::unique_ptr<directstreambuf> buf;
+ idirectstream(error_code &ec, afio::handle_ptr h) : std::istream(new directstreambuf(ec, std::move(h))), buf(static_cast<directstreambuf *>(rdbuf()))
+ {
+ }
+ virtual ~idirectstream() override
+ {
+ // Reset the stream before deleting the buffer
+ rdbuf(nullptr);
+ }
+};
+
+// An iostream which writes to an AFIO file in 4Kb pages
+struct odirectstream : public std::ostream
+{
+ struct directstreambuf : public std::streambuf
+ {
+ using int_type = std::streambuf::int_type;
+ using traits_type = std::streambuf::traits_type;
+ afio::future<> lastwrite; // the last async write performed
+ afio::off_t offset; // offset of next write
+ file_buffer_type buffer; // a page size on this machine
+ file_buffer_type lastbuffer;
+ directstreambuf(afio::handle_ptr _h) : lastwrite(std::move(_h)), offset(0), buffer(afio::utils::page_sizes().front())
+ {
+ // Set the put buffer this streambuf is to use
+ setp(buffer.data(), buffer.data() + buffer.size());
+ }
+ virtual ~directstreambuf() override
+ {
+ try
+ {
+ // Flush buffers and wait until last write completes
+ // Schedule an asynchronous write of the buffer to storage
+ size_t thisbuffer = pptr() - pbase();
+ if(thisbuffer)
+ lastwrite = afio::async_write(afio::async_truncate(lastwrite, offset+thisbuffer), buffer.data(), thisbuffer, offset);
+ lastwrite.get();
+ }
+ catch(...)
+ {
+ }
+ }
+ virtual int_type overflow(int_type c) override
+ {
+ size_t thisbuffer=pptr()-pbase();
+ if(thisbuffer>=buffer.size())
+ sync();
+ if(c!=traits_type::eof())
+ {
+ *pptr()=(char)c;
+ pbump(1);
+ return traits_type::to_int_type(c);
+ }
+ return traits_type::eof();
+ }
+ virtual int sync() override
+ {
+ // Wait for the last write to complete, propagating any exceptions
+ lastwrite.get();
+ size_t thisbuffer=pptr()-pbase();
+ if(thisbuffer > 0)
+ {
+ // Detach the current buffer and replace with a fresh one to allow the kernel to steal the page
+ lastbuffer=std::move(buffer);
+ buffer.resize(lastbuffer.size());
+ setp(buffer.data(), buffer.data() + buffer.size());
+ // Schedule an extension of physical storage by an extra page
+ lastwrite = afio::async_truncate(lastwrite, offset + thisbuffer);
+ // Schedule an asynchronous write of the buffer to storage
+ lastwrite=afio::async_write(lastwrite, lastbuffer.data(), thisbuffer, offset);
+ offset+=thisbuffer;
+ }
+ return 0;
+ }
+ };
+ std::unique_ptr<directstreambuf> buf;
+ odirectstream(afio::handle_ptr h) : std::ostream(new directstreambuf(std::move(h))), buf(static_cast<directstreambuf *>(rdbuf()))
+ {
+ }
+ virtual ~odirectstream() override
+ {
+ // Reset the stream before deleting the buffer
+ rdbuf(nullptr);
+ }
+};
+//]
+
+//[workshop_naive_afio1]
+namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+using BOOST_OUTCOME_V1_NAMESPACE::empty;
+using BOOST_AFIO_V2_NAMESPACE::error_code;
+using BOOST_AFIO_V2_NAMESPACE::generic_category;
+
+static bool is_valid_name(const std::string &name) noexcept
+{
+ static const std::string banned("<>:\"/\\|?*\0", 10);
+ if(std::string::npos!=name.find_first_of(banned))
+ return false;
+ // No leading period
+ return name[0]!='.';
+}
+
+data_store::data_store(size_t flags, afio::path path)
+{
+ // Make a dispatcher for the local filesystem URI, masking out write flags on all operations if not writeable
+ _dispatcher=afio::make_dispatcher("file:///", afio::file_flags::none, !(flags & writeable) ? afio::file_flags::write : afio::file_flags::none).get();
+ // Set the dispatcher for this thread, and open a handle to the store directory
+ afio::current_dispatcher_guard h(_dispatcher);
+ _store=afio::dir(std::move(path), afio::file_flags::create); // throws if there was an error
+}
+
+outcome<data_store::istream> data_store::lookup(std::string name) noexcept
+{
+ if(!is_valid_name(name))
+ return error_code(EINVAL, generic_category());
+ try
+ {
+ error_code ec;
+ // Open the file using the handle to the store directory as the base.
+ // The store directory can be freely renamed by any third party process
+ // and everything here will work perfectly.
+ afio::handle_ptr h(afio::file(ec, _store, name, afio::file_flags::read));
+ if(ec)
+ {
+ // If the file was not found, return empty else the error
+ if(ec==error_code(ENOENT, generic_category()))
+ return empty;
+ return ec;
+ }
+ // Create an istream which directly uses the mapped file.
+ data_store::istream ret(std::make_shared<idirectstream>(ec, std::move(h)));
+ if(ec)
+ return ec;
+ return ret;
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+}
+
+outcome<data_store::ostream> data_store::write(std::string name) noexcept
+{
+ if(!is_valid_name(name))
+ return error_code(EINVAL, generic_category());
+ try
+ {
+ error_code ec;
+ // Open the file using the handle to the store directory as the base.
+ // The store directory can be freely renamed by any third party process
+ // and everything here will work perfectly. You could enable direct
+ // buffer writing - this sends 4Kb pages directly to the physical hardware
+ // bypassing the kernel file page cache, however this is not optimal if reads of
+ // the value are likely to occur soon.
+ afio::handle_ptr h(afio::file(ec, _store, name, afio::file_flags::create | afio::file_flags::write
+ /*| afio::file_flags::os_direct*/));
+ if(ec)
+ return ec;
+ // Create an ostream which directly uses the mapped file.
+ return outcome<data_store::ostream>(std::make_shared<odirectstream>(std::move(h)));
+ }
+ catch (...)
+ {
+ return std::current_exception();
+ }
+}
+//]
+
+int main(void)
+{
+ // To write a key-value item called "dog"
+ {
+ data_store ds;
+ auto dogh = ds.write("dog").get();
+ auto &dogs = *dogh;
+ dogs << "I am a dog";
+ }
+
+ // To retrieve a key-value item called "dog"
+ {
+ data_store ds;
+ auto dogh = ds.lookup("dog");
+ if (dogh.empty())
+ std::cerr << "No item called dog" << std::endl;
+ else if(dogh.has_error())
+ std::cerr << "ERROR: Looking up dog returned error " << dogh.get_error().message() << std::endl;
+ else if (dogh.has_exception())
+ {
+ std::cerr << "FATAL: Looking up dog threw exception" << std::endl;
+ std::terminate();
+ }
+ else
+ {
+ std::string buffer;
+ *dogh.get() >> buffer;
+ std::cout << "Item dog has value " << buffer << std::endl;
+ }
+ }
+ return 0;
+}
diff --git a/attic/example/workshop_naive_async_afio.cpp b/attic/example/workshop_naive_async_afio.cpp
new file mode 100644
index 00000000..86cb8052
--- /dev/null
+++ b/attic/example/workshop_naive_async_afio.cpp
@@ -0,0 +1,2 @@
+#include "afio_pch.hpp"
+#include "workshop_naive_async_afio.ipp"
diff --git a/attic/example/workshop_naive_async_afio.ipp b/attic/example/workshop_naive_async_afio.ipp
new file mode 100644
index 00000000..651d2a4e
--- /dev/null
+++ b/attic/example/workshop_naive_async_afio.ipp
@@ -0,0 +1,272 @@
+//[workshop_naive_async_afio_interface
+namespace afio = BOOST_AFIO_V2_NAMESPACE;
+namespace filesystem = BOOST_AFIO_V2_NAMESPACE::filesystem;
+using BOOST_OUTCOME_V1_NAMESPACE::lightweight_futures::shared_future;
+
+class data_store
+{
+ afio::dispatcher_ptr _dispatcher;
+ afio::handle_ptr _store;
+public:
+ // Type used for read streams
+ using istream = std::shared_ptr<std::istream>;
+ // Type used for write streams
+ using ostream = std::shared_ptr<std::ostream>;
+ // Type used for lookup
+ using lookup_result_type = shared_future<istream>;
+ // Type used for write
+ using write_result_type = shared_future<ostream>;
+
+ // Disposition flags
+ static constexpr size_t writeable = (1<<0);
+
+ // Open a data store at path
+ data_store(size_t flags = 0, afio::path path = "store");
+
+ // Look up item named name for reading, returning an istream for the item
+ shared_future<istream> lookup(std::string name) noexcept;
+ // Look up item named name for writing, returning an ostream for that item
+ shared_future<ostream> write(std::string name) noexcept;
+};
+//]
+
+//[workshop_naive_async_afio3]
+namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+using BOOST_AFIO_V2_NAMESPACE::error_code;
+using BOOST_AFIO_V2_NAMESPACE::generic_category;
+
+// A special allocator of highly efficient file i/o memory
+using file_buffer_type = std::vector<char, afio::utils::page_allocator<char>>;
+
+// An iostream which reads directly from a memory mapped AFIO file
+struct idirectstream : public std::istream
+{
+ struct directstreambuf : public std::streambuf
+ {
+ afio::handle_ptr h; // Holds the file open
+ std::shared_ptr<file_buffer_type> buffer;
+ afio::handle::mapped_file_ptr mfp;
+ // From a mmap
+ directstreambuf(afio::handle_ptr _h, afio::handle::mapped_file_ptr _mfp, size_t length) : h(std::move(_h)), mfp(std::move(_mfp))
+ {
+ // Set the get buffer this streambuf is to use
+ setg((char *) mfp->addr, (char *) mfp->addr, (char *) mfp->addr + length);
+ }
+ // From a malloc
+ directstreambuf(afio::handle_ptr _h, std::shared_ptr<file_buffer_type> _buffer, size_t length) : h(std::move(_h)), buffer(std::move(_buffer))
+ {
+ // Set the get buffer this streambuf is to use
+ setg(buffer->data(), buffer->data(), buffer->data() + length);
+ }
+ };
+ std::unique_ptr<directstreambuf> buf;
+ template<class U> idirectstream(afio::handle_ptr h, U &&buffer, size_t length) : std::istream(new directstreambuf(std::move(h), std::forward<U>(buffer), length)), buf(static_cast<directstreambuf *>(rdbuf()))
+ {
+ }
+ virtual ~idirectstream() override
+ {
+ // Reset the stream before deleting the buffer
+ rdbuf(nullptr);
+ }
+};
+
+// An iostream which writes to an AFIO file in 4Kb pages
+struct odirectstream : public std::ostream
+{
+ struct directstreambuf : public std::streambuf
+ {
+ using int_type = std::streambuf::int_type;
+ using traits_type = std::streambuf::traits_type;
+ afio::future<> lastwrite; // the last async write performed
+ afio::off_t offset; // offset of next write
+ file_buffer_type buffer; // a page size on this machine
+ file_buffer_type lastbuffer;
+ directstreambuf(afio::handle_ptr _h) : lastwrite(std::move(_h)), offset(0), buffer(afio::utils::page_sizes().front())
+ {
+ // Set the put buffer this streambuf is to use
+ setp(buffer.data(), buffer.data() + buffer.size());
+ }
+ virtual ~directstreambuf() override
+ {
+ try
+ {
+ // Flush buffers and wait until last write completes
+ // Schedule an asynchronous write of the buffer to storage
+ size_t thisbuffer = pptr() - pbase();
+ if(thisbuffer)
+ lastwrite = afio::async_write(afio::async_truncate(lastwrite, offset+thisbuffer), buffer.data(), thisbuffer, offset);
+ lastwrite.get();
+ }
+ catch(...)
+ {
+ }
+ }
+ virtual int_type overflow(int_type c) override
+ {
+ size_t thisbuffer=pptr()-pbase();
+ if(thisbuffer>=buffer.size())
+ sync();
+ if(c!=traits_type::eof())
+ {
+ *pptr()=(char)c;
+ pbump(1);
+ return traits_type::to_int_type(c);
+ }
+ return traits_type::eof();
+ }
+ virtual int sync() override
+ {
+ // Wait for the last write to complete, propagating any exceptions
+ lastwrite.get();
+ size_t thisbuffer=pptr()-pbase();
+ if(thisbuffer > 0)
+ {
+ // Detach the current buffer and replace with a fresh one to allow the kernel to steal the page
+ lastbuffer=std::move(buffer);
+ buffer.resize(lastbuffer.size());
+ setp(buffer.data(), buffer.data() + buffer.size());
+ // Schedule an extension of physical storage by an extra page
+ lastwrite = afio::async_truncate(lastwrite, offset + thisbuffer);
+ // Schedule an asynchronous write of the buffer to storage
+ lastwrite=afio::async_write(lastwrite, lastbuffer.data(), thisbuffer, offset);
+ offset+=thisbuffer;
+ }
+ return 0;
+ }
+ };
+ std::unique_ptr<directstreambuf> buf;
+ odirectstream(afio::handle_ptr h) : std::ostream(new directstreambuf(std::move(h))), buf(static_cast<directstreambuf *>(rdbuf()))
+ {
+ }
+ virtual ~odirectstream() override
+ {
+ // Reset the stream before deleting the buffer
+ rdbuf(nullptr);
+ }
+};
+//]
+
+namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+using BOOST_AFIO_V2_NAMESPACE::error_code;
+using BOOST_AFIO_V2_NAMESPACE::generic_category;
+
+static bool is_valid_name(const std::string &name) noexcept
+{
+ static const std::string banned("<>:\"/\\|?*\0", 10);
+ if(std::string::npos!=name.find_first_of(banned))
+ return false;
+ // No leading period
+ return name[0]!='.';
+}
+
+data_store::data_store(size_t flags, afio::path path)
+{
+ // Make a dispatcher for the local filesystem URI, masking out write flags on all operations if not writeable
+ _dispatcher=afio::make_dispatcher("file:///", afio::file_flags::none, !(flags & writeable) ? afio::file_flags::write : afio::file_flags::none).get();
+ // Set the dispatcher for this thread, and open a handle to the store directory
+ afio::current_dispatcher_guard h(_dispatcher);
+ _store=afio::dir(std::move(path), afio::file_flags::create); // throws if there was an error
+}
+
+//[workshop_naive_async_afio2]
+shared_future<data_store::istream> data_store::lookup(std::string name) noexcept
+{
+ if(!is_valid_name(name))
+ return error_code(EINVAL, generic_category());
+ try
+ {
+ // Schedule the opening of the file for reading
+ afio::future<> h(afio::async_file(_store, name, afio::file_flags::read));
+ // When it completes, call this continuation
+ return h.then([](afio::future<> &_h) -> shared_future<data_store::istream> {
+ // If file didn't open, return the error or exception immediately
+ BOOST_OUTCOME_PROPAGATE(_h);
+ size_t length=(size_t) _h->lstat(afio::metadata_flags::size).st_size;
+ // Is a memory map more appropriate?
+ if(length>=128*1024)
+ {
+ afio::handle::mapped_file_ptr mfp;
+ if((mfp=_h->map_file()))
+ {
+ data_store::istream ret(std::make_shared<idirectstream>(_h.get_handle(), std::move(mfp), length));
+ return ret;
+ }
+ }
+ // Schedule the reading of the file into a buffer
+ auto buffer=std::make_shared<file_buffer_type>(length);
+ afio::future<> h(afio::async_read(_h, buffer->data(), length, 0));
+ // When the read completes call this continuation
+ return h.then([buffer, length](const afio::future<> &h) -> shared_future<data_store::istream> {
+ // If read failed, return the error or exception immediately
+ BOOST_OUTCOME_PROPAGATE(h);
+ data_store::istream ret(std::make_shared<idirectstream>(h.get_handle(), buffer, length));
+ return ret;
+ });
+ });
+ }
+ catch(...)
+ {
+ // Boost.Monad futures are also monads, so this implies a make_ready_future()
+ return std::current_exception();
+ }
+}
+//]
+
+//[workshop_naive_async_afio1]
+shared_future<data_store::ostream> data_store::write(std::string name) noexcept
+{
+ if(!is_valid_name(name))
+ return error_code(EINVAL, generic_category());
+ try
+ {
+ // Schedule the opening of the file for writing
+ afio::future<> h(afio::async_file(_store, name, afio::file_flags::create | afio::file_flags::write));
+ // When it completes, call this continuation
+ return h.then([](const afio::future<> &h) -> shared_future<data_store::ostream> {
+ // If file didn't open, return the error or exception immediately
+ BOOST_OUTCOME_PROPAGATE(h);
+ // Create an ostream which directly uses the file.
+ data_store::ostream ret(std::make_shared<odirectstream>(h.get_handle()));
+ return std::move(ret);
+ });
+ }
+ catch (...)
+ {
+ // Boost.Monad futures are also monads, so this implies a make_ready_future()
+ return std::current_exception();
+ }
+}
+//]
+
+int main(void)
+{
+ // To write a key-value item called "dog"
+ {
+ data_store ds;
+ auto dogh = ds.write("dog").get();
+ auto &dogs = *dogh;
+ dogs << "I am a dog";
+ }
+
+ // To retrieve a key-value item called "dog"
+ {
+ data_store ds;
+ auto dogh = ds.lookup("dog");
+ if (dogh.empty())
+ std::cerr << "No item called dog" << std::endl;
+ else if(dogh.has_error())
+ std::cerr << "ERROR: Looking up dog returned error " << dogh.get_error().message() << std::endl;
+ else if (dogh.has_exception())
+ {
+ std::cerr << "FATAL: Looking up dog threw exception" << std::endl;
+ std::terminate();
+ }
+ else
+ {
+ std::string buffer;
+ *dogh.get() >> buffer;
+ std::cout << "Item dog has value " << buffer << std::endl;
+ }
+ }
+ return 0;
+}
diff --git a/attic/include/boost/afio/v2/afio.hpp b/attic/include/boost/afio/v2/afio.hpp
new file mode 100644
index 00000000..ab502238
--- /dev/null
+++ b/attic/include/boost/afio/v2/afio.hpp
@@ -0,0 +1,6350 @@
+/* async_file_io
+Provides a threadpool and asynchronous file i/o infrastructure based on Boost.ASIO, Boost.Iostreams and filesystem
+(C) 2013-2015 Niall Douglas http://www.nedprod.com/
+File Created: Mar 2013
+
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef BOOST_AFIO_HEADER_INCLUDED
+
+#ifdef DOXYGEN_SHOULD_SKIP_THIS
+#define BOOST_AFIO_HEADERS_ONLY 0
+#define BOOST_AFIO_USE_BOOST_THREAD 0
+#define BOOST_AFIO_USE_BOOST_FILESYSTEM 1
+#define ASIO_STANDALONE 0
+#endif
+
+#include "config.hpp"
+
+// clang-format off
+#ifdef DOXYGEN_SHOULD_SKIP_THIS
+#undef BOOST_AFIO_V2_NAMESPACE
+#undef BOOST_AFIO_V2_NAMESPACE_BEGIN
+#undef BOOST_AFIO_V2_NAMESPACE_END
+#undef BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC
+#undef BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC
+
+#define BOOST_AFIO_V2_NAMESPACE boost::afio
+#define BOOST_AFIO_V2_NAMESPACE_BEGIN namespace boost { namespace afio {
+#define BOOST_AFIO_V2_NAMESPACE_END } }
+#define BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC
+#define BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC virtual
+#endif
+// clang-format on
+
+#ifndef BOOST_AFIO_AFIO_H
+#define BOOST_AFIO_AFIO_H
+
+#include "detail/Undoer.hpp"
+#include "detail/ErrorHandling.hpp"
+#include "detail/Utility.hpp"
+#include <algorithm> // Boost.ASIO needs std::min and std::max
+#include <exception>
+#include <iostream>
+#include <type_traits>
+
+/*! \brief Validate inputs at the point of instantiation.
+
+Turns on the checking of inputs for validity and throwing of exception conditions
+at the point of instantiation rather than waiting until those inputs are sent
+for dispatch. This, being very useful for debugging, defaults to 1 except when
+`NDEBUG` is defined i.e. final release builds.
+\ingroup macros
+*/
+#ifndef BOOST_AFIO_VALIDATE_INPUTS
+#ifndef NDEBUG
+#define BOOST_AFIO_VALIDATE_INPUTS 1
+#else
+#define BOOST_AFIO_VALIDATE_INPUTS 0
+#endif
+#endif
+
+#ifdef BOOST_MSVC
+#pragma warning(push)
+#pragma warning(disable: 4251) // type needs to have dll-interface to be used by clients of class
+#endif
+
+/*! \file afio.hpp
+\brief Provides a batch asynchronous file i/o implementation based on Boost.ASIO
+*/
+/*! \def BOOST_AFIO_HEADERS_ONLY
+\brief Determines if AFIO is compiled as headers only. Defaults to 1.
+*/
+/*! \def BOOST_AFIO_USE_BOOST_THREAD
+\brief Determines if AFIO is bound against Boost.Thread or the C++ 11 STL thread. Defaults to 0.
+*/
+/*! \def BOOST_AFIO_USE_BOOST_FILESYSTEM
+\brief Determines if AFIO is bound against Boost.Filesystem or the C++ 1z Filesystem TS. Defaults to 1 unless on VS2015 which provides a full Filesystem TS implementation.
+*/
+/*! \def ASIO_STANDALONE
+\brief Determines if AFIO is bound against standalone ASIO or Boost.ASIO. Defaults to undefined, and therefore Boost.ASIO.
+*/
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+// This isn't consistent on MSVC so hard code it
+typedef unsigned long long off_t;
+
+//! \brief The namespace containing Boost.ASIO internal details
+namespace detail
+{
+ template<class R> class enqueued_task_impl
+ {
+ protected:
+ struct Private
+ {
+ std::function<R()> task;
+ promise<R> r;
+ shared_future<R> f;
+ bool autoset;
+ atomic<int> done;
+ Private(std::function<R()> _task) : task(std::move(_task)), f(r.get_future().share()), autoset(true), done(0) { }
+ };
+ std::shared_ptr<Private> p;
+ void validate() const { assert(p); /*if(!p) abort();*/ }
+ public:
+ //! Default constructor
+ enqueued_task_impl(std::function<R()> _task=std::function<R()>()) : p(std::make_shared<Private>(std::move(_task))) { }
+ //! Returns true if valid
+ bool valid() const noexcept{ return p.get()!=nullptr; }
+ //! Swaps contents with another instance
+ void swap(enqueued_task_impl &o) noexcept{ p.swap(o.p); }
+ //! Resets the contents
+ void reset() { p.reset(); }
+ //! Sets the task
+ void set_task(std::function<R()> _task) { p->task=std::move(_task); }
+ //! Returns the shared stl_future corresponding to the stl_future return value of the task
+ const shared_future<R> &get_future() const { validate(); return p->f; }
+ //! Sets the shared stl_future corresponding to the stl_future return value of the task.
+ template<class T> void set_future_value(T v)
+ {
+ int _=0;
+ validate();
+ if(!p->done.compare_exchange_strong(_, 1))
+ return;
+ p->r.set_value(std::move(v));
+ }
+ void set_future_value()
+ {
+ int _=0;
+ validate();
+ if(!p->done.compare_exchange_strong(_, 1))
+ return;
+ p->r.set_value();
+ }
+ //! Sets the shared stl_future corresponding to the stl_future return value of the task.
+ void set_future_exception(exception_ptr e)
+ {
+ int _=0;
+ validate();
+ if(!p->done.compare_exchange_strong(_, 1))
+ return;
+ p->r.set_exception(e);
+ }
+ //! Disables the task setting the shared stl_future return value.
+ void disable_auto_set_future(bool v=true) { validate(); p->autoset=!v; }
+ };
+}
+
+template<class R> class enqueued_task;
+/*! \class enqueued_task<R()>
+\tparam "class R" The return type of the callable which must be without parameters.
+\brief Effectively our own custom std::packaged_task<>, with copy semantics and letting us early set value to significantly improve performance
+
+Unlike `std::packaged_task<>`, this custom variant is copyable though each copy always refers to the same
+internal state. Early stl_future value setting is possible, with any subsequent value setting including that
+by the function being executed being ignored. Note that this behaviour opens the potential to lose exception
+state - if you set the stl_future value early and then an exception is later thrown, the exception is swallowed.
+
+*/
+// Can't have args in callable type as that segfaults VS2010
+template<class R> class enqueued_task<R()> : public detail::enqueued_task_impl<R>
+{
+ typedef detail::enqueued_task_impl<R> Base;
+public:
+ //! Default constructor
+ enqueued_task(std::function<R()> _task=std::function<R()>()) : Base(std::move(_task)) { }
+ //! Invokes the callable, setting the shared stl_future to the value it returns
+ void operator()()
+ {
+ auto _p(Base::p);
+ Base::validate();
+ if(!_p->task) abort();
+ try
+ {
+ auto v(_p->task());
+ if(_p->autoset && !_p->done) Base::set_future_value(v);
+ }
+ catch(...)
+ {
+ if(_p->done)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT(detail::output_exception_info << " thrown up to enqueued_task<> after stl_future set." << std::endl);
+ BOOST_AFIO_THROW_FATAL(std::runtime_error("Exception thrown up to enqueued_task<> after stl_future set."));
+ }
+ if(_p->autoset && !_p->done)
+ {
+ auto e(current_exception());
+ Base::set_future_exception(e);
+ }
+ }
+ // Free any bound parameters in task to save memory
+ _p->task=std::function<R()>();
+ }
+};
+template<> class enqueued_task<void()> : public detail::enqueued_task_impl<void>
+{
+ typedef detail::enqueued_task_impl<void> Base;
+public:
+ //! Default constructor
+ enqueued_task(std::function<void()> _task=std::function<void()>()) : Base(std::move(_task)) { }
+ //! Invokes the callable, setting the stl_future to the value it returns
+ void operator()()
+ {
+ auto _p(Base::p);
+ Base::validate();
+ if(!_p->task) abort();
+ try
+ {
+ _p->task();
+ if(_p->autoset && !_p->done) Base::set_future_value();
+ }
+ catch(...)
+ {
+ if(_p->done)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT(detail::output_exception_info << " thrown up to enqueued_task<> after stl_future set." << std::endl);
+ BOOST_AFIO_THROW_FATAL(std::runtime_error("Exception thrown up to enqueued_task<> after stl_future set."));
+ }
+ if(_p->autoset && !_p->done)
+ {
+ auto e(current_exception());
+ Base::set_future_exception(e);
+ }
+ }
+ // Free any bound parameters in task to save memory
+ _p->task=std::function<void()>();
+ }
+};
+/*! \class thread_source
+\brief Abstract base class for a source of thread workers
+
+Note that in Boost 1.54, and possibly later versions, `asio::io_service` on Windows appears to dislike being
+destructed during static data deinit, hence why this inherits from `std::enable_shared_from_this<>` in order that it
+may be reference count deleted before static data deinit occurs.
+*/
+class thread_source : public std::enable_shared_from_this<thread_source>
+{
+protected:
+ asio::io_service &service;
+ thread_source(asio::io_service &_service) : service(_service) { }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~thread_source() { }
+ thread_source &operator=(const thread_source &) = delete;
+public:
+ //! Returns the underlying io_service
+ asio::io_service &io_service() { return service; }
+ //! Sends a task to the thread pool for execution \tparam "class R" The return type of the enqueued task
+ template<class R> void enqueue(enqueued_task<R> task)
+ {
+ service.post(task);
+ }
+ //! Sends some callable entity to the thread pool for execution \return An enqueued task for the enqueued callable \tparam "class F" Any callable type with signature R(void) \param f Any instance of a callable type
+ template<class F> shared_future<typename std::result_of<F()>::type> enqueue(F f)
+ {
+ typedef typename std::result_of<F()>::type R;
+ enqueued_task<R()> out(std::move(f));
+ auto ret(out.get_future());
+ service.post(out);
+ return ret;
+ }
+};
+
+/*! \class std_thread_pool
+\brief A very simple thread pool based on std::thread or boost::thread
+
+This instantiates a `asio::io_service` and a latchable `asio::io_service::work` to keep any threads working until the instance is destructed.
+*/
+class std_thread_pool : public thread_source {
+ class worker
+ {
+ std_thread_pool *pool;
+ public:
+ explicit worker(std_thread_pool *p) : pool(p) { }
+ void operator()()
+ {
+ detail::set_threadname("boost::afio::std_thread_pool worker");
+ try
+ {
+ pool->service.run();
+ }
+ catch(...)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("WARNING: ASIO exits via " << detail::output_exception_info << " which shouldn't happen." << std::endl);
+ }
+ }
+ };
+ friend class worker;
+
+ asio::io_service service;
+ std::unique_ptr<asio::io_service::work> working;
+ std::vector< std::unique_ptr<thread> > workers;
+public:
+ /*! \brief Constructs a thread pool of \em no workers
+ \param no The number of worker threads to create
+ */
+ explicit std_thread_pool(size_t no) : thread_source(service), working(detail::make_unique<asio::io_service::work>(service))
+ {
+ add_workers(no);
+ }
+ //! Adds more workers to the thread pool \param no The number of worker threads to add
+ void add_workers(size_t no)
+ {
+ workers.reserve(workers.size()+no);
+ for(size_t n=0; n<no; n++)
+ workers.push_back(detail::make_unique<thread>(worker(this)));
+ }
+ //! Destroys the thread pool, waiting for worker threads to exit beforehand.
+ void destroy()
+ {
+ if(!service.stopped())
+ {
+ // Tell the threads there is no more work to do
+ working.reset();
+ for(auto &i: workers) { i->join(); }
+ workers.clear();
+ // For some reason ASIO occasionally thinks there is still more work to do
+ if(!service.stopped())
+ service.run();
+ service.stop();
+ service.reset();
+ }
+ }
+ ~std_thread_pool() final
+ {
+ destroy();
+ }
+};
+/*! \brief Returns the process threadpool
+
+On first use, this instantiates a default std_thread_pool running `BOOST_AFIO_MAX_NON_ASYNC_QUEUE_DEPTH` threads which will remain until its shared count reaches zero.
+\ingroup process_threadpool
+*/
+BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC std::shared_ptr<std_thread_pool> process_threadpool();
+
+
+class dispatcher;
+using dispatcher_ptr = std::shared_ptr<dispatcher>;
+template<class T=void> class future;
+struct path_req;
+template<class T> struct io_req;
+struct enumerate_req;
+struct lock_req;
+namespace detail {
+ struct async_io_handle_posix;
+ struct async_io_handle_windows;
+ struct dispatcher_p;
+ class async_file_io_dispatcher_compat;
+ class async_file_io_dispatcher_windows;
+ class async_file_io_dispatcher_linux;
+ class async_file_io_dispatcher_qnx;
+ struct immediate_async_ops;
+ template<bool for_writing> class io_req_impl;
+}
+
+//! \brief The types of path normalisation available
+enum class path_normalise
+{
+ dos, //!< Return the shortest normalised path possible (usually a drive letter prefix). This is a traditional DOS style path.
+ guid_volume, //!< Return the volume as a GUID. This eliminates problems with drive letters vanishing or being ambiguous. Anything accepting a Win32 path can accept one of these.
+ guid_all //!< Return the whole path as a GUID. This eliminates problems with long paths or if the file is renamed. Note this may cause the creation of a GUID for the file. Anything accepting a Win32 path can accept one of these.
+};
+
+/*! \class path
+\brief An AFIO filesystem path, a thin wrapper of filesystem::path used to mark when a
+filesystem path has been prepared for AFIO usage. Note that on Windows this exclusively
+refers to a case sensitive NT kernel path, not a Win32 path (Win32 paths are converted in the constructor).
+
+\qbk{
+[include generated/struct_path_1_1make_absolute.qbk]
+[include generated/group_normalise_path.qbk]
+[include generated/struct_path_hash.qbk]
+}
+*/
+class path : protected filesystem::path
+{
+ void int_regularise()
+ {
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 6326) // comparison of constants
+#endif
+ if(preferred_separator!='/')
+ make_preferred();
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+#ifdef WIN32
+ // Need to strip off any win32 prefixing, and instead prefix any drive letters
+ bool isExtendedPath=false, isDevicePath=false;
+ if(native().size()>=4)
+ {
+#ifndef NDEBUG
+ if(native()[0]=='\\' && native()[1]=='?' && native()[2]=='?' && native()[3]=='\\')
+ {
+ assert(!(native()[0]=='\\' && native()[1]=='?' && native()[2]=='?' && native()[3]=='\\'));
+ }
+#endif
+ isExtendedPath=(native()[0]=='\\' && native()[1]=='\\' && native()[2]=='?' && native()[3]=='\\');
+ isDevicePath=(native()[0]=='\\' && native()[1]=='\\' && native()[2]=='.' && native()[3]=='\\');
+ }
+ bool hasDriveLetter=(isalpha(native()[((int) isExtendedPath+(int) isDevicePath)*4+0]) && native()[((int) isExtendedPath+(int) isDevicePath)*4+1]==':');
+ if(hasDriveLetter && (isExtendedPath || isDevicePath))
+ {
+ filesystem::path::string_type &me=const_cast<filesystem::path::string_type &>(native());
+ me[1]=me[2]='?';
+ }
+ else if(hasDriveLetter)
+ {
+ filesystem::path::string_type &me=const_cast<filesystem::path::string_type &>(native());
+ me=L"\\??\\"+me;
+ }
+ else if(isExtendedPath || isDevicePath)
+ {
+ filesystem::path::string_type &me=const_cast<filesystem::path::string_type &>(native());
+ me=me.substr(isDevicePath ? 3 : 4);
+ }
+#endif
+ }
+ friend struct detail::async_io_handle_windows;
+ struct direct { };
+ path(filesystem::path &&p, direct) : filesystem::path(std::move(p)) { }
+public:
+ typedef filesystem::path::value_type value_type;
+ typedef filesystem::path::string_type string_type;
+ using filesystem::path::preferred_separator;
+ //! Makes a path absolute
+ struct make_absolute;
+
+ //! \constr
+ path() {}
+ //! \cconstr
+ path(const path &p) : filesystem::path(p) { }
+ //! Converts a filesystem::path to AFIO format
+ path(const filesystem::path &p) : filesystem::path(p) { int_regularise(); }
+ //! Converts a filesystem::path to AFIO format
+ path(const char *p) : filesystem::path(p) { int_regularise(); }
+#ifdef WIN32
+ //! Converts a filesystem::path to AFIO format
+ path(const wchar_t *p) : filesystem::path(p) { int_regularise(); }
+ //! Converts a filesystem::path to AFIO format
+ path(const std::string &p) : filesystem::path(p) { int_regularise(); }
+#endif
+ //! Converts a filesystem::path to AFIO format
+ path(const string_type &p) : filesystem::path(p) { int_regularise(); }
+ //! \mconstr
+ path(path &&p) noexcept : filesystem::path(std::move(p)) { }
+ //! Converts a filesystem::path to AFIO format
+ path(filesystem::path &&p) : filesystem::path(std::move(p)) { int_regularise(); }
+#ifdef WIN32
+ //! Converts a filesystem::path to AFIO format
+ path(std::string &&p) : filesystem::path(std::move(p)) { int_regularise(); }
+#endif
+ //! Converts a filesystem::path to AFIO format
+ path(string_type &&p) : filesystem::path(std::move(p)) { int_regularise(); }
+ //! Converts source to AFIO path format
+ //template<class Source> path(const Source &source) : filesystem::path(source) { int_regularise(); }
+ //! Converts source to AFIO path format
+ template <class InputIterator> path(InputIterator begin, InputIterator end) : filesystem::path(begin, end) { int_regularise(); }
+ //! \cassign
+ path& operator=(const path& p) { filesystem::path::operator=(filesystem::path(p)); return *this; }
+ //! \massign
+ path& operator=(path&& p) noexcept { filesystem::path::operator=(static_cast<filesystem::path &&>(p)); return *this; }
+ //! Converts source to AFIO path format
+ //template <class Source> path& operator=(Source const& source) { filesystem::path::operator=(source); int_regularise(); return *this; }
+
+ template <class Source>
+ path& assign(Source const& source) { filesystem::path::assign(source); return *this; }
+ template <class InputIterator>
+ path& assign(InputIterator begin, InputIterator end) { filesystem::path::assign(begin, end); return *this; }
+ path& operator/=(const path& p) { filesystem::path::operator/=(filesystem::path(p)); return *this; }
+ template <class Source>
+ path& operator/=(Source const& source) { filesystem::path::operator/=(source); return *this; }
+ template <class Source>
+ path& append(Source const& source) { filesystem::path::append(source); return *this; }
+ template <class InputIterator>
+ path& append(InputIterator begin, InputIterator end) { filesystem::path::append(begin, end); return *this; }
+
+ path& operator+=(const path& x) { filesystem::path::operator+=(filesystem::path(x)); return *this; }
+ path& operator+=(const string_type& x) { filesystem::path::operator+=(x); return *this; }
+ path& operator+=(const value_type* x) { filesystem::path::operator+=(x); return *this; }
+ path& operator+=(value_type x) { filesystem::path::operator+=(x); return *this; }
+ template <class Source>
+ path& operator+=(Source const& x) { filesystem::path::operator+=(x); return *this; }
+ template <class Source>
+ path& concat(Source const& x) { filesystem::path::concat(x); return *this; }
+ template <class InputIterator>
+ path& concat(InputIterator begin, InputIterator end) { filesystem::path::concat(begin, end); return *this; }
+
+ using filesystem::path::clear;
+ path& make_preferred() { filesystem::path::make_preferred(); return *this; }
+ path& remove_filename() { filesystem::path::remove_filename(); return *this; }
+ path& replace_extension(const path& new_extension = path()) { filesystem::path::replace_extension(filesystem::path(new_extension)); return *this; }
+ using filesystem::path::swap;
+
+ using filesystem::path::native;
+ using filesystem::path::c_str;
+ using filesystem::path::string;
+ using filesystem::path::wstring;
+ using filesystem::path::generic_string;
+ using filesystem::path::compare;
+
+ path root_name() const { return path(filesystem::path::root_name(), direct()); }
+ path root_directory() const { return path(filesystem::path::root_directory(), direct()); }
+ path root_path() const { return path(filesystem::path::root_path(), direct()); }
+ path relative_path() const { return path(filesystem::path::relative_path(), direct()); }
+ path parent_path() const { return path(filesystem::path::parent_path(), direct()); }
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ path filename() const { return path(filesystem::path::leaf(), direct()); }
+#else
+ path filename() const { return path(filesystem::path::filename(), direct()); }
+#endif
+ path stem() const { return path(filesystem::path::stem(), direct()); }
+ path extension() const { return path(filesystem::path::extension(), direct()); }
+
+ using filesystem::path::empty;
+ using filesystem::path::has_root_name;
+ using filesystem::path::has_root_directory;
+ using filesystem::path::has_root_path;
+ using filesystem::path::has_relative_path;
+ using filesystem::path::has_parent_path;
+ using filesystem::path::has_filename;
+ using filesystem::path::has_stem;
+ using filesystem::path::has_extension;
+ using filesystem::path::is_absolute;
+ using filesystem::path::is_relative;
+
+ // TODO FIXME: Need our own iterator here
+ typedef filesystem::path::iterator iterator;
+ typedef filesystem::path::const_iterator const_iterator;
+
+ iterator begin() const { return filesystem::path::begin(); }
+ iterator end() const { return filesystem::path::end(); }
+
+ /*! \brief Return a normalised filesystem::path from an AFIO path.
+
+ On POSIX this passes through its input unchanged.
+
+ On Windows AFIO exclusively uses NT kernel paths which are not necessarily trivially convertible
+ to Win32 paths. As an example, the Win32 path `C:\Foo` might be `\??\C:\Foo` or even
+ `\Device\HarddiskVolume1\Foo`. This function will convert any NT kernel path into
+ something which can be fed to normal Win32 APIs quickly, though note that the
+ output path will be rejected by most other APIs as invalid. If you need a Win32
+ path which is completely valid, use normalise_path().
+ */
+ filesystem::path filesystem_path() const
+ {
+#ifdef WIN32
+ bool isSymlinkedDosPath=(native()[0]=='\\' && native()[1]=='?' && native()[2]=='?' && native()[3]=='\\');
+ if(isSymlinkedDosPath)
+ {
+ filesystem::path::string_type p(native());
+ p[1]='\\';
+ return p;
+ }
+ else
+ return filesystem::path(L"\\\\.")/filesystem::path(*this);
+#else
+ return *this;
+#endif
+ }
+ friend inline bool operator<(const path& lhs, const path& rhs);
+ friend inline bool operator<=(const path& lhs, const path& rhs);
+ friend inline bool operator>(const path& lhs, const path& rhs);
+ friend inline bool operator>=(const path& lhs, const path& rhs);
+ friend inline bool operator==(const path& lhs, const path& rhs);
+ friend inline bool operator!=(const path& lhs, const path& rhs);
+ friend inline path operator/(const path& lhs, const path& rhs);
+ friend inline std::ostream &operator<<(std::ostream &s, const path &p);
+ friend struct path_hash;
+#ifdef WIN32
+#ifdef _MSC_VER
+ friend BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC filesystem::path normalise_path(path p, path_normalise type);
+#else
+ friend filesystem::path normalise_path(path p, path_normalise type);
+#endif
+#else
+ friend inline filesystem::path normalise_path(path p, path_normalise type);
+#endif
+};
+inline bool operator<(const path& lhs, const path& rhs) { return filesystem::path(lhs)<filesystem::path(rhs); }
+inline bool operator<=(const path& lhs, const path& rhs) { return filesystem::path(lhs)<=filesystem::path(rhs); }
+inline bool operator>(const path& lhs, const path& rhs) { return filesystem::path(lhs)>filesystem::path(rhs); }
+inline bool operator>=(const path& lhs, const path& rhs) { return filesystem::path(lhs)>=filesystem::path(rhs); }
+inline bool operator==(const path& lhs, const path& rhs) { return filesystem::path(lhs)==filesystem::path(rhs); }
+inline bool operator!=(const path& lhs, const path& rhs) { return filesystem::path(lhs)!=filesystem::path(rhs); }
+inline path operator/(const path& lhs, const path& rhs) { return path(filesystem::path(lhs)/filesystem::path(rhs), path::direct()); }
+inline std::ostream &operator<<(std::ostream &s, const path &p) { return s << filesystem::path(p); }
+//! Makes a path absolute according to the current working directory
+struct path::make_absolute : public path
+{
+ make_absolute(const path &p) : path(p)
+ {
+ if(native()[0]!=preferred_separator)
+ *this=filesystem::absolute(std::move(*this));
+ }
+ make_absolute(path &&p) : path(std::move(p))
+ {
+ if(native()[0]!=preferred_separator)
+ *this=filesystem::absolute(std::move(*this));
+ }
+ template<class T, typename=typename std::enable_if<std::is_constructible<filesystem::path, T>::value>::type> make_absolute(T &&p) : path(filesystem::absolute(std::forward<T>(p))) { }
+};
+/*! \brief A hasher for path
+*/
+struct path_hash
+{
+ std::hash<path::string_type> hasher;
+public:
+ size_t operator()(const path &p) const
+ {
+ return hasher(p.native());
+ }
+};
+
+/*! \brief Return a normalised filesystem::path from an AFIO path.
+
+On POSIX this passes through its input unchanged.
+
+On Windows AFIO exclusively uses NT kernel paths which are not necessarily trivially convertible
+to Win32 paths. As an example, the Win32 path `C:\\Foo` might be `\\??\\C:\\Foo` or even
+`\\Device\\HarddiskVolume1\\Foo`. This function will convert any NT kernel path into
+something which can be fed to normal Win32 APIs - a drive letter if available, else a GUID volume
+path, and with an extended path prefix if the path is sufficiently long. It also scans the path
+for characters illegal under Win32 or paths which begin with a space or end with a period, and
+will extended path prefix such paths as well.
+
+\ingroup normalise_path
+\param p Path to be normalised
+\param type A path_normalise enum
+*/
+#ifdef WIN32
+BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC filesystem::path normalise_path(path p, path_normalise type=path_normalise::dos);
+#else
+inline filesystem::path normalise_path(path p, path_normalise type=path_normalise::dos) { return p; }
+#endif
+
+
+#define BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(type) \
+inline constexpr type operator&(type a, type b) \
+{ \
+ return static_cast<type>(static_cast<size_t>(a) & static_cast<size_t>(b)); \
+} \
+inline constexpr type operator|(type a, type b) \
+{ \
+ return static_cast<type>(static_cast<size_t>(a) | static_cast<size_t>(b)); \
+} \
+inline constexpr type operator~(type a) \
+{ \
+ return static_cast<type>(~static_cast<size_t>(a)); \
+} \
+inline constexpr bool operator!(type a) \
+{ \
+ return 0==static_cast<size_t>(a); \
+}
+
+
+
+/*! \enum file_flags
+\brief Bitwise file and directory open flags
+\ingroup file_flags
+*/
+enum class file_flags : size_t
+{
+ none=0, //!< No flags set
+ read=1, //!< Read access
+ write=2, //!< Write access
+ read_write=3, //!< Read and write access
+ append=4, //!< Append only
+ truncate=8, //!< Truncate existing file to zero
+ create=16, //!< Open and create if doesn't exist. Always creates sparse files if possible.
+ create_only_if_not_exist=32, //!< Create and open only if doesn't exist
+ create_compressed=64, //!< Create a compressed file, needs to be combined with one of the other create flags. Only succeeds if supported by the underlying filing system.
+
+ will_be_sequentially_accessed=128, //!< Will be \em exclusively either read or written sequentially. If you're exclusively writing sequentially, \em strongly consider turning on `os_direct` too.
+ will_be_randomly_accessed=256, //!< Will be randomly accessed, so don't bother with read-ahead. If you're using this, \em strongly consider turning on `os_direct` too.
+ no_sparse=512, //!< Don't create sparse files. May be ignored by some filing systems (e.g. ext4).
+
+ hold_parent_open=(1<<10), //!< Hold a file handle open to the containing directory of each open file for fast directory enumeration and fast relative path ops.
+ unique_directory_handle=(1<<11), //!< Return a unique directory handle rather than a shared directory handle
+ no_race_protection=(1<<12), //!< Skip taking steps to avoid destruction of data due to filing system races. Most of the performance benefit of enabling this goes away if you enable HoldParentOpen instead, so be especially careful when considering turning this on.
+ temporary_file=(1<<13), //!< On some systems causes dirty cache data to not be written to physical storage until file close. Useful for temporary files and lock files, especially on Windows when combined with `delete_on_close` as this avoids an fsync of the containing directory on file close.
+ delete_on_close=(1<<14), //!< Only when combined with `create_only_if_not_exist`, deletes the file on close. This is especially useful on Windows with temporary and lock files where normally closing a file is an implicit fsync of its containing directory. Note on POSIX this unlinks the file on first close by AFIO, whereas on Windows the operating system unlinks the file on last close including sudden application exit. Note also that AFIO permits you to delete files which are currently open on Windows and the file entry disappears immediately just as on POSIX.
+
+ os_direct=(1<<16), //!< Bypass the OS file buffers (only really useful for writing large files, or a lot of random reads and writes. Note you must 4Kb align everything if this is on). Be VERY careful mixing this with memory mapped files.
+ os_lockable=(1<<17), // Deliberately undocumented
+
+ always_sync=(1<<24), //!< Ask the OS to not complete until the data is on the physical storage. Some filing systems do much better with this than `sync_on_close`.
+ sync_on_close=(1<<25), //!< Automatically initiate an asynchronous flush just before file close, and fuse both operations so both must complete for close to complete.
+
+ int_hold_parent_open_nested=(1<<27), //!< Internal use only. Don't use.
+ int_file_share_delete=(1<<28), //!< Internal use only. Don't use.
+ int_opening_link=(1<<29), //!< Internal use only. Don't use.
+ int_opening_dir=(1<<30) //!< Internal use only. Don't use.
+};
+BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(file_flags)
+
+/*! \enum async_op_flags
+\brief Bitwise async_op_flags flags
+\ingroup async_op_flags
+*/
+enum class async_op_flags : size_t
+{
+ none=0, //!< No flags set
+ immediate=1 //!< Call chained completion immediately instead of scheduling for later. Make SURE your completion can not block!
+};
+BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(async_op_flags)
+
+namespace detail {
+ /*! \enum OpType
+ \brief The type of operation
+ */
+ enum class OpType
+ {
+ Unknown,
+ UserCompletion,
+ dir,
+ rmdir,
+ file,
+ rmfile,
+ symlink,
+ rmsymlink,
+ sync,
+ close,
+ read,
+ write,
+ truncate,
+ barrier,
+ enumerate,
+ adopt,
+ zero,
+ extents,
+ statfs,
+ lock,
+
+ Last
+ };
+ static const char *optypes[]={
+ "unknown",
+ "UserCompletion",
+ "dir",
+ "rmdir",
+ "file",
+ "rmfile",
+ "symlink",
+ "rmsymlink",
+ "sync",
+ "close",
+ "read",
+ "write",
+ "truncate",
+ "barrier",
+ "enumerate",
+ "adopt",
+ "zero",
+ "extents",
+ "statfs",
+ "lock"
+ };
+ static_assert(static_cast<size_t>(OpType::Last)==sizeof(optypes)/sizeof(*optypes), "You forgot to fix up the strings matching OpType");
+
+ enum class unit_testing_flags : size_t
+ {
+ none=0, //!< No flags set
+ no_symbol_lookup=(1<<0) //!< Don't bother looking up symbols in stack backtracing as it's horribly slow on POSIX especially
+ };
+ BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(unit_testing_flags)
+}
+
+class handle;
+//! A type alias to a shared pointer to handle
+using handle_ptr = std::shared_ptr<handle>;
+
+/*! \enum metadata_flags
+\brief Bitflags for availability of metadata from `struct stat_t`
+\ingroup metadata_flags
+
+See __afio_stat_t__ for explanation of meaning.
+*/
+enum class metadata_flags : size_t
+{
+ None=0,
+ dev=1<<0,
+ ino=1<<1,
+ type=1<<2,
+ perms=1<<3,
+ nlink=1<<4,
+ uid=1<<5,
+ gid=1<<6,
+ rdev=1<<7,
+ atim=1<<8,
+ mtim=1<<9,
+ ctim=1<<10,
+ size=1<<11,
+ allocated=1<<12,
+ blocks=1<<13,
+ blksize=1<<14,
+ flags=1<<15,
+ gen=1<<16,
+ birthtim=1<<17,
+ sparse=1<<24,
+ compressed=1<<25,
+ reparse_point=1<<26,
+ All=(size_t)-1 //!< Return the maximum possible metadata.
+};
+BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(metadata_flags)
+/*! \struct stat_t
+\brief Metadata about a directory entry
+
+This structure looks somewhat like a `struct stat`, and indeed it was derived from BSD's `struct stat`.
+However there are a number of changes to better interoperate with modern practice, specifically:
+
+- inode value containers are forced to 64 bits.
+- Timestamps use C++11's `std::chrono::system_clock::time_point` or Boost equivalent. The resolution
+of these may or may not equal what a `struct timespec` can do depending on your STL.
+- The type of a file, which is available on Windows and on POSIX without needing an additional
+syscall, is provided by `st_type` which is one of the values from `filesystem::file_type`.
+- As type is now separate from permissions, there is no longer a `st_mode`, instead being a
+`st_perms` which is solely the permissions bits. If you want to test permission bits in `st_perms`
+but don't want to include platform specific headers, note that `filesystem::perms` contains
+definitions of the POSIX permissions flags.
+- The st_sparse and st_compressed flags indicate if your file is sparse and/or compressed, or if
+the directory will compress newly created files by default. Note that on POSIX, a file is sparse
+if and only if st_allocated < st_size which can include compressed files if that filing system is mounted
+with compression enabled (e.g. ZFS with ZLE compression which elides runs of zeros).
+- The st_reparse_point is a Windows only flag and is never set on POSIX, even on a NTFS volume.
+*/
+struct stat_t
+{
+#ifndef WIN32
+ uint64_t st_dev; /*!< inode of device containing file (POSIX only) */
+#endif
+ uint64_t st_ino; /*!< inode of file (Windows, POSIX) */
+ filesystem::file_type st_type; /*!< type of file (Windows, POSIX) */
+#ifndef WIN32
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ uint16_t st_perms;
+#else
+ filesystem::perms st_perms; /*!< uint16_t bitfield perms of file (POSIX only) */
+#endif
+#endif
+ int16_t st_nlink; /*!< number of hard links (Windows, POSIX) */
+#ifndef WIN32
+ int16_t st_uid; /*!< user ID of the file (POSIX only) */
+ int16_t st_gid; /*!< group ID of the file (POSIX only) */
+ dev_t st_rdev; /*!< id of file if special (POSIX only) */
+#endif
+ chrono::system_clock::time_point st_atim; /*!< time of last access (Windows, POSIX) */
+ chrono::system_clock::time_point st_mtim; /*!< time of last data modification (Windows, POSIX) */
+ chrono::system_clock::time_point st_ctim; /*!< time of last status change (Windows, POSIX) */
+ off_t st_size; /*!< file size, in bytes (Windows, POSIX) */
+ off_t st_allocated; /*!< bytes allocated for file (Windows, POSIX) */
+ off_t st_blocks; /*!< number of blocks allocated (Windows, POSIX) */
+ uint16_t st_blksize; /*!< block size used by this device (Windows, POSIX) */
+ uint32_t st_flags; /*!< user defined flags for file (FreeBSD, OS X, zero otherwise) */
+ uint32_t st_gen; /*!< file generation number (FreeBSD, OS X, zero otherwise)*/
+ chrono::system_clock::time_point st_birthtim; /*!< time of file creation (Windows, FreeBSD, OS X, zero otherwise) */
+
+ unsigned st_sparse : 1; /*!< if this file is sparse, or this directory capable of sparse files (Windows, POSIX) */
+ unsigned st_compressed : 1; /*!< if this file is compressed, or this directory capable of compressed files (Windows) */
+ unsigned st_reparse_point : 1; /*!< if this file or directory is a reparse point (Windows) */
+
+ //! Constructs a UNINITIALIZED instance i.e. full of random garbage
+ stat_t() { }
+ //! Constructs a zeroed instance
+ stat_t(std::nullptr_t) :
+#ifndef WIN32
+ st_dev(0),
+#endif
+ st_ino(0),
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ st_type(filesystem::file_type::type_unknown),
+#else
+ st_type(filesystem::file_type::unknown),
+#endif
+#ifndef WIN32
+ st_perms(0),
+#endif
+ st_nlink(0),
+#ifndef WIN32
+ st_uid(0), st_gid(0), st_rdev(0),
+#endif
+ st_size(0), st_allocated(0), st_blocks(0), st_blksize(0), st_flags(0), st_gen(0), st_sparse(0), st_compressed(0), st_reparse_point(0) { }
+};
+
+/*! \enum fs_metadata_flags
+\brief Bitflags for availability of metadata from `struct statfs_t`
+\ingroup fs_metadata_flags
+*/
+enum class fs_metadata_flags : size_t
+{
+ None=0,
+ flags=1<<1,
+ bsize=1<<2,
+ iosize=1<<3,
+ blocks=1<<4,
+ bfree=1<<5,
+ bavail=1<<6,
+ files=1<<7,
+ ffree=1<<8,
+ namemax=1<<9,
+ owner=1<<10,
+ fsid=1<<11,
+ fstypename=1<<12,
+ mntfromname=1<<13,
+ mntonname=1<<14,
+ All=(size_t)-1 //!< Return the maximum possible metadata.
+};
+BOOST_AFIO_DECLARE_CLASS_ENUM_AS_BITFIELD(fs_metadata_flags)
+/*! \struct statfs_t
+\brief Metadata about a filing system. Unsupported entries are -1.
+
+\qbk{
+[include generated/struct_statfs_t_1_1f_flags_t.qbk]
+}
+*/
+struct statfs_t
+{
+ struct f_flags_t
+ {
+ uint32_t rdonly : 1; //!< Filing system is read only (Windows, POSIX)
+ uint32_t noexec : 1; //!< Filing system cannot execute programs (POSIX)
+ uint32_t nosuid : 1; //!< Filing system cannot superuser (POSIX)
+ uint32_t acls : 1; //!< Filing system provides ACLs (Windows, POSIX)
+ uint32_t xattr : 1; //!< Filing system provides extended attributes (Windows, POSIX)
+ uint32_t compression : 1; //!< Filing system provides whole volume compression (Windows, POSIX)
+ uint32_t extents : 1; //!< Filing system provides extent based file storage (sparse files) (Windows, POSIX)
+ uint32_t filecompression : 1; //!< Filing system provides per-file selectable compression (Windows)
+ } f_flags; /*!< copy of mount exported flags (Windows, POSIX) */
+ uint64_t f_bsize; /*!< fundamental filesystem block size (Windows, POSIX) */
+ uint64_t f_iosize; /*!< optimal transfer block size (Windows, POSIX) */
+ uint64_t f_blocks; /*!< total data blocks in filesystem (Windows, POSIX) */
+ uint64_t f_bfree; /*!< free blocks in filesystem (Windows, POSIX) */
+ uint64_t f_bavail; /*!< free blocks avail to non-superuser (Windows, POSIX) */
+ uint64_t f_files; /*!< total file nodes in filesystem (POSIX) */
+ uint64_t f_ffree; /*!< free nodes avail to non-superuser (POSIX) */
+ uint32_t f_namemax; /*!< maximum filename length (Windows, POSIX) */
+#ifndef WIN32
+ int16_t f_owner; /*!< user that mounted the filesystem (BSD, OS X) */
+#endif
+ uint64_t f_fsid[2]; /*!< filesystem id (Windows, POSIX) */
+ std::string f_fstypename; /*!< filesystem type name (Windows, POSIX) */
+ std::string f_mntfromname; /*!< mounted filesystem (Windows, POSIX) */
+ path f_mntonname; /*!< directory on which mounted (Windows, POSIX) */
+ statfs_t()
+ {
+ size_t frontbytes=((char *) &f_fstypename)-((char *) this);
+ memset(this, 0xff, frontbytes);
+ memset(this, 0, sizeof(f_flags));
+ }
+};
+
+/*! \brief The abstract base class for an entry in a directory with lazily filled metadata.
+
+Note that `directory_entry_hash` will hash one of these for you, and a `std::hash<directory_entry>` specialisation
+is defined for you so you ought to be able to use directory_entry directly in an `unordered_map<>`.
+
+See `__afio_stat_t__` for explanations of the fields.
+
+\qbk{
+[include generated/struct_directory_entry_hash.qbk]
+}
+*/
+class BOOST_AFIO_DECL directory_entry
+{
+ friend class detail::async_file_io_dispatcher_compat;
+ friend class detail::async_file_io_dispatcher_windows;
+ friend class detail::async_file_io_dispatcher_linux;
+ friend class detail::async_file_io_dispatcher_qnx;
+
+ path::string_type leafname;
+ stat_t stat;
+ metadata_flags have_metadata;
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void _int_fetch(metadata_flags wanted, handle_ptr dirh);
+public:
+ //! \constr
+ directory_entry() : stat(nullptr), have_metadata(metadata_flags::None) { }
+ //! \constr
+ directory_entry(path::string_type _leafname, stat_t __stat, metadata_flags _have_metadata) : leafname(_leafname), stat(__stat), have_metadata(_have_metadata) { }
+ directory_entry(const directory_entry &) = default;
+ directory_entry &operator=(const directory_entry &) = default;
+ directory_entry(directory_entry &&o) noexcept : leafname(std::move(o.leafname)), stat(std::move(o.stat)), have_metadata(std::move(o.have_metadata)) { }
+ directory_entry &operator=(directory_entry &&o) noexcept
+ {
+ leafname=std::move(o.leafname);
+ stat=std::move(o.stat);
+ have_metadata=std::move(o.have_metadata);
+ return *this;
+ }
+
+ bool operator==(const directory_entry& rhs) const noexcept { return leafname == rhs.leafname; }
+ bool operator!=(const directory_entry& rhs) const noexcept { return leafname != rhs.leafname; }
+ bool operator< (const directory_entry& rhs) const noexcept { return leafname < rhs.leafname; }
+ bool operator<=(const directory_entry& rhs) const noexcept { return leafname <= rhs.leafname; }
+ bool operator> (const directory_entry& rhs) const noexcept { return leafname > rhs.leafname; }
+ bool operator>=(const directory_entry& rhs) const noexcept { return leafname >= rhs.leafname; }
+ //! \return The name of the directory entry. May be empty if the file is deleted.
+ path::string_type name() const noexcept { return leafname; }
+ //! \return A bitfield of what metadata is ready right now
+ metadata_flags metadata_ready() const noexcept { return have_metadata; }
+ /*! \brief Fetches the specified metadata, returning that newly available. This is a blocking call if wanted metadata is not yet ready.
+ Note that if the call blocks and the leafname no longer exists or the directory handle is null, an exception is thrown.
+ \return The metadata now available in this directory entry.
+ \param dirh An open handle to the entry's containing directory. You can get this from an op ref using dirop.get_handle().
+ \param wanted A bitfield of the metadata to fetch. This does not replace existing metadata.
+ */
+ metadata_flags fetch_metadata(handle_ptr dirh, metadata_flags wanted)
+ {
+ metadata_flags tofetch;
+ wanted=wanted&metadata_supported();
+ tofetch=wanted&~have_metadata;
+ if(!!tofetch) _int_fetch(tofetch, dirh);
+ return have_metadata;
+ }
+ /*! \brief Returns a copy of the internal `stat_t` structure. This is a blocking call if wanted metadata is not yet ready.
+ Note that if the call blocks and the leafname no longer exists or the directory handle is null, an exception is thrown.
+ \return A copy of the internal `stat_t` structure.
+ \param dirh An open handle to the entry's containing directory. You can get this from an op ref using dirop.get_handle().
+ \param wanted A bitfield of the metadata to fetch. This does not replace existing metadata.
+ */
+ stat_t fetch_lstat(handle_ptr dirh, metadata_flags wanted=directory_entry::metadata_fastpath())
+ {
+ fetch_metadata(dirh, wanted);
+ return stat;
+ }
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+#define BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(field) \
+decltype(stat_t().st_##field) st_##field() const { if(!(have_metadata&metadata_flags::field)) { BOOST_AFIO_THROW(std::runtime_error("Field st_" #field " not present.")); } return stat.st_##field; } \
+decltype(stat_t().st_##field) st_##field(handle_ptr dirh) { if(!(have_metadata&metadata_flags::field)) { _int_fetch(metadata_flags::field, dirh); } return stat.st_##field; }
+#else
+#define BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(field) \
+fieldtype st_##field(handle_ptr dirh=handle_ptr()) { if(!(have_metadata&metadata_flags::field)) { _int_fetch(metadata_flags::field, dirh); } return stat.st_##field; }
+#endif
+#ifndef WIN32
+ //! Returns st_dev \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(dev)
+#endif
+ //! Returns st_ino \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(ino)
+ //! Returns st_type \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(type)
+#ifndef WIN32
+ //! Returns st_perms \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(perms)
+#endif
+ //! Returns st_nlink \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(nlink)
+#ifndef WIN32
+ //! Returns st_uid \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(uid)
+ //! Returns st_gid \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(gid)
+ //! Returns st_rdev \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(rdev)
+#endif
+ //! Returns st_atim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(atim)
+ //! Returns st_mtim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(mtim)
+ //! Returns st_ctim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(ctim)
+ //! Returns st_size \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(size)
+ //! Returns st_allocated \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(allocated)
+ //! Returns st_blocks \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(blocks)
+ //! Returns st_blksize \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(blksize)
+ //! Returns st_flags \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(flags)
+ //! Returns st_gen \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(gen)
+ //! Returns st_birthtim \param dirh An optional open handle to the entry's containing directory if fetching missing metadata is desired (an exception is thrown otherwise). You can get this from an op ref using dirop.get_handle().
+ BOOST_AFIO_DIRECTORY_ENTRY_ACCESS_METHOD(birthtim)
+
+ //! A bitfield of what metadata is available on this platform. This doesn't mean all is available for every filing system.
+ static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC metadata_flags metadata_supported() noexcept;
+ //! A bitfield of what metadata is fast on this platform. This doesn't mean all is available for every filing system.
+ static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC metadata_flags metadata_fastpath() noexcept;
+ //! The maximum number of entries which is "usual" to fetch at once i.e. what your libc does.
+ static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t compatibility_maximum() noexcept;
+};
+
+/*! \brief A hasher for directory_entry, hashing inode and birth time (if available on this platform).
+*/
+struct directory_entry_hash
+{
+public:
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4310) // cast truncates constant value
+#endif
+ size_t operator()(const directory_entry &p) const
+ {
+ size_t seed = (size_t) 0x9ddfea08eb382d69ULL;
+ detail::hash_combine(seed, p.st_ino());
+ if(!!(directory_entry::metadata_supported() & metadata_flags::birthtim))
+ detail::hash_combine(seed, p.st_birthtim().time_since_epoch().count());
+ return seed;
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+};
+
+/*! \brief The abstract base class encapsulating a platform-specific file handle
+
+Note that failure to explicitly schedule closing a file handle in the dispatcher means it will be synchronously closed on last reference count
+by handle. This can consume considerable time, especially if SyncOnClose is enabled.
+
+\qbk{
+[include generated/struct_handle_1_1mapped_file.qbk]
+[include generated/group_async_io_handle__ops.qbk]
+}
+*/
+class handle : public std::enable_shared_from_this<handle>
+{
+ friend class dispatcher;
+ friend struct detail::async_io_handle_posix;
+ friend struct detail::async_io_handle_windows;
+ friend class detail::async_file_io_dispatcher_compat;
+ friend class detail::async_file_io_dispatcher_windows;
+ friend class detail::async_file_io_dispatcher_linux;
+ friend class detail::async_file_io_dispatcher_qnx;
+
+ dispatcher *_parent;
+ chrono::system_clock::time_point _opened;
+ file_flags _flags;
+protected:
+ handle_ptr dirh;
+ atomic<off_t> bytesread, byteswritten, byteswrittenatlastfsync;
+ handle(dispatcher *parent, file_flags flags) : _parent(parent), _opened(chrono::system_clock::now()), _flags(flags), bytesread(0), byteswritten(0), byteswrittenatlastfsync(0) { }
+ //! Calling this directly can cause misoperation. Best to avoid unless you have inspected the source code for the consequences.
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void close() BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+public:
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~handle() { }
+ //! Returns the parent of this io handle
+ dispatcher *parent() const { return _parent; }
+ //! Returns a handle to the directory containing this handle. Only works if `file_flags::hold_parent_open` was specified when this handle was opened.
+ handle_ptr container() const { return dirh; }
+ //! In which way this handle is opened or not
+ enum class open_states
+ {
+ closed, //!< This handle is closed.
+ open, //!< This handle is open as a normal handle.
+ opendir //!< This handle is open as a cached directory handle, and therefore closing it explicitly has no effect.
+ };
+ //! Returns if this handle is opened or not
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC open_states is_open() const BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ //! Returns the native handle of this io handle. On POSIX, you can cast this to a fd using `(int)(size_t) native_handle()`. On Windows it's a simple `(HANDLE) native_handle()`.
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void *native_handle() const BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ //! Returns when this handle was opened
+ const chrono::system_clock::time_point &opened() const { return _opened; }
+ /*! \brief Returns the path of this i/o handle right now if the handle is open and \em refresh is true, else last known good. May be null if the file has been deleted.
+
+ Note the refreshed path completely dereferences any intermediate symbolic links to return a truly absolute canonical path, and therefore may look quite different to before.
+ Some operating systems unfortunately also return any one of the hard links to the file, so if hard links is greater than one the path refreshed will randomly permute.
+
+ \ntkernelnamespacenote
+ \return The path of this i/o handle right now.
+ \param refresh Whether to ask the OS for the current path of this handle.
+ \ingroup async_io_handle__ops
+ \raceguarantees{
+ [raceguarantee FreeBSD..Paths are only refreshed for directories, not files.]
+ [raceguarantee Linux, Windows..Paths are always refreshed and ignore other hard links.]
+ [raceguarantee OS X..Paths are only refreshed for directories and files with a single hard link.]
+ }
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path(bool refresh=false) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ //! Returns the last known good path of this i/o handle. May be null if the file has been deleted.
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path() const BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ //! Returns the final flags used when this handle was opened
+ file_flags flags() const { return _flags; }
+ //! True if this handle was opened as a file
+ bool opened_as_file() const { return !(_flags&file_flags::int_opening_dir) && !(_flags&file_flags::int_opening_link); }
+ //! True if this handle was opened as a directory
+ bool opened_as_dir() const { return !!(_flags&file_flags::int_opening_dir); }
+ //! True if this handle was opened as a symlink
+ bool opened_as_symlink() const { return !!(_flags&file_flags::int_opening_link); }
+ //! True if this handle is used by the directory handle cache (not UniqueDirectoryHandle and is open for write and not open for write)
+ bool available_to_directory_cache() const { return opened_as_dir() && !(_flags&file_flags::unique_directory_handle) && !!(_flags&file_flags::read) && !(_flags&file_flags::write); }
+ //! Returns how many bytes have been read since this handle was opened.
+ off_t read_count() const { return bytesread; }
+ //! Returns how many bytes have been written since this handle was opened.
+ off_t write_count() const { return byteswritten; }
+ //! Returns how many bytes have been written since this handle was last fsynced.
+ off_t write_count_since_fsync() const { return byteswritten-byteswrittenatlastfsync; }
+ /*! \brief Returns a mostly filled directory_entry for the file or directory referenced by this handle. Use `metadata_flags::All` if you want it as complete as your platform allows, even at the cost of severe performance loss.
+
+ Related types: `__afio_directory_entry__`, `__afio_stat_t__`
+ \return A directory entry for this handle.
+ \param wanted The metadata wanted.
+ \ingroup async_io_handle__ops
+ \raceguarantees{
+ [raceguarantee FreeBSD..Race free if handle open for directories and regular files only, else if handle closed or a symlink race free up to the containing directory. All metadata is fetched in a single shot.]
+ [raceguarantee Linux..Race free if handle open, else if handle closed race free up to the containing directory. All metadata is fetched in a single shot.]
+ [raceguarantee OS X..Race free if handle open for directories and regular files only. No guarantees if handle closed or a symlink.]
+ [raceguarantee Windows..Handle must be open and is always race free. Metadata may be fetched in a single shot if at least two categories requested, or else the following categories apply: (i) ino (ii) type, atim, mtim, ctim, birthtim, sparse, compressed (iii) nlink, size, allocated, blocks.]
+ }
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC directory_entry direntry(metadata_flags wanted=directory_entry::metadata_fastpath()) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Returns a mostly filled stat_t structure for the file or directory referenced by this handle. Use `metadata_flags::All` if you want it as complete as your platform allows, even at the cost of severe performance loss. Calls direntry(), so same race guarantees as that call.
+
+ Related types: `__afio_directory_entry__`, `__afio_stat_t__`
+ */
+ stat_t lstat(metadata_flags wanted=directory_entry::metadata_fastpath())
+ {
+ directory_entry de(direntry(wanted));
+ return de.fetch_lstat(handle_ptr() /* actually unneeded */, wanted);
+ }
+ /*! \brief Returns the target path of this handle if it is a symbolic link.
+
+ \ntkernelnamespacenote
+ \return The path the symbolic link points to. May not exist or even be valid.
+ \ingroup async_io_handle__ops
+ \raceguarantees{
+ [raceguarantee FreeBSD..Race free up to the containing directory.]
+ [raceguarantee Linux, Windows..Race free if handle open, else up to the containing directory.]
+ [raceguarantee OS X..No guarantees.]
+ }
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path target() BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ //! A holder of a mapped file.
+ struct BOOST_AFIO_DECL mapped_file
+ {
+ friend class handle;
+ handle_ptr h; //!< The file being mapped
+ void *addr; //!< The address in memory of the map
+ size_t length; //!< The length of the map
+ off_t offset; //!< The offset of the map into the file
+ mapped_file(const mapped_file &) = delete;
+ mapped_file(mapped_file &&) = delete;
+ mapped_file &operator=(const mapped_file &) = delete;
+ mapped_file &operator=(mapped_file &&) = delete;
+ mapped_file(handle_ptr _h, void *_addr, size_t _length, off_t _offset) : h(std::move(_h)), addr(_addr), length(_length), offset(_offset) { }
+ ~mapped_file();
+ };
+ //! A type alias to a mapped file pointer
+ using mapped_file_ptr = std::unique_ptr<mapped_file>;
+ //! Maps the file into memory, returning a null pointer if couldn't map (e.g. address space exhaustion). Do NOT mix this with `file_flags::os_direct`!
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC mapped_file_ptr map_file(size_t length = (size_t)-1, off_t offset = 0, bool read_only = false) { return nullptr; }
+ /*! \brief Hard links the file to a new location on the same volume.
+
+ If you wish to make a temporary file whose contents are ready appear at a location and error out if
+ a file entry is already there, use link() and if success, unlink() on the former location. If you wish
+ to always overwrite the destination, use atomic_relink() instead.
+
+ On Windows, the destination directory cannot have any handle opened to it with delete/rename privileges (`file_flags::write`)
+ anywhere in the system. This is an operating system limitation.
+
+ \ntkernelnamespacenote
+
+ Related types: `__afio_path_req__`
+
+ \param req The absolute or relative (in which case precondition specifies a directory) path to create a hard link at.
+ \ingroup async_io_handle__ops
+ \raceguarantees{
+ [raceguarantee FreeBSD..Race free up to the containing directory for both source and target.]
+ [raceguarantee Linux, Windows..Race free for source if handle open, else up to the containing directory. Race free up to the target directory.]
+ [raceguarantee OS X..No guarantees.]
+ }
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void link(const path_req &req) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Unlinks the file from its present location as determined by path(true), which could be any hard link on
+ those operating systems with an unstable path(true). Other links may remain to the same file.
+
+ On Microsoft Windows, this routine unlinks items as follows:
+
+ 1. It tries to atomically rename the item to the root of the mounted volume it lives in with a .afiodXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX where
+ the X's are a 128 bit crypto random hexadecimal. If that fails, it tries the next directory up, and
+ the next after that until success if any. This rename may fail for any reason, including if it is a directory with
+ open file handles somewhere within. If it fails, the rename is skipped.
+
+ 2. It marks the item with hidden and system attributes to hide it from normal directory enumeration.
+
+ 3. It sets the delete on last handle close flag. At some stl_future point Windows will delete the item, until which it will hang
+ around in a zombie state with an unknowable name and location unopenable by any new processes.
+
+ The reason for such complexity is that this algorithm, if it renames successfully, neatly works around a number of
+ annoying features in Windows, specifically that when you delete a file you actually don't delete it till an unknown amount
+ of time later. This breaks code which tries to delete a directory tree, and finds that the directories won't delete because
+ they still contain files supposedly deleted but actually not quite yet. By renaming the items as far away as possible, this
+ problem ought to go away - unless of course that the user does not have permissions to write into any directory other than the
+ one being eventually deleted, in which case you will still see the strange access denied and directory not empty errors from
+ before.
+
+ \ntkernelnamespacenote
+
+ Related types: `__afio_path_req__`
+
+ \ingroup async_io_handle__ops
+ \raceguarantees{
+ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+ [raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+ [raceguarantee OS X..No guarantees.]
+ }
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlink() BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Links the file to a new location and unlinks the file from its present location as determined by path(true),
+ <em>atomically overwriting any file entry at the new location</em>. Very useful for preparing file content elsewhere and once ready, atomically
+ making it visible at some named location to other processes. Note that operating systems with an unstable path(true) may
+ relink any hard link to the file to the new location.
+
+ Note that not all filing systems guarantee the atomicity of the relink itself (i.e. the file may appear at two locations
+ in the filing system for a period of time), though all supported platforms do
+ guarantee the atomicity of the replaced location i.e. the location you are relinking to will always refer to
+ some valid file to all readers, and will never be deleted or missing. Some filing systems may also fail to do the unlink
+ if power is lost close to the relinking operation.
+
+ On Windows, the destination directory cannot have any handle opened to it with delete/rename privileges (`file_flags::write`)
+ anywhere in the system. This is an operating system limitation.
+
+ \ntkernelnamespacenote
+
+ Related types: `__afio_path_req__`
+
+ \param req The absolute or relative (in which case precondition specifies a directory) path to relink to.
+ \ingroup async_io_handle__ops
+ \raceguarantees{
+ [raceguarantee FreeBSD, Linux..Race free up to the containing directory for both source and target.]
+ [raceguarantee OS X..No guarantees.]
+ [raceguarantee Windows..Race free for source if handle open, else up to the containing directory. Race free up to the target directory.]
+ }
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void atomic_relink(const path_req &req) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+
+#if 0
+ // Undocumented deliberately
+ enum class change_flags : size_t
+ {
+ created=(1<<0), // NOTE_EXTEND, IN_CREATE,
+ renamed=(1<<1), // NOTE_RENAME, IN_MOVED_FROM/IN_MOVED_TO,
+ deleted=(1<<2), // NOTE_DELETE, IN_DELETE,
+ attributes=(1<<3), // NOTE_ATTRIB, IN_ATTRIB,
+ opened=(1<<4), // ?, IN_OPEN,
+ closed=(1<<5), // ?, IN_CLOSE_WRITE/IN_CLOSE_NOWRITE,
+ read=(1<<6), // ?, IN_ACCESS,
+ written=(1<<7), // NOTE_WRITE, IN_MODIFY,
+ extended=(1<<8), // NOTE_EXTEND, ?,
+
+ region_locked=(1<<16),
+ region_timedout=(1<<17),
+ region_unlocked=(1<<18)
+ };
+ // Undocumented deliberately
+ struct change_listener : public std::enable_shared_from_this<change_listener>
+ {
+ virtual ~change_listener() { }
+ virtual void operator()(handle *h, change_flags changed, void *additional)=0;
+ };
+ // Undocumented deliberately
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void listen(const std::vector<std::pair<change_flags>, std::shared_ptr<change_listener>> &listeners) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ // Undocumented deliberately
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlisten(const std::vector<std::pair<change_flags>, std::shared_ptr<change_listener>> &listeners) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+#endif
+};
+
+/*! \brief Retrieves the currently set async_file_io_dispatcher for this thread, optionally setting it to
+a new dispatcher.
+
+\return The current async_file_io_dispatcher.
+\param new_dispatcher The new async_file_io_dispatcher to set.
+\ingroup async_file_io_dispatcher
+*/
+BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC dispatcher_ptr current_dispatcher(option<dispatcher_ptr> new_dispatcher = empty);
+
+/*! \class current_dispatcher_guard
+
+RAII holder for the current async file i/o dispatcher.
+
+\ingroup async_file_io_dispatcher
+*/
+class current_dispatcher_guard
+{
+ dispatcher_ptr _old;
+public:
+ current_dispatcher_guard(dispatcher_ptr _new) : _old(current_dispatcher(_new)) { }
+ ~current_dispatcher_guard() { current_dispatcher(_old); }
+ //! Restore the former async file i/o dispatcher now.
+ void release() { current_dispatcher(_old); _old.reset(); }
+ //! Don't restore the former async file i/o dispatcher.
+ void dismiss() { _old.reset(); }
+ //! Set a different former async file i/o dispatcher on destruction.
+ void reset(dispatcher_ptr p) { _old=p; }
+};
+
+//! Trait for determining if a type is an afio::future<T>
+template<class T> struct is_future : std::false_type { };
+template<class T> struct is_future<future<T>> : std::true_type {};
+
+// Temporary friends for future<>
+namespace detail
+{
+ struct barrier_count_completed_state;
+ template<bool rethrow, class Iterator> inline stl_future<std::vector<handle_ptr>> when_all_ops(Iterator first, Iterator last);
+ template<bool rethrow, class Iterator> inline stl_future<handle_ptr> when_any_ops(Iterator first, Iterator last);
+
+ // Shim code for lightweight future continuations
+ template<class R, bool return_is_lightweight_future=is_lightweight_future<R>::value, bool return_is_afio_future=is_future<R>::value> struct continuation_return_type { using future_type = future<R>; using promise_type = void; };
+ template<class R, bool _> struct continuation_return_type<R, true, _> { using future_type = R; using promise_type = typename future_type::promise_type; };
+ template<class R, bool _> struct continuation_return_type<R, _, true> { using future_type = R; using promise_type = void; };
+ template<class future_type, class promise_type> struct do_continuation;
+}
+
+/*! \ref future
+*/
+template<> class future<void>
+{
+ // Temporary friends until lightweight future promise comes in
+ friend struct detail::barrier_count_completed_state;
+ template<bool rethrow, class Iterator> friend inline stl_future<std::vector<handle_ptr>> detail::when_all_ops(Iterator first, Iterator last);
+ template<bool rethrow, class Iterator> friend inline stl_future<handle_ptr> detail::when_any_ops(Iterator first, Iterator last);
+
+ dispatcher *_parent; //!< The parent dispatcher
+ size_t _id; //!< A unique id for this operation
+ shared_future<handle_ptr> _h; //!< A stl_future handle to the item being operated upon
+public:
+ future(future<void> &&o, stl_future<void> &&result) : future<void>(std::move(o)) { }
+ // NOTE TO SELF: MAKE THE CONSTRUCTORS AND MEMBER FUNCTIONS constexpr WHEN I MERGE LIGHTWEIGHT FUTURE-PROMISES
+
+ //! The type of value potentially returned
+ using value_type = void;
+ //! The error type potentially returned
+ using error_type = error_code;
+ //! The exception type potentially returned
+ using exception_type = exception_ptr;
+
+ //! \constr
+ future() : _parent(nullptr), _id(0) { }
+ //! \cconstr
+ future(const future &o) = default;
+ //! \mconstr
+ future(future &&o) = default;
+ /*! Constructs an instance.
+ \param parent The dispatcher this op belongs to.
+ \param id The unique non-zero id of this op.
+ \param handle A shared_ptr to shared state between all instances of this reference.
+ \param check_handle Whether to have validation additionally check if a handle is not null
+ \param validate Whether to check the inputs and shared state for valid (and not errored) values
+ */
+ future(dispatcher *parent, size_t id, shared_future<handle_ptr> handle, bool check_handle=true, bool validate=true) : _parent(parent), _id(id), _h(std::move(handle)) { if(validate) _validate(check_handle); }
+ /*! Constructs an instance.
+ \param _handle A shared_ptr to shared state between all instances of this reference.
+ \param check_handle Whether to have validation additionally check if a handle is not null
+ \param validate Whether to check the inputs and shared state for valid (and not errored) values
+ */
+ future(handle_ptr _handle, bool check_handle=true, bool validate=true) : _parent(_handle->parent()), _id((size_t)-1) { promise<handle_ptr> p; p.set_value(std::move(_handle)); _h=p.get_future(); if(validate) _validate(check_handle); }
+ /*! Constructs an instance.
+ \param parent The dispatcher this op belongs to.
+ \param id The unique non-zero id of this op.
+ */
+ future(dispatcher *parent, size_t id) : _parent(parent), _id(id) { }
+ //! \cassign
+ future &operator=(const future &o) { _parent = o._parent; _id = o._id; _h = o._h; return *this; }
+ //! \massign
+ future &operator=(future &&o) noexcept { _parent = std::move(o._parent); _id = std::move(o._id); _h = std::move(o._h); return *this; }
+
+ //! True if this future is valid
+ bool valid() const noexcept { return _parent && _id; }
+ //! \brief Same as `true_(tribool(*this))`
+ explicit operator bool() const noexcept { return has_value(); }
+ //! \brief True if monad is not empty
+ bool is_ready() const noexcept
+ {
+ return valid() || _h.wait_for(chrono::seconds(0)) == future_status::ready;
+ }
+ //! \brief True if monad contains a value_type
+ bool has_value() const noexcept { return is_ready() && !has_exception(); }
+ //! \brief True if monad contains an error_type
+ bool has_error() const noexcept
+ {
+ if (!is_ready())
+ return false;
+ error_type ec = get_error();
+ return ec && ec.category() != monad_category();
+ }
+ /*! \brief True if monad contains an exception_type or error_type (any error_type is returned as an exception_ptr by get_exception()).
+ This needs to be true for both for compatibility with Boost.Thread's future. If you really want to test only for has exception only,
+ pass true as the argument.
+ */
+ bool has_exception(bool only_exception = false) const noexcept
+ {
+ if (!is_ready())
+ return false;
+ return !!get_exception();
+ }
+
+ //! The parent dispatcher of this future
+ dispatcher *parent() const noexcept { return _parent; }
+ //! \deprecate{Expected to be removed in the v1.5 engine}
+ size_t id() const noexcept { return _id; }
+ //! Retrieves the handle or exception from the shared state, rethrowing any exception. Returns a null shared pointer if this future is invalid.
+ handle_ptr get_handle(bool return_null_if_errored=false) const
+ {
+ if(!_parent && !_id)
+ return handle_ptr();
+ // std::shared_future in older libstdc++ does not have a const get().
+ if(!return_null_if_errored)
+ return const_cast<future *>(this)->_h.get();
+ auto e=get_exception_ptr(_h);
+ return e ? handle_ptr() : const_cast<future *>(this)->_h.get();
+ }
+ //! Retrieves the handle or exception from the shared state, rethrowing any exception but setting _ec if there is an error. Returns a null shared pointer if this future is invalid.
+ handle_ptr get_handle(error_type &ec) const
+ {
+ if (!_parent && !_id)
+ return handle_ptr();
+ ec = get_error();
+ return ec ? handle_ptr() : const_cast<future *>(this)->_h.get();
+ }
+ //! Dereferences the handle from the shared state. Same as *h.get_handle().
+ const handle &operator *() const { return *get_handle(); }
+ //! Dereferences the handle from the shared state. Same as *h.get_handle().
+ handle &operator *() { return *get_handle(); }
+ //! Dereferences the handle from the shared state. Same as h.get_handle()->get().
+ const handle *operator->() const { return get_handle().get(); }
+ //! Dereferences the handle from the shared state. Same as h.get_handle()->get().
+ handle *operator->() { return get_handle().get(); }
+ //! Waits for the future to become ready, rethrowing any exception found. Throws a `future_errc::no_state` if this future is invalid.
+ void get()
+ {
+ if (!valid())
+ throw future_error(future_errc::no_state);
+ _h.get();
+ }
+ //! Waits for the future to become ready, returning any error state found
+ error_type get_error() const
+ {
+ if (!valid())
+ throw future_error(future_errc::no_state);
+ auto e = get_exception_ptr(_h);
+ if (e)
+ {
+ try
+ {
+ rethrow_exception(e);
+ }
+ catch (const system_error &_e)
+ {
+ return _e.code();
+ }
+ catch (...)
+ {
+ return error_type((int)monad_errc::exception_present, monad_category());
+ }
+ }
+ return error_type();
+ }
+ //! Waits for the future to become ready, returning any error state found
+ exception_type get_exception() const
+ {
+ if (!valid())
+ throw future_error(future_errc::no_state);
+ return get_exception_ptr(_h);
+ }
+ //! Waits for the future to become ready. Throws a `future_errc::no_state` if this future is invalid.
+ void wait() const
+ {
+ if (!valid())
+ throw future_error(future_errc::no_state);
+ _h.wait();
+ }
+ //! Waits for the future to become ready for a period. Throws a `future_errc::no_state` if this future is invalid.
+ template<class Rep, class Period> future_status wait_for(const chrono::duration<Rep, Period> &duration) const
+ {
+ if (!valid())
+ throw future_error(future_errc::no_state);
+ return _h.wait_for(duration);
+ }
+ //! Waits for the future to become ready until a deadline. Throws a `future_errc::no_state` if this future is invalid.
+ template<class Clock, class Duration> future_status wait_until(const chrono::time_point<Clock, Duration> &deadline) const
+ {
+ if (!valid())
+ throw future_error(future_errc::no_state);
+ return _h.wait_until(deadline);
+ }
+ //! Schedules a callable to be invoked after this future becomes ready. If this future is null, use the current async file i/o dispatcher.
+ template<class U> auto then(U &&f) -> typename detail::continuation_return_type<decltype(f(*this))>::future_type
+ {
+ using future_type = typename detail::continuation_return_type<decltype(f(*this))>::future_type;
+ using promise_type = typename detail::continuation_return_type<decltype(f(*this))>::promise_type;
+ return detail::do_continuation<future_type, promise_type>()(parent(), this, std::forward<U>(f));
+ }
+ //! Validates contents
+ bool validate(bool check_handle=true) const
+ {
+ if(!valid()) return false;
+ // If h is valid and ready and contains an exception, throw it now
+ if(_h.valid() && BOOST_AFIO_V2_NAMESPACE::is_ready(_h))
+ {
+ if(check_handle)
+ if(!const_cast<shared_future<handle_ptr> &>(_h).get().get())
+ return false;
+ }
+ return true;
+ }
+protected:
+ void _validate(bool check_handle=true) const
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if(!validate(check_handle))
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ }
+};
+/*! \class future
+\tparam T Any returned result. Note this is defaulted to `void` for you, so usually you write `future<>`.
+\brief The future status of a scheduled asynchronous operation
+
+As of v1.4 of the AFIO engine, the legacy `async_io_op` struct has been replaced with this custom future type
+based on the lightweight future-promise factory toolkit in forthcoming Boost.Monad. This custom future type
+consists of two pieces of future data each with different semantics:
+
+1. A `handle_ptr` retrieved using `get_handle()`, `operator*` and `operator->` - this is
+the shared i/o handle returned by the asynchronous operation. This has non-consuming semantics i.e. you can call
+`get_handle()` as many times as you like. Note that for legacy compatibility reasons, calling `get_handle()` on
+an invalid instance returns a null shared pointer instead of an exception throw.
+
+2. If T is non-void, any type `T` - this is any additional data returned by an asynchronous operation above and beyond the i/o handle
+(e.g. `enumerate()` returns a `std::pair<std::vector<directory_entry>, bool>`. This has *consuming* semantics, so
+calling `get()` returns the result exactly once.
+
+The reason for the difference in semantics is because it is very common that you need access to an earlier i/o handle
+in a sequence if some operation returns an error, and besides the shared pointer encapsulation makes non-consumption
+cost free.
+
+Other than the fact that `get()` returns a `T` and `get_handle()` returns a handle, the errored and excepted state
+for both is identical and non-consuming for both.
+
+Finally, note that there is a freely available type slice from `future<T>` to `future<void>` which moves/copies only
+the `future<void>` part of the `future<T>`, leaving the `T` behind. This is because the AFIO engine resides behind a stable
+ABI layer which cannot know anything about arbitrary types, and therefore it accepts only `future<void>`. Equally, this
+means you can supply a `future<T>` as a precondition to another op safe in the knowledge that any `T` part will remain
+untouched for later consumption.
+*/
+template<class T> class future : public future<void>
+{
+ stl_future<T> _result;
+public:
+ //! The type of value potentially returned
+ using value_type = T;
+
+ future() = default;
+ /*! Constructs an instance.
+ \param parent The dispatcher this op belongs to.
+ \param id The unique non-zero id of this op.
+ \param handle A shared_future to shared state between all instances of this reference.
+ \param result A future to any result from the operation.
+ \param check_handle Whether to have validation additionally check if a handle is not null
+ \param validate Whether to check the inputs and shared state for valid (and not errored) values
+ */
+ future(dispatcher *parent, size_t id, shared_future<handle_ptr> handle, stl_future<T> result, bool check_handle = true, bool validate = true) : future<void>(parent, id, std::move(handle), check_handle, validate), _result(std::move(result)) { }
+ /*! Constructs an instance from an existing future<void>
+ \param o The future<void>
+ \param result The future<T> to add
+ */
+ future(future<void> &&o, stl_future<T> &&result) : future<void>(std::move(o)), _result(std::move(result)) { }
+ //! True if this future is valid
+ bool valid(bool just_handle=false) const noexcept { return future<void>::valid() && (just_handle || _result.valid()); }
+ //! Waits for the future to become ready, returning any value or rethrowing any exception found. Throws a `future_errc::no_state` if this future is invalid.
+ T get()
+ {
+ return _result.get();
+ }
+ //! Schedules a callable to be invoked after this future becomes ready. If this future is null, use the current async file i/o dispatcher.
+ template<class U> auto then(U &&f) -> typename detail::continuation_return_type<decltype(f(*this))>::future_type
+ {
+ using future_type = typename detail::continuation_return_type<decltype(f(*this))>::future_type;
+ using promise_type = typename detail::continuation_return_type<decltype(f(*this))>::promise_type;
+ return detail::do_continuation<future_type, promise_type>()(parent(), this, std::forward<U>(f));
+ }
+};
+
+namespace detail
+{
+ // For continuations returning lightweight futures
+ template<class future_type, class promise_type> struct do_continuation
+ {
+ template<class D, class U> future_type operator()(D *d, future<> *src, U &&f)
+ {
+ if (!d) d = current_dispatcher().get();
+ auto p = std::make_shared<promise_type>();
+ d->completion(*src, std::make_pair(async_op_flags::immediate, [f, p](size_t id, future<> _f) -> std::pair<bool, handle_ptr> {
+ try
+ {
+ using result_type = decltype(f(_f));
+ f(_f).then([p](const result_type &_f) {
+ auto s(_f.get_state());
+ try
+ {
+ p->set_state(std::move(s));
+ }
+ catch (...) { /* Really should filter for no_state but this is shim code */ }
+ });
+ }
+ catch (...)
+ {
+ p->set_exception(current_exception());
+ }
+ return std::make_pair(true, _f.get_handle());
+ }));
+ return p->get_future();
+ }
+ };
+ // For continuations returning shim AFIO futures or some naked type
+ template<class R> struct do_continuation<future<R>, void>
+ {
+ template<class D, class T, class U> future<R> operator()(D *d, future<T> *src, U &&f)
+ {
+ if (!d) d = current_dispatcher().get();
+ // TEMPORARY: For continuations taking a future<T> where T is not void
+ // we have no way of passing the correct future to the continuation until
+ // the lightweight future promise refactor
+ //
+ // So simply call the continuation now. When it calls .get() it will block.
+ return f(*src);
+ }
+ template<class D, class U> future<> operator()(D *d, future<> *src, U &&f)
+ {
+ if (!d) d = current_dispatcher().get();
+ return d->completion(*src, std::make_pair(async_op_flags::immediate, [f](size_t id, future<> _f) -> std::pair<bool, handle_ptr> {
+ f(_f);
+ return std::make_pair(true, _f.get_handle());
+ }));
+ }
+ };
+}
+
+// This is a result_of filter to work around the weird mix of brittle decltype(), SFINAE incapable
+// std::result_of and variadic template overload resolution rules in VS2013. Works on other compilers
+// too of course, it simply prefilters out the call() overloads not matching the variadic overload.
+namespace detail
+{
+#if 0
+ template<class C, class... Args> struct vs2013_variadic_overload_resolution_workaround;
+ // Match callable
+ template<class R, class... OArgs, class... Args> struct vs2013_variadic_overload_resolution_workaround<R (*)(OArgs...), Args...>
+ {
+ typedef typename std::result_of<R(*)(Args...)>::type type;
+ };
+ // Match callable
+ template<class R, class T, class... OArgs, class... Args> struct vs2013_variadic_overload_resolution_workaround<R (T::*)(OArgs...) const, Args...>
+ {
+ typedef typename std::result_of<R (T::*)(Args...) const>::type type;
+ };
+ // Match callable
+ template<class R, class T, class... OArgs, class... Args> struct vs2013_variadic_overload_resolution_workaround<R (T::*const)(OArgs...) const, Args...>
+ {
+ typedef typename std::result_of<R (T::*const)(Args...) const>::type type;
+ };
+#else
+ /*
+ call(const std::vector<future> &ops , const std::vector<std::function<R()>> &callables );
+ call(const std::vector<std::function<R()>> &callables );
+ call(const future &req , std::function<R()> callback );
+ call(const future &req , C callback , Args... args);
+ */
+ template<class C, class... Args> struct vs2013_variadic_overload_resolution_workaround
+ {
+ typedef typename std::result_of<C(Args...)>::type type;
+ };
+ // Disable C being a const std::vector<std::function<R()>> &callables
+ template<class T, class... Args> struct vs2013_variadic_overload_resolution_workaround<std::vector<T>, Args...>;
+#endif
+ template<class Impl, class Handle> handle_ptr decode_relative_path(path_req &req, bool force_absolute=false);
+}
+
+/*! \class dispatcher
+\brief Abstract base class for dispatching file i/o asynchronously
+
+This is a reference counted instance with platform-specific implementation in object code.
+Construct an instance using the `boost::afio::make_dispatcher()` function.
+
+\qbk{
+[/ link afio.reference.functions.async_file_io_dispatcher `async_file_io_dispatcher()`]
+[/ include generated/group_dispatcher__filter.qbk]
+[/ include generated/group_dispatcher__completion.qbk]
+[/ include generated/group_dispatcher__call.qbk]
+[include generated/group_dispatcher__filedirops.qbk]
+[include generated/group_dispatcher__enumerate.qbk]
+[include generated/group_dispatcher__extents.qbk]
+[include generated/group_dispatcher__statfs.qbk]
+[/ include generated/group_dispatcher__depends.qbk]
+[/ include generated/group_dispatcher__barrier.qbk]
+[include generated/group_dispatcher__misc.qbk]
+}
+*/
+class BOOST_AFIO_DECL dispatcher : public std::enable_shared_from_this<dispatcher>
+{
+ //friend BOOST_AFIO_DECL dispatcher_ptr async_file_io_dispatcher(thread_source &threadpool=process_threadpool(), file_flags flagsforce=file_flags::none, file_flags flagsmask=file_flags::none);
+ template<class Impl, class Handle> friend handle_ptr detail::decode_relative_path(path_req &req, bool force_absolute);
+ friend struct detail::async_io_handle_posix;
+ friend struct detail::async_io_handle_windows;
+ friend class detail::async_file_io_dispatcher_compat;
+ friend class detail::async_file_io_dispatcher_windows;
+ friend class detail::async_file_io_dispatcher_linux;
+ friend class detail::async_file_io_dispatcher_qnx;
+
+ detail::dispatcher_p *p;
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_directory_cached_handle_path_changed(path oldpath, path newpath, handle_ptr h);
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_add_io_handle(void *key, handle_ptr h);
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_del_io_handle(void *key);
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> int_op_from_scheduled_id(size_t id) const;
+
+protected:
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher(std::shared_ptr<thread_source> threadpool, file_flags flagsforce, file_flags flagsmask);
+ std::pair<bool, handle_ptr> doadopt(size_t, future<>, handle_ptr h)
+ {
+ return std::make_pair(true, h);
+ }
+public:
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void testing_flags(detail::unit_testing_flags flags);
+ //! Destroys the dispatcher, blocking inefficiently if any ops are still in flight.
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~dispatcher();
+
+ //! Returns the thread source used by this dispatcher
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::shared_ptr<thread_source> threadsource() const;
+ //! Returns file flags as would be used after forcing and masking bits passed during construction
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC file_flags fileflags(file_flags flags) const;
+ //! Returns the current wait queue depth of this dispatcher
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t wait_queue_depth() const;
+ //! Returns the number of open items in this dispatcher
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t fd_count() const;
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ /* \brief Returns an op ref for a given \b currently scheduled op id, throwing an exception if id not scheduled at the point of call.
+ Can be used to retrieve exception state from some op id, or one's own shared stl_future.
+
+ \return An future<> with the same shared stl_future as all op refs with this id.
+ \param id The unique integer id for the op.
+ */
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> op_from_scheduled_id(size_t id) const;
+
+ // The type of an op filter callback handler \ingroup dispatcher__filter
+ typedef void filter_t(detail::OpType, future<> &);
+ // The type of a readwrite filter callback handler \ingroup dispatcher__filter
+ typedef void filter_readwrite_t(detail::OpType, handle *, const detail::io_req_impl<true> &, off_t, size_t, size_t, const error_code &, size_t);
+ /* \brief Clears the post op and readwrite filters. Not threadsafe.
+
+ \ingroup dispatcher__filter
+ \complexity{O(1).}
+ \qexample{filter_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void post_op_filter_clear();
+ /* \brief Install op filters for non-buffer taking ops. Not threadsafe.
+
+ `std::function<dispatcher::filter_t>` will be called after every op of type `detail::OpType`
+ completes (`detail::OpType::Unknown` means call this filter for all ops) with the op type and op output.
+
+ Note that filters are currently implemented as a linear scan, so a full iteration of all filters is done
+ for every op completed. The filter is called straight after an op's stl_future is set and before any completions
+ are issued. Any exceptions thrown by the filter are thrown away.
+
+ \param filters A batch of pairs of op type to be filtered and bound filter handler functions of type `filter_t`
+ \ingroup dispatcher__filter
+ \complexity{O(N) where N is the total number of filters currently configured.}
+ \qexample{filter_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void post_op_filter(std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_t>>> filters);
+ /* \brief Install read/write op filters, useful for tight ASIO integration. Not threadsafe.
+
+ `std::function<dispatcher::filter_buffers_t>` will be called after every op of type `detail::OpType`
+ completes (`detail::OpType::Unknown` means call this filter for all ops) with the op type, file handle, op input,
+ file offset, buffers offset, buffers amount, error state and bytes transferred. Any filter other than read() and write()
+ will be ignored, for those use post_op_filter().
+
+ Note that buffer filters are currently implemented as a linear scan, so a full iteration of all buffer filters is done
+ for every read/write op completed. The filter is called straight after a read or write operation has completed, and
+ BEFORE any checks that it transferred the data it was supposed to. Any exceptions thrown by the filter are reported
+ as if the read/write operation threw them, and filter processing stops at the filter which threw.
+
+ \param filters A batch of pairs of op type to be filtered and bound filter handler functions of type `filter_buffers_t`
+ \ingroup dispatcher__filter
+ \complexity{O(N) where N is the total number of filters currently configured.}
+ \qexample{filter_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void post_readwrite_filter(std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_readwrite_t>>> filters);
+
+ // The type returned by a completion handler \ingroup dispatcher__completion
+ typedef std::pair<bool, handle_ptr> completion_returntype;
+ // The type of a completion handler \ingroup dispatcher__completion
+ typedef completion_returntype completion_t(size_t, future<>);
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+#if defined(BOOST_AFIO_ENABLE_BENCHMARKING_COMPLETION) || BOOST_AFIO_HEADERS_ONLY==0 // Only really used for benchmarking
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> completion(const std::vector<future<>> &ops, const std::vector<std::pair<async_op_flags, dispatcher::completion_t *>> &callbacks);
+ inline future<> completion(const future<> &req, const std::pair<async_op_flags, dispatcher::completion_t *> &callback);
+#endif
+#endif
+ /* \brief Schedule a batch of asynchronous invocations of the specified functions when their supplied operations complete.
+
+ \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.}
+ \return A batch of op handles
+ \param ops A batch of precondition op handles.
+ \param callbacks A batch of pairs of op flags and bound completion handler functions of type `completion_t`
+ \ingroup dispatcher__completion
+ \qbk{distinguish, batch bound functions}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.}
+ \exceptionmodelstd
+ \qexample{completion_example1}
+ */
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> completion(const std::vector<future<>> &ops, const std::vector<std::pair<async_op_flags, std::function<dispatcher::completion_t>>> &callbacks);
+ /* \brief Schedule the asynchronous invocation of the specified single function when the supplied single operation completes.
+
+ \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.}
+ \return An op handle
+ \param req A precondition op handle
+ \param callback A pair of op flag and bound completion handler function of type `completion_t`
+ \ingroup dispatcher__completion
+ \qbk{distinguish, single bound function}
+ \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
+ \exceptionmodelstd
+ \qexample{completion_example1}
+ */
+ inline future<> completion(const future<> &req, const std::pair<async_op_flags, std::function<dispatcher::completion_t>> &callback);
+
+ /* \brief Schedule a batch of asynchronous invocations of the specified bound functions when their supplied preconditions complete.
+
+ \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.}
+ This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t`
+ handler specification and calls the specified arbitrary callable, always returning completion on exit.
+
+ \return A pair with a batch of futures returning the result of each of the callables and a batch of op handles.
+ \tparam "class R" A compiler deduced return type of the bound functions.
+ \param ops A batch of precondition op handles. If default constructed, a precondition is null.
+ \param callables A batch of bound functions to call, returning R.
+ \ingroup dispatcher__call
+ \qbk{distinguish, batch bound functions}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.}
+ \exceptionmodelstd
+ \qexample{call_example}
+ */
+ template<class R> inline std::vector<future<R>> call(const std::vector<future<>> &ops, const std::vector<std::function<R()>> &callables);
+ /* \brief Schedule a batch of asynchronous invocations of the specified bound functions when their supplied preconditions complete.
+
+ \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.}
+ This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t`
+ handler specification and calls the specified arbitrary callable, always returning completion on exit. If you
+ are seeing performance issues, using `completion()` directly will have much less overhead.
+
+ \return A pair with a batch of futures returning the result of each of the callables and a batch of op handles.
+ \tparam "class R" A compiler deduced return type of the bound functions.
+ \param callables A batch of bound functions to call, returning R.
+ \ingroup dispatcher__call
+ \qbk{distinguish, batch bound functions without preconditions}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.}
+ \exceptionmodelstd
+ \qexample{call_example}
+ */
+ template<class R> std::vector<future<R>> call(const std::vector<std::function<R()>> &callables) { return call(std::vector<future<>>(), callables); }
+ /* \brief Schedule an asynchronous invocation of the specified bound function when its supplied precondition completes.
+
+ \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls .then() on the future.}
+ This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t`
+ handler specification and calls the specified arbitrary callable, always returning completion on exit. If you
+ are seeing performance issues, using `completion()` directly will have much less overhead.
+
+ \return A pair with a stl_future returning the result of the callable and an op handle.
+ \tparam "class R" A compiler deduced return type of the bound functions.
+ \param req A precondition op handle. If default constructed, the precondition is null.
+ \param callback A bound functions to call, returning R.
+ \ingroup async_file_io_dispatcher__call
+ \qbk{distinguish, single bound function}
+ \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
+ \exceptionmodelstd
+ \qexample{call_example}
+ */
+ template<class R> inline future<R> call(const future<> &req, std::function<R()> callback);
+
+
+
+
+ /* \brief Schedule an asynchronous invocation of the specified unbound callable when its supplied precondition completes.
+ Note that this function essentially calls `std::bind()` on the callable and the args and passes it to the other call() overload taking a `std::function<>`.
+ You should therefore use `std::ref()` etc. as appropriate.
+
+ This is effectively a convenience wrapper for `completion()`. It creates an enqueued_task matching the `completion_t`
+ handler specification and calls the specified arbitrary callable, always returning completion on exit. If you
+ are seeing performance issues, using `completion()` directly will have much less overhead.
+
+ \return A pair with a stl_future returning the result of the callable and an op handle.
+ \tparam "class C" Any callable type.
+ \tparam Args Any sequence of argument types.
+ \param req A precondition op handle. If default constructed, the precondition is null.
+ \param callback An unbound callable to call.
+ \param args An arbitrary sequence of arguments to bind to the callable.
+ \ingroup dispatcher__call
+ \qbk{distinguish, single unbound callable}
+ \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
+ \exceptionmodelstd
+ \qexample{call_example}
+ */
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ template<class C, class... Args> inline future<typename detail::vs2013_variadic_overload_resolution_workaround<C, Args...>::type> call(const future<> &req, C callback, Args... args);
+#else
+ template<class C, class... Args> inline future<typename std::result_of<C(Args...)>::type> call(const future<> &req, C callback, Args... args);
+#endif
+
+
+
+ /* \brief Schedule a batch of third party handle adoptions.
+
+ \docs_adopt
+
+ \return A batch of op handles.
+ \param hs A batch of handles to adopt.
+ \ingroup dispatcher__filedirops
+ \qbk{distinguish, batch}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete.}
+ \exceptionmodelstd
+ \qexample{adopt_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> adopt(const std::vector<handle_ptr> &hs);
+#endif
+ /*! \brief Schedule a batch of asynchronous directory creations and opens after optional preconditions.
+
+ \docs_dir
+ \ntkernelnamespacenote
+
+ \return A batch of op handles.
+ \param reqs A batch of `path_req` structures.
+ \ingroup dir
+ \qbk{distinguish, batch}
+ \raceguarantees{
+ [raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
+ [raceguarantee OS X..No guarantees.]
+ }
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if directory creation is constant time.}
+ \exceptionmodelstd
+ \qexample{filedir_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> dir(const std::vector<path_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Schedule a batch of asynchronous directory deletions after optional preconditions.
+
+ \docs_rmdir
+ \ntkernelnamespacenote
+
+ \return A batch of op handles.
+ \param reqs A batch of `path_req` structures.
+ \ingroup rmdir
+ \qbk{distinguish, batch}
+ \raceguarantees{
+ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+ [raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+ [raceguarantee OS X..No guarantees.]
+ }
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if directory deletion is constant time.}
+ \exceptionmodelstd
+ \qexample{filedir_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmdir(const std::vector<path_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Schedule a batch of asynchronous file creations and opens after optional preconditions.
+
+ \docs_file
+ \ntkernelnamespacenote
+
+ \return A batch of op handles.
+ \param reqs A batch of `path_req` structures.
+ \ingroup file
+ \qbk{distinguish, batch}
+ \raceguarantees{
+ [raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
+ [raceguarantee OS X..No guarantees.]
+ }
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if file creation is constant time.}
+ \exceptionmodelstd
+ \qexample{filedir_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> file(const std::vector<path_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Schedule a batch of asynchronous file deletions after optional preconditions.
+
+ \docs_rmfile
+ \ntkernelnamespacenote
+
+ \return A batch of op handles.
+ \param reqs A batch of `path_req` structures.
+ \ingroup rmfile
+ \qbk{distinguish, batch}
+ \raceguarantees{
+ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+ [raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+ [raceguarantee OS X..No guarantees.]
+ }
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if file deletion is constant time.}
+ \exceptionmodelstd
+ \qexample{filedir_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmfile(const std::vector<path_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Schedule a batch of asynchronous symlink creations and opens after a precondition.
+
+ \docs_symlink
+ \ntkernelnamespacenote
+
+ \return A batch of op handles.
+ \param reqs A batch of `path_req` structures.
+ \param targets An optional batch of targets if creating symlinks.
+ \ingroup symlink
+ \qbk{distinguish, batch}
+ \raceguarantees{
+ [raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
+ [raceguarantee OS X..No guarantees.]
+ }
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if symlink creation is constant time.}
+ \exceptionmodelstd
+ \qexample{filedir_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> symlink(const std::vector<path_req> &reqs, const std::vector<future<>> &targets=std::vector<future<>>()) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Schedule a batch of asynchronous symlink deletions after optional preconditions.
+
+ \docs_rmsymlink
+ \return A batch of op handles.
+ \param reqs A batch of `path_req` structures.
+ \ingroup rmsymlink
+ \qbk{distinguish, batch}
+ \raceguarantees{
+ [raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+ [raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+ [raceguarantee OS X..No guarantees.]
+ }
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if symlink deletion is constant time.}
+ \exceptionmodelstd
+ \qexample{filedir_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmsymlink(const std::vector<path_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Schedule a batch of asynchronous content synchronisations with physical storage after preceding operations.
+
+ \docs_sync
+
+ \return A batch of op handles.
+ \param ops A batch of op handles.
+ \ingroup sync
+ \qbk{distinguish, batch}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if content synchronisation is constant time (which is extremely unlikely).}
+ \exceptionmodelstd
+ \qexample{readwrite_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> sync(const std::vector<future<>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Schedule a batch of asynchronous zeroing and deallocations of physical storage ("hole punching") after preceding operations.
+
+ \docs_zero
+
+ \return A batch of op handles.
+ \param ops A batch of op handles.
+ \param ranges A batch of vectors of extents to zero and deallocate.
+ \ingroup zero
+ \qbk{distinguish, batch}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if deallocation is constant time.}
+ \exceptionmodelstd
+ \qexample{extents_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> zero(const std::vector<future<>> &ops, const std::vector<std::vector<std::pair<off_t, off_t>>> &ranges) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Schedule a batch of asynchronous file or directory handle closes after preceding operations.
+
+ \docs_close
+
+ \return A batch of op handles.
+ \param ops A batch of op handles.
+ \ingroup close
+ \qbk{distinguish, batch}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if closing handles is constant time.}
+ \exceptionmodelstd
+ \qexample{filedir_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> close(const std::vector<future<>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+
+ /*! \brief Schedule a batch of asynchronous data reads after preceding operations, where
+ offset and total data read must not exceed the present file size.
+
+ \docs_read
+ \direct_io_note
+
+ \return A batch of op handles.
+ \tparam "class T" Any type.
+ \param ops A batch of io_req<T> structures.
+ \ingroup read
+ \qbk{distinguish, batch}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if reading data is constant time.}
+ \exceptionmodelstd
+ \qexample{readwrite_example}
+ */
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> read(const std::vector<detail::io_req_impl<false>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ template<class T> inline std::vector<future<>> read(const std::vector<io_req<T>> &ops);
+#else
+ template<class T> BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> read(const std::vector<io_req<T>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+#endif
+ /*! \brief Schedule a batch of asynchronous data writes after preceding operations, where
+ offset and total data written must not exceed the present file size.
+
+ \docs_write
+ \direct_io_note
+
+ \return A batch of op handles.
+ \tparam "class T" Any type.
+ \param ops A batch of io_req<const T> structures.
+ \ingroup write
+ \qbk{distinguish, batch}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if writing data is constant time.}
+ \exceptionmodelstd
+ \qexample{readwrite_example}
+ */
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> write(const std::vector<detail::io_req_impl<true>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ template<class T> inline std::vector<future<>> write(const std::vector<io_req<T>> &ops);
+#else
+ template<class T> BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> write(const std::vector<io_req<const T>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+#endif
+
+ /*! \brief Schedule a batch of asynchronous file length truncations after preceding operations.
+
+ \docs_truncate
+
+ \return A batch of op handles.
+ \param ops A batch of op handles.
+ \param sizes A batch of new lengths.
+ \ingroup truncate
+ \qbk{distinguish, batch}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool) to complete if truncating file lengths is constant time.}
+ \exceptionmodelstd
+ \qexample{readwrite_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> truncate(const std::vector<future<>> &ops, const std::vector<off_t> &sizes) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+
+ /*! \brief Schedule a batch of asynchronous directory enumerations after preceding operations.
+
+ \docs_enumerate
+
+ \return A batch of stl_future vectors of directory entries with boolean returning false if done.
+ \param reqs A batch of enumeration requests.
+ \ingroup enumerate
+ \qbk{distinguish, batch}
+ \raceguarantees{
+ [raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+ many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+ comparing inodes for equivalence to a direntry() won't help you.]
+ [raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+ birthtim, sparse, compressed.]
+ }
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of entries in each directory.}
+ \exceptionmodelstd
+ \qexample{enumerate_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<std::pair<std::vector<directory_entry>, bool>>> enumerate(const std::vector<enumerate_req> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Schedule a batch of asynchronous extent enumerations after preceding operations.
+
+ \docs_extents
+
+ \return A batch of stl_future vectors of extents.
+ \param ops A batch of op handles.
+ \ingroup extents
+ \qbk{distinguish, batch}
+ \raceguarantees{
+ [raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
+ before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
+ into single extents.]
+ [raceguarantee Windows..Race free.]
+ }
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of extents in each file.}
+ \exceptionmodelstd
+ \qexample{extents_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<std::vector<std::pair<off_t, off_t>>>> extents(const std::vector<future<>> &ops) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+ /*! \brief Schedule a batch of asynchronous volume enumerations after preceding operations.
+
+ \docs_statfs
+
+ \return A batch of stl_future volume metadatas.
+ \param ops A batch of op handles.
+ \param reqs A batch of metadata requests.
+ \ingroup statfs
+ \qbk{distinguish, batch}
+ \raceguarantees{
+ [raceguarantee FreeBSD, OS X..Race free.]
+ [raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
+ flags.rdonly, flags.noexec, flags.nosuid.]
+ [raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
+ is fetched separately.]
+ }
+ \complexity{Amortised O(N) to dispatch. Amortised O(N/threadpool*M) to complete where M is the average number of entries in each directory.}
+ \exceptionmodelstd
+ \qexample{statfs_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<statfs_t>> statfs(const std::vector<future<>> &ops, const std::vector<fs_metadata_flags> &reqs) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+ inline future<> adopt(handle_ptr h);
+ inline future<> dir(const path_req &req);
+ inline future<> rmdir(const path_req &req);
+ inline future<> file(const path_req &req);
+ inline future<> rmfile(const path_req &req);
+ inline future<> symlink(const path_req &req, const future<> &target=future<>());
+ inline future<> rmsymlink(const path_req &req);
+ inline future<> sync(const future<> &req);
+ inline future<> zero(const future<> &req, const std::vector<std::pair<off_t, off_t>> &ranges);
+ inline future<> close(const future<> &req);
+ inline future<> read(const detail::io_req_impl<false> &req);
+ inline future<> write(const detail::io_req_impl<true> &req);
+ inline future<> truncate(const future<> &op, off_t newsize);
+ inline future<std::pair<std::vector<directory_entry>, bool>> enumerate(const enumerate_req &req);
+ inline future<std::vector<std::pair<off_t, off_t>>> extents(const future<> &op);
+ inline future<statfs_t> statfs(const future<> &op, const fs_metadata_flags &req);
+
+ // Undocumented deliberately
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> lock(const std::vector<lock_req> &req) BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC
+
+
+ /*! \brief Schedule an asynchronous synchronisation of preceding operations.
+ \deprecate{This function will be eliminated after lightweight future-promises are merged as one simply calls when_all_p() on the futures.}
+ If you perform many asynchronous operations of unequal duration but wish to schedule one of more operations
+ to occur only after \b all of those operations have completed, this is the correct function to use. The returned
+ batch of ops exactly match the input batch of ops (including their exception states), but they will only
+ complete when the last of the input batch of ops completes.
+
+ \note If an input op is in an exceptioned state at the point of entry into this function, this function
+ will propagate the exception there and then. \em Only error states which occur \em after this function
+ has been scheduled are propagated into the output set of ops.
+
+ \return A batch of op handles.
+ \param ops A batch of op handles.
+ \ingroup dispatcher__barrier
+ \qbk{distinguish, batch}
+ \complexity{Amortised O(N) to dispatch. Amortised O(N) to complete.}
+ \exceptionmodel{See detailed description above.}
+ \qexample{barrier_example}
+ */
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> barrier(const std::vector<future<>> &ops);
+#endif
+
+ /*! \brief Schedule the return of an op handle after another op handle completes. This is useful when you
+ need to supply one op handle to a function but it must not begin until another op handle has finished.
+
+ \return The op handle op.
+ \param precondition The op handle which must complete for op to be passed through.
+ \param op The op handle to return.
+ \ingroup dispatcher__depends
+ \complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
+ \exceptionmodelstd
+ \qexample{filecopy_example}
+ */
+ inline future<> depends(future<> precondition, future<> op);
+
+ /*! \brief Completes an operation with a handle or an error, usually used when an operation was previously deferred.
+
+ \ingroup dispatcher__misc
+ \qbk{distinguish, normal}
+ \complexity{O(N) where N is the number of completions dependent on this op.}
+ \exceptionmodel{Should not throw any exception except for out of memory.}
+ */
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void complete_async_op(size_t id, handle_ptr h, exception_ptr e=exception_ptr());
+ /*! \brief Completes an operation with an error, usually used when an operation was previously deferred.
+
+ \ingroup dispatcher__misc
+ \qbk{distinguish, errored}
+ \complexity{O(N) where N is the number of completions dependent on this op.}
+ \exceptionmodel{Should not throw any exception except for out of memory.}
+ */
+ void complete_async_op(size_t id, exception_ptr e) { complete_async_op(id, handle_ptr(), e); }
+protected:
+ template<class F> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC handle_ptr int_get_handle_to_containing_dir(F *parent, size_t id, path_req req, completion_returntype(F::*dofile)(size_t, future<>, path_req));
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC completion_returntype invoke_user_completion_fast(size_t id, future<> h, completion_t *callback);
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC completion_returntype invoke_user_completion_slow(size_t id, future<> h, std::function<completion_t> callback);
+
+ template<class F> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> chain_async_ops(int optype, const std::vector<future<>> &preconditions, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, future<>));
+ template<class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> chain_async_ops(int optype, const std::vector<future<>> &preconditions, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T));
+ template<class R, class F> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<R>> chain_async_ops(int optype, const std::vector<future<>> &preconditions, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, std::shared_ptr<promise<R>>));
+ template<class R, class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<R>> chain_async_ops(int optype, const std::vector<future<>> &preconditions, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T, std::shared_ptr<promise<R>>));
+ template<class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> chain_async_ops(int optype, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T));
+ template<class R, class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<R>> chain_async_ops(int optype, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T, std::shared_ptr<promise<R>>));
+
+ template<class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher::completion_returntype dobarrier(size_t id, future<> h, T);
+ template<class F, class... Args> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC handle_ptr invoke_async_op_completions(size_t id, future<> h, completion_returntype(F::*f)(size_t, future<>, Args...), Args... args);
+ template<class F, class... Args> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> chain_async_op(detail::immediate_async_ops &immediates, int optype, const future<> &precondition, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, Args...), Args... args);
+};
+/*! \brief Instatiates the best available async_file_io_dispatcher implementation for this system for the given uri.
+
+Note that the number of threads in the threadpool supplied is the maximum non-async op queue depth (e.g. file opens, closes etc.).
+For fast SSDs, there isn't much gain after eight-sixteen threads, so the process threadpool is set to eight by default.
+For slow hard drives, or worse, SANs, a queue depth of 64 or higher might deliver significant benefits.
+
+URIs currently supported by AFIO:
+- <b>`__fileurl__`</b> The dispatcher will refer to the local filesystem of this machine.
+
+\return A shared_ptr to the best available async_file_io_dispatcher implementation for this system for the given uri.
+\param uri Where to open the dispatcher upon.
+\param flagsforce The flags to bitwise OR with any opened file flags. Used to force on certain flags.
+\param flagsmask The flags to bitwise AND with any opened file flags. Used to force off certain flags.
+\param threadpool The threadpool instance to use for asynchronous dispatch.
+\ingroup async_file_io_dispatcher
+\qbk{
+[heading Example]
+[call_example]
+}
+*/
+#ifdef DOXYGEN_SHOULD_SKIP_THIS
+BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<dispatcher_ptr> make_dispatcher(std::string uri="file : / / /", file_flags flagsforce = file_flags::none, file_flags flagsmask = file_flags::none, std::shared_ptr<thread_source> threadpool = process_threadpool()) noexcept;
+#else
+BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<dispatcher_ptr> make_dispatcher(std::string uri="file:///", file_flags flagsforce=file_flags::none, file_flags flagsmask=file_flags::none, std::shared_ptr<thread_source> threadpool = process_threadpool()) noexcept;
+#endif
+
+namespace detail
+{
+ struct when_all_state : std::enable_shared_from_this<when_all_state>
+ {
+ promise<std::vector<handle_ptr>> out;
+ std::vector<shared_future<handle_ptr>> in;
+ };
+ template<bool rethrow> inline void when_all_ops_do(std::shared_ptr<when_all_state> state)
+ {
+ // If we're on Boost.Thread, coalesce all wait ops into a single
+#if BOOST_AFIO_USE_BOOST_THREAD
+ boost::wait_for_all(state->in.begin(), state->in.end());
+#endif
+ std::vector<handle_ptr> ret;
+ ret.reserve(state->in.size());
+ for(auto &i: state->in)
+ {
+ auto e(get_exception_ptr(i));
+ if(e)
+ {
+ if(rethrow)
+ {
+ state->out.set_exception(e);
+ return;
+ }
+ ret.push_back(handle_ptr());
+ }
+ else
+ ret.push_back(i.get());
+ }
+ state->out.set_value(ret);
+ }
+ template<bool rethrow, class Iterator> inline stl_future<std::vector<handle_ptr>> when_all_ops(Iterator first, Iterator last)
+ {
+ auto state=std::make_shared<when_all_state>();
+ state->in.reserve(std::distance(first, last));
+ for(; first!=last; ++first)
+ state->in.push_back(first->_h);
+ auto ret=state->out.get_future();
+ process_threadpool()->enqueue([BOOST_AFIO_LAMBDA_MOVE_CAPTURE(state)] { when_all_ops_do<rethrow>(std::move(state)); });
+ return ret;
+ }
+ struct when_any_state : std::enable_shared_from_this<when_any_state>
+ {
+ atomic<size_t> count;
+ promise<handle_ptr> out;
+ std::vector<shared_future<handle_ptr>> in;
+ when_any_state() : count(0) { }
+ };
+#if BOOST_AFIO_USE_BOOST_THREAD
+ // Boost.Thread has wait_for_any() which lets us be more efficient here and wait directly on the futures
+ template<bool rethrow> inline void when_any_ops_do(std::shared_ptr<when_any_state> state)
+ {
+ auto &i=*boost::wait_for_any(state->in.begin(), state->in.end());
+ auto e(get_exception_ptr(i));
+ if(e)
+ {
+ if(rethrow)
+ {
+ state->out.set_exception(e);
+ return;
+ }
+ state->out.set_value(handle_ptr());
+ }
+ else
+ state->out.set_value(i.get());
+ }
+ template<bool rethrow, class Iterator> inline stl_future<handle_ptr> when_any_ops(Iterator first, Iterator last)
+ {
+ auto state=std::make_shared<when_any_state>();
+ state->in.reserve(std::distance(first, last));
+ for(; first!=last; ++first)
+ state->in.push_back(first->h);
+ auto ret=state->out.get_future();
+ process_threadpool()->enqueue([BOOST_AFIO_LAMBDA_MOVE_CAPTURE(state)]{ when_any_ops_do<rethrow>(std::move(state)); });
+ return ret;
+ }
+#else
+ // Without wait_for_any, schedule a completion onto every op and the first to fire wins
+ template<bool rethrow> inline std::pair<bool, handle_ptr> when_any_ops_do(std::shared_ptr<when_any_state> state, size_t idx, size_t id, future<> h)
+ {
+ auto &i=state->in[idx];
+ if(0==state->count.fetch_add(1, memory_order_relaxed)) // Will be zero exactly once
+ {
+ auto e(get_exception_ptr(i));
+ if(e)
+ {
+ if(rethrow)
+ {
+ state->out.set_exception(e);
+ return std::make_pair(true, handle_ptr());
+ }
+ state->out.set_value(handle_ptr());
+ }
+ else
+ state->out.set_value(i.get());
+ }
+ return std::make_pair(true, handle_ptr());
+ }
+ template<bool rethrow, class Iterator> inline stl_future<handle_ptr> when_any_ops(Iterator first, Iterator last)
+ {
+ auto state=std::make_shared<when_any_state>();
+ auto dispatcher=first->parent();
+ std::vector<future<>> ops(first, last);
+ state->in.reserve(ops.size());
+ for(auto &op : ops)
+ state->in.push_back(op._h);
+ auto ret=state->out.get_future();
+ typedef std::function<typename dispatcher::completion_t> ft;
+ std::vector<std::pair<async_op_flags, ft>> completions;
+ completions.reserve(ops.size());
+ for(size_t n=0; n<ops.size(); n++)
+ completions.push_back(std::make_pair(async_op_flags::immediate, std::bind(&when_any_ops_do<rethrow>, state, n, std::placeholders::_1, std::placeholders::_2)));
+ dispatcher->completion(ops, completions);
+ return ret;
+ }
+#endif
+ template<bool is_all> struct select_when_ops_return_type
+ {
+ typedef stl_future<std::vector<handle_ptr>> type; // when_all_p()
+ };
+ template<> struct select_when_ops_return_type<false>
+ {
+ typedef stl_future<handle_ptr> type; // when_any()
+ };
+ template<bool is_all, class T> struct enable_if_async_op
+ {
+ //static_assert(std::is_same<T, T>::value, "Not an iterator of future<>");
+ };
+ template<bool is_all, class T> struct enable_if_async_op<is_all, future<T>>
+ {
+ typedef typename select_when_ops_return_type<is_all>::type type;
+ };
+}
+
+/*! \brief Returns a result when all the supplied ops complete. Does not propagate exception states.
+
+\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
+\return A stl_future vector of shared_ptr's to handle.
+\tparam "class Iterator" An iterator type.
+\param _ An instance of std::nothrow_t.
+\param first An iterator pointing to the first future<> to wait upon.
+\param last An iterator pointing after the last future<> to wait upon.
+\ingroup when_all_ops
+\qbk{distinguish, iterator batch of ops not exception propagating}
+\complexity{O(N).}
+\exceptionmodel{Non propagating}
+*/
+template<class Iterator> inline typename detail::enable_if_async_op<true, typename Iterator::value_type>::type when_all_p(std::nothrow_t _, Iterator first, Iterator last)
+{
+ if(first==last)
+ return stl_future<std::vector<handle_ptr>>();
+ return detail::when_all_ops<false>(first, last);
+}
+/*! \brief Returns a result when any the supplied ops complete. Does not propagate exception states.
+
+\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
+\return A stl_future vector of shared_ptr's to handle.
+\tparam "class Iterator" An iterator type.
+\param _ An instance of std::nothrow_t.
+\param first An iterator pointing to the first future<> to wait upon.
+\param last An iterator pointing after the last future<> to wait upon.
+\ingroup when_all_ops
+\qbk{distinguish, iterator batch of ops not exception propagating}
+\complexity{O(N).}
+\exceptionmodel{Non propagating}
+*/
+template<class Iterator> inline typename detail::enable_if_async_op<false, typename Iterator::value_type>::type when_any(std::nothrow_t _, Iterator first, Iterator last)
+{
+ if(first==last)
+ return stl_future<handle_ptr>();
+ return detail::when_any_ops<false>(first, last);
+}
+/*! \brief Returns a result when all the supplied ops complete. Does not propagate exception states.
+
+\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
+\return A stl_future vector of shared_ptr's to handle.
+\param _ An instance of std::nothrow_t.
+\param ops A vector of the async_io_ops to wait upon.
+\ingroup when_all_ops
+\qbk{distinguish, vector batch of ops not exception propagating}
+\complexity{O(N).}
+\exceptionmodel{Non propagating}
+*/
+template<class T> inline stl_future<std::vector<handle_ptr>> when_all_p(std::nothrow_t _, std::vector<future<T>> ops)
+{
+ if(ops.empty())
+ return stl_future<std::vector<handle_ptr>>();
+ return detail::when_all_ops<false>(ops.begin(), ops.end());
+}
+/*! \brief Returns a result when any the supplied ops complete. Does not propagate exception states.
+
+\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
+\return A stl_future vector of shared_ptr's to handle.
+\param _ An instance of std::nothrow_t.
+\param ops A vector of the async_io_ops to wait upon.
+\ingroup when_all_ops
+\qbk{distinguish, vector batch of ops not exception propagating}
+\complexity{O(N).}
+\exceptionmodel{Non propagating}
+*/
+template<class T> inline stl_future<handle_ptr> when_any(std::nothrow_t _, std::vector<future<T>> ops)
+{
+ if(ops.empty())
+ return stl_future<handle_ptr>();
+ return detail::when_any_ops<false>(ops.begin(), ops.end());
+}
+/*! \brief Returns a result when all the supplied ops complete. Propagates exception states.
+
+\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
+\return A stl_future vector of shared_ptr's to handle.
+\tparam "class Iterator" An iterator type.
+\param first An iterator pointing to the first future<> to wait upon.
+\param last An iterator pointing after the last future<> to wait upon.
+\ingroup when_all_ops
+\qbk{distinguish, iterator batch of ops exception propagating}
+\complexity{O(N).}
+\exceptionmodel{Propagating}
+*/
+template<class Iterator> inline typename detail::enable_if_async_op<true, typename Iterator::value_type>::type when_all_p(Iterator first, Iterator last)
+{
+ if(first==last)
+ return stl_future<std::vector<handle_ptr>>();
+ return detail::when_all_ops<true>(first, last);
+}
+/*! \brief Returns a result when any the supplied ops complete. Propagates exception states.
+
+\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
+\return A stl_future vector of shared_ptr's to handle.
+\tparam "class Iterator" An iterator type.
+\param first An iterator pointing to the first future<> to wait upon.
+\param last An iterator pointing after the last future<> to wait upon.
+\ingroup when_all_ops
+\qbk{distinguish, iterator batch of ops exception propagating}
+\complexity{O(N).}
+\exceptionmodel{Propagating}
+*/
+template<class Iterator> inline typename detail::enable_if_async_op<false, typename Iterator::value_type>::type when_any(Iterator first, Iterator last)
+{
+ if(first==last)
+ return stl_future<handle_ptr>();
+ return detail::when_any_ops<true>(first, last);
+}
+/*! \brief Returns a result when all the supplied ops complete. Propagates exception states.
+
+\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
+\return A stl_future vector of shared_ptr's to handle.
+\param ops A vector of the async_io_ops to wait upon.
+\ingroup when_all_ops
+\qbk{distinguish, vector batch of ops exception propagating}
+\complexity{O(N).}
+\exceptionmodel{Propagating}
+*/
+template<class T> inline stl_future<std::vector<handle_ptr>> when_all_p(std::vector<future<T>> ops)
+{
+ if(ops.empty())
+ return stl_future<std::vector<handle_ptr>>();
+ return detail::when_all_ops<true>(ops.begin(), ops.end());
+}
+/*! \brief Returns a result when any the supplied ops complete. Propagates exception states.
+
+\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
+\return A stl_future vector of shared_ptr's to handle.
+\param ops A vector of the async_io_ops to wait upon.
+\ingroup when_all_ops
+\qbk{distinguish, vector batch of ops exception propagating}
+\complexity{O(N).}
+\exceptionmodel{Propagating}
+*/
+template<class T> inline stl_future<handle_ptr> when_any(std::vector<future<T>> ops)
+{
+ if(ops.empty())
+ return stl_future<handle_ptr>();
+ return detail::when_any_ops<true>(ops.begin(), ops.end());
+}
+/*! \brief Returns a result when the supplied op completes. Does not propagate exception states.
+
+\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
+\return A stl_future vector of shared_ptr's to handle.
+\param _ An instance of std::nothrow_t.
+\param op An future<> to wait upon.
+\ingroup when_all_ops
+\qbk{distinguish, convenience single op not exception propagating}
+\complexity{O(1).}
+\exceptionmodel{Non propagating}
+*/
+template<class T> inline stl_future<std::vector<handle_ptr>> when_all_p(std::nothrow_t _, future<T> op)
+{
+ std::vector<future<T>> ops(1, op);
+ return when_all_p(_, ops);
+}
+/*! \brief Returns a result when the supplied op completes. Propagates exception states.
+
+\deprecate{This will be replaced with the latest Concurrency TS specification (which has changed since AFIO was first designed).}
+\return A stl_future vector of shared_ptr's to handle.
+\param ops A sequence of future<> to wait upon.
+\ingroup when_all_ops
+\qbk{distinguish, convenience multiple op exception propagating}
+\complexity{O(N).}
+\exceptionmodel{Propagating}
+*/
+template<class... Types> inline stl_future<std::vector<handle_ptr>> when_all_p(future<Types> &... ops)
+{
+ std::vector<future<>> _ops = { std::forward<future<Types> &>(ops)... };
+ return when_all_p(_ops);
+}
+
+/*! \struct path_req
+\brief A convenience bundle of path and flags, with optional precondition. Paths may be a path fragment (relative to the precondition) or absolute, in which case
+if necessary they are made canonical and absolute in the constructor according to the current working directory.
+
+\qbk{
+[include generated/struct_path_req_1_1absolute.qbk]
+[include generated/struct_path_req_1_1relative.qbk]
+}
+*/
+struct path_req
+{
+ bool is_relative; //!< Whether the precondition is also where this path begins
+ BOOST_AFIO_V2_NAMESPACE::path path; //!< The filing system path to be used for this operation
+ file_flags flags; //!< The flags to be used for this operation (note they can be overriden by flags passed during dispatcher construction).
+ future<> precondition; //!< An optional precondition for this operation
+ //! \brief Tags the path as being absolute
+ struct absolute;
+ //! \brief Tags the path as being relative
+ struct relative;
+ //! \constr
+ path_req() : is_relative(false), flags(file_flags::none) { }
+ //! \cconstr
+ path_req(const path_req &o) = default;
+ //! \mconstr
+ path_req(path_req &&o) noexcept : is_relative(o.is_relative), path(std::move(o.path)), flags(std::move(o.flags)), precondition(std::move(o.precondition)) { }
+ //! \mconstr
+ inline path_req(absolute &&o);
+ //! \mconstr
+ inline path_req(relative &&o);
+ /*! \brief Constructs an instance.
+
+ \tparam "class T" The type of path to be used.
+ \param _path The filing system path to be used.
+ \param _flags The flags to be used.
+ */
+
+ template<class T, typename=typename std::enable_if<!std::is_constructible<path_req, T>::value && !std::is_constructible<future<>, T>::value>::type> path_req(T &&_path, file_flags _flags=file_flags::none) : is_relative(false), path(BOOST_AFIO_V2_NAMESPACE::path::make_absolute(std::forward<T>(_path))), flags(_flags) { }
+ /*! \brief Constructs an instance.
+
+ \tparam "class T" The type of path to be used.
+ \param _is_relative Whether the precondition is where the path begins
+ \param _precondition The precondition for this operation.
+ \param _path The filing system path to be used.
+ \param _flags The flags to be used.
+ */
+ template<class T, typename=typename std::enable_if<!std::is_convertible<BOOST_AFIO_V2_NAMESPACE::path, T>::value>::type> path_req(bool _is_relative, future<> _precondition, T &&_path, file_flags _flags=file_flags::none) : is_relative(_is_relative), path(_is_relative ? BOOST_AFIO_V2_NAMESPACE::path(std::forward<T>(_path)) : BOOST_AFIO_V2_NAMESPACE::path(BOOST_AFIO_V2_NAMESPACE::path::make_absolute(std::forward<T>(_path)))), flags(_flags), precondition(std::move(_precondition)) { _validate(); }
+ //! \overload
+ path_req(bool _is_relative, future<> _precondition, BOOST_AFIO_V2_NAMESPACE::path _path, file_flags _flags=file_flags::none) : is_relative(_is_relative), path(std::move(_path)), flags(_flags), precondition(std::move(_precondition)) { _validate(); }
+ /*! \brief Constructs an instance.
+
+ \param _precondition The precondition for this operation (used as the path).
+ \param _flags The flags to be used.
+ */
+ path_req(future<> _precondition, file_flags _flags=file_flags::none) : is_relative(true), flags(_flags), precondition(std::move(_precondition)) { _validate(); }
+ //! Validates contents
+ bool validate() const
+ {
+ if(!is_relative && path.empty()) return false;
+ return !precondition.valid() || precondition.validate();
+ }
+protected:
+ void _validate() const
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if(!validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ }
+};
+//! Convenience tag type constructing a relative path path_req
+struct path_req::relative : path_req
+{
+ /*! \brief Constructs an instance.
+
+ \tparam "class T" The type of path to be used.
+ \param _precondition The precondition for this operation.
+ \param _path The filing system path to be used.
+ \param _flags The flags to be used.
+ */
+ template<class T> relative(future<> _precondition, T &&_path, file_flags _flags=file_flags::none) : path_req(true, std::move(_precondition), std::forward<T>(_path), _flags) { _validate(); }
+ /*! \brief Constructs an instance.
+
+ \param _precondition The precondition for this operation.
+ \param _flags The flags to be used.
+ */
+ relative(future<> _precondition, file_flags _flags=file_flags::none) : path_req(std::move(_precondition), _flags) { _validate(); }
+};
+//! Convenience tag type constructing an absolute path path_req
+struct path_req::absolute : path_req
+{
+ /*! \brief Constructs an instance.
+
+ \tparam "class T" The type of path to be used.
+ \param _precondition The precondition for this operation.
+ \param _path The filing system path to be used.
+ \param _flags The flags to be used.
+ */
+ template<class T> absolute(future<> _precondition, T &&_path, file_flags _flags=file_flags::none) : path_req(false, std::move(_precondition), std::move(BOOST_AFIO_V2_NAMESPACE::path::make_absolute(std::forward<T>(_path))), _flags) { _validate(); }
+};
+inline path_req::path_req(path_req::absolute &&o) : is_relative(o.is_relative), path(std::move(o.path)), flags(std::move(o.flags)), precondition(std::move(o.precondition)) { }
+inline path_req::path_req(path_req::relative &&o) : is_relative(o.is_relative), path(std::move(o.path)), flags(std::move(o.flags)), precondition(std::move(o.precondition)) { }
+
+/*! \defgroup to_asio_buffers Overloadable free functions converting the types passed to io_req<> into an asio buffer sequence for read() and write().
+
+You can add your own free function overloads to tell AFIO how to convert your custom types into ASIO scatter gather buffers.
+Note that const types must convert into asio::const_buffer, and non-const types must convert into asio::mutable_buffer. It
+is entirely acceptable for types to allow writing (const) only and not reading.
+
+Default overloads provided are as follows:
+ - Any trivial type T * and number of items.
+ - void * and const void * with number of bytes.
+ - C array types are treated as if a std::array.
+ - asio::const_buffer and asio::mutable_buffer are passed through as-is.
+ - Any container type holding a trivial type T. This includes std::basic_string (write only), std::vector and std::array,
+ all three of which are specially collapsed into a single scatter gather as they guarantee storing their
+ contents contiguously.
+ - Any container type holding any of the above, including other containers. These will be converted into
+ scatter gather lists for you. Note that the constness of the type returned by the container's iterator is respected,
+ so if the container iterator returns a const reference (e.g. std::basic_string) then you cannot gather read into
+ that container, and instead should receive a compile time error.
+*/
+template<class T> inline std::vector<asio::mutable_buffer> to_asio_buffers(T &v);
+template<class T> inline std::vector<asio::const_buffer> to_asio_buffers(const T &v);
+template<class T, size_t N> inline std::vector<asio::mutable_buffer> to_asio_buffers(T (&v)[N]);
+template<class T, size_t N> inline std::vector<asio::const_buffer> to_asio_buffers(const T (&v)[N]);
+/*! \brief Passing through asio::mutable_buffer
+
+\return A vector of ASIO buffers
+\ingroup to_asio_buffers
+\qbk{distinguish, asio mutable_buffer}
+*/
+inline std::vector<asio::mutable_buffer> to_asio_buffers(asio::mutable_buffer &v)
+{
+ return std::vector<asio::mutable_buffer>(1, v);
+}
+/*! \brief Passing through asio::const_buffer
+
+\return A vector of ASIO buffers
+\ingroup to_asio_buffers
+\qbk{distinguish, asio const_buffer}
+*/
+inline std::vector<asio::const_buffer> to_asio_buffers(asio::const_buffer &v)
+{
+ return std::vector<asio::const_buffer>(1, v);
+}
+/*! \brief A buffer at v sized length*sizeof(T)
+
+\tparam "class T" Any trivial type T
+\return A vector of ASIO buffers
+\ingroup to_asio_buffers
+\qbk{distinguish, buffer of T}
+*/
+template<class T> inline std::vector<asio::mutable_buffer> to_asio_buffers(T *v, size_t length)
+{
+ static_assert(std::is_trivial<T>::value, "to_asio_buffers<T> has not been specialised for this non-trivial type, which suggests you are trying to read or write a complex C++ type! Either add a custom specialisation, or directly instantiate an io_req with a void * and size_t length to some serialised representation.");
+ return std::vector<asio::mutable_buffer>(1, asio::mutable_buffer((void *) v, length*sizeof(T)));
+}
+/*! \brief A buffer at v sized length*sizeof(T)
+
+\tparam "class T" Any trivial type T
+\return A vector of ASIO buffers
+\ingroup to_asio_buffers
+\qbk{distinguish, const buffer of T}
+*/
+template<class T> inline std::vector<asio::const_buffer> to_asio_buffers(const T *v, size_t length)
+{
+ static_assert(std::is_trivial<T>::value, "to_asio_buffers<T> has not been specialised for this non-trivial type, which suggests you are trying to read or write a complex C++ type! Either add a custom specialisation, or directly instantiate an io_req with a void * and size_t length to some serialised representation.");
+ return std::vector<asio::const_buffer>(1, asio::const_buffer((void *) v, length*sizeof(T)));
+}
+/*! \brief A buffer at v sized length
+
+\return A vector of ASIO buffers
+\ingroup to_asio_buffers
+\qbk{distinguish, buffer}
+*/
+inline std::vector<asio::mutable_buffer> to_asio_buffers(void *v, size_t length)
+{
+ return std::vector<asio::mutable_buffer>(1, asio::mutable_buffer(v, length));
+}
+/*! \brief A buffer at v sized length
+
+\return A vector of ASIO buffers
+\ingroup to_asio_buffers
+\qbk{distinguish, const buffer of T}
+*/
+inline std::vector<asio::const_buffer> to_asio_buffers(const void *v, size_t length)
+{
+ return std::vector<asio::const_buffer>(1, asio::const_buffer(v, length));
+}
+namespace detail
+{
+ // Length deducing asio buffer conversions
+ template<bool is_const, class R, class T, bool is_trivial=std::is_trivial<T>::value, bool is_container=is_container<T>::value> struct to_asio_buffers_helper
+ {
+ template<class U> std::vector<R> operator()(U &v) const
+ {
+ static_assert(!std::is_same<T, T>::value, "to_asio_buffers(T) called with type T which is neither trivial nor a container. Did you mean to call io_req with a void * and a byte length, or do you need to overload to_asio_buffers()?");
+ static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This type is const, so you cannot generate an asio::mutable_buffer from it.");
+ return std::vector<R>();
+ }
+ };
+ // Trivial types get sent as is
+ template<bool is_const, class R, class T> struct to_asio_buffers_helper<is_const, R, T, true, false>
+ {
+ template<class U> std::vector<R> operator()(U &v) const
+ {
+ static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This type is const, so you cannot generate an asio::mutable_buffer from it.");
+ return std::vector<R>(1, R(&v, sizeof(v)));
+ }
+ };
+
+ // Container types build a scatter gather list of their contents
+ template<class R, class C, class T, bool is_const=std::is_const<T>::value, bool is_trivial=std::is_trivial<T>::value> struct container_to_asio_buffers_helper
+ {
+ template<class U> std::vector<R> operator()(U &v) const
+ {
+ static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it.");
+ std::vector<R> ret;
+ for(auto &i : v)
+ {
+ std::vector<R> item(to_asio_buffers(i));
+ ret.reserve(ret.size()+item.size());
+ ret.insert(ret.end(), std::make_move_iterator(item.begin()), std::make_move_iterator(item.end()));
+ }
+ return ret;
+ }
+ };
+ // Container specialisations where we know we can skip scatter gather
+ template<class R, class C, class T, class A, class _Ct, bool is_const> struct container_to_asio_buffers_helper<R, std::basic_string<C, T, A>, _Ct, is_const, true>
+ {
+ template<class U> std::vector<R> operator()(U &v) const
+ {
+ static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it.");
+ return std::vector<R>(1, R(&v.front(), v.size()*sizeof(C)));
+ }
+ };
+ template<class R, class T, class A, class _T, bool is_const> struct container_to_asio_buffers_helper<R, std::vector<T, A>, _T, is_const, true>
+ {
+ template<class U> std::vector<R> operator()(U &v) const
+ {
+ static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it.");
+ return std::vector<R>(1, R(v.data(), v.size()*sizeof(T)));
+ }
+ };
+ template<class R, class T, size_t N, class _T, bool is_const> struct container_to_asio_buffers_helper<R, std::array<T, N>, _T, is_const, true>
+ {
+ template<class U> std::vector<R> operator()(U &v) const
+ {
+ static_assert(!std::is_same<asio::mutable_buffer, R>::value || !is_const, "This container only permits const access to its iterators, so you cannot generate an asio::mutable_buffer from it.");
+ std::vector<R> ret(1, R(v.data(), v.size()*sizeof(T)));
+ return ret;
+ }
+ };
+ template<bool is_const, class R, class T, bool is_trivial> struct to_asio_buffers_helper<is_const, R, T, is_trivial, true> : container_to_asio_buffers_helper<R, T, typename is_container<T>::type>
+ {
+ };
+ // Pass through vectors and arrays of asio buffers
+ template<bool is_const, class R> struct to_asio_buffers_helper<is_const, R, std::vector<asio::mutable_buffer>, false, true>
+ {
+ template<class U> std::vector<R> operator()(U &v) const
+ {
+ std::vector<R> ret(v.begin(), v.end());
+ return ret;
+ }
+ };
+ template<bool is_const> struct to_asio_buffers_helper<is_const, asio::mutable_buffer, std::vector<asio::mutable_buffer>, false, true>
+ {
+ template<class U> std::vector<asio::mutable_buffer> operator()(U &v) const
+ {
+ return v;
+ }
+ };
+ template<bool is_const, class R> struct to_asio_buffers_helper<is_const, R, std::vector<asio::const_buffer>, false, true>
+ {
+ template<class U> std::vector<R> operator()(U &v) const
+ {
+ std::vector<R> ret(v.begin(), v.end());
+ return ret;
+ }
+ };
+ template<bool is_const> struct to_asio_buffers_helper<is_const, asio::const_buffer, std::vector<asio::const_buffer>, false, true>
+ {
+ template<class U> std::vector<asio::const_buffer> operator()(U &v) const
+ {
+ return v;
+ }
+ };
+ template<bool is_const, size_t N, class R> struct to_asio_buffers_helper<is_const, R, std::array<asio::mutable_buffer, N>, false, true>
+ {
+ template<class U> std::vector<R> operator()(U &v) const
+ {
+ std::vector<R> ret(v.begin(), v.end());
+ return ret;
+ }
+ };
+ template<bool is_const, size_t N, class R> struct to_asio_buffers_helper<is_const, R, std::array<asio::const_buffer, N>, false, true>
+ {
+ template<class U> std::vector<R> operator()(U &v) const
+ {
+ std::vector<R> ret(v.begin(), v.end());
+ return ret;
+ }
+ };
+}
+/*! \brief Any trivial type T or STL container.
+
+Trivial types turn into a buffer of &v sized sizeof(T).
+Container types have their value type deduced and to_asio_buffers() called on that value_type.
+Additional specialisations are provided for string, vector and array to collapse the scatter
+gather buffers into a single one for contiguous storage.
+
+\tparam "class T" Any trivial type T or STL container
+\return A vector of ASIO buffers
+\ingroup to_asio_buffers
+\qbk{distinguish, trivial and container types}
+*/
+template<class T> inline std::vector<asio::mutable_buffer> to_asio_buffers(T &v)
+{
+ static_assert(!std::is_pointer<T>::value, "You cannot assemble scatter gather buffers from raw pointers, you need to specify a length or supply a type carrying a length");
+ return detail::to_asio_buffers_helper<false, asio::mutable_buffer, T>()(v);
+}
+/*! \brief Any trivial type T or STL container.
+
+Trivial types turn into a buffer of &v sized sizeof(T).
+Container types have their value type deduced and to_asio_buffers() called on that value_type.
+Additional specialisations are provided for string, vector and array to collapse the scatter
+gather buffers into a single one for contiguous storage.
+
+\tparam "class T" Any trivial type T or STL container
+\return A vector of ASIO buffers
+\ingroup to_asio_buffers
+\qbk{distinguish, const trivial and container types}
+*/
+template<class T> inline std::vector<asio::const_buffer> to_asio_buffers(const T &v)
+{
+ static_assert(!std::is_pointer<T>::value, "You cannot assemble scatter gather buffers from raw pointers, you need to specify a length or supply a type carrying a length");
+ return detail::to_asio_buffers_helper<true, asio::const_buffer, T>()(v);
+}
+/*! \brief A buffer at v sized N*sizeof(T)
+
+\tparam "class T" Any trivial type T
+\return A vector of ASIO buffers
+\ingroup to_asio_buffers
+\qbk{distinguish, C arrays}
+*/
+template<class T, size_t N> inline std::vector<asio::mutable_buffer> to_asio_buffers(T (&v)[N])
+{
+ return to_asio_buffers(reinterpret_cast<std::array<T, N> &>(v));
+}
+/*! \brief A buffer at v sized N*sizeof(T)
+
+\tparam "class T" Any trivial type T
+\return A vector of ASIO buffers
+\ingroup to_asio_buffers
+\qbk{distinguish, const C arrays}
+*/
+template<class T, size_t N> inline std::vector<asio::const_buffer> to_asio_buffers(const T (&v)[N])
+{
+ return to_asio_buffers(reinterpret_cast<const std::array<T, N> &>(v));
+}
+
+namespace detail
+{
+ //! \brief The implementation of all io_req specialisations. \tparam for_writing Whether this implementation is for writing data. \ingroup io_req
+ template<bool for_writing> class io_req_impl;
+ template<> class io_req_impl<false>
+ {
+ public:
+ //! An optional precondition for this operation
+ future<> precondition;
+ //! A sequence of mutable Boost.ASIO buffers to read into
+ std::vector<asio::mutable_buffer> buffers;
+ //! The offset from which to read
+ off_t where;
+ //! \constr
+ io_req_impl() { }
+ //! \cconstr
+ io_req_impl(const io_req_impl &o) : precondition(o.precondition), buffers(o.buffers), where(o.where) { }
+ //! \mconstr
+ io_req_impl(io_req_impl &&o) noexcept : precondition(std::move(o.precondition)), buffers(std::move(o.buffers)), where(std::move(o.where)) { }
+ //! \cassign
+ io_req_impl &operator=(const io_req_impl &o) { precondition=o.precondition; buffers=o.buffers; where=o.where; return *this; }
+ //! \massign
+ io_req_impl &operator=(io_req_impl &&o) noexcept { precondition=std::move(o.precondition); buffers=std::move(o.buffers); where=std::move(o.where); return *this; }
+ //! \io_req2
+ io_req_impl(future<> _precondition, std::vector<asio::mutable_buffer> _buffers, off_t _where) : precondition(std::move(_precondition)), buffers(std::move(_buffers)), where(_where) { _validate(); }
+ //! Validates contents for correctness \return True if contents are correct
+ bool validate() const
+ {
+ //if(!precondition.validate()) return false;
+ if(buffers.empty()) return false;
+ for(auto &b: buffers)
+ {
+ if(!asio::buffer_cast<const void *>(b) || !asio::buffer_size(b)) return false;
+ if(precondition.parent() && !!(precondition.parent()->fileflags(file_flags::none)&file_flags::os_direct))
+ {
+ if(((size_t) asio::buffer_cast<const void *>(b) & 4095) || (asio::buffer_size(b) & 4095)) return false;
+ }
+ }
+ return true;
+ }
+ private:
+ void _validate() const
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if(!validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ }
+ };
+ template<> class io_req_impl<true>
+ {
+ public:
+ //! An optional precondition for this operation
+ future<> precondition;
+ //! A sequence of mutable Boost.ASIO buffers to read into
+ std::vector<asio::const_buffer> buffers;
+ //! The offset from which to read
+ off_t where;
+ //! \constr
+ io_req_impl() { }
+ //! \cconstr
+ io_req_impl(const io_req_impl &o) : precondition(o.precondition), buffers(o.buffers), where(o.where) { }
+ //! \mconstr
+ io_req_impl(io_req_impl &&o) noexcept : precondition(std::move(o.precondition)), buffers(std::move(o.buffers)), where(std::move(o.where)) { }
+ //! \cconstr
+ io_req_impl(const io_req_impl<false> &o) : precondition(o.precondition), where(o.where) { buffers.reserve(o.buffers.capacity()); for(auto &i: o.buffers){ buffers.push_back(i); } }
+ //! \mconstr
+ io_req_impl(io_req_impl<false> &&o) noexcept : precondition(std::move(o.precondition)), where(std::move(o.where)) { buffers.reserve(o.buffers.capacity()); for(auto &&i: o.buffers){ buffers.push_back(std::move(i)); } }
+ //! \cassign
+ io_req_impl &operator=(const io_req_impl &o) { precondition=o.precondition; buffers=o.buffers; where=o.where; return *this; }
+ //! \massign
+ io_req_impl &operator=(io_req_impl &&o) noexcept { precondition=std::move(o.precondition); buffers=std::move(o.buffers); where=std::move(o.where); return *this; }
+ //! \io_req2
+ io_req_impl(future<> _precondition, std::vector<asio::const_buffer> _buffers, off_t _where) : precondition(std::move(_precondition)), buffers(std::move(_buffers)), where(_where) { _validate(); }
+ //! \io_req2
+ io_req_impl(future<> _precondition, std::vector<asio::mutable_buffer> _buffers, off_t _where) : precondition(std::move(_precondition)), where(_where)
+ {
+ buffers.reserve(_buffers.capacity());
+ for(auto &&i: _buffers)
+ buffers.push_back(std::move(i));
+ _validate();
+ }
+ //! Validates contents for correctness \return True if contents are correct
+ bool validate() const
+ {
+ //if(!precondition.validate()) return false;
+ if(buffers.empty()) return false;
+ for(auto &b: buffers)
+ {
+ if(!asio::buffer_cast<const void *>(b) || !asio::buffer_size(b)) return false;
+ if(precondition.parent() && !!(precondition.parent()->fileflags(file_flags::none)&file_flags::os_direct))
+ {
+ if(((size_t) asio::buffer_cast<const void *>(b) & 4095) || (asio::buffer_size(b) & 4095)) return false;
+ }
+ }
+ return true;
+ }
+ private:
+ void _validate() const
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if(!validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ }
+ };
+}
+
+/*! \struct io_req
+\brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes.
+
+\tparam "class T" Any readable (if const) or writable (if non-const) type T as specified by its to_asio_buffers() overload.
+\ingroup io_req
+*/
+template<class T> struct io_req : public detail::io_req_impl<false>
+{
+#ifdef DOXYGEN_SHOULD_SKIP_THIS
+ //! A precondition containing an open file handle for this operation
+ future<> precondition;
+ //! A sequence of mutable Boost.ASIO buffers to read into
+ std::vector<asio::mutable_buffer> buffers;
+ //! The offset from which to read
+ off_t where;
+#endif
+ //! \constr
+ io_req() { }
+ //! \cconstr
+ io_req(const io_req &o) : detail::io_req_impl<false>(o) { }
+ //! \mconstr
+ io_req(io_req &&o) noexcept : detail::io_req_impl<false>(std::move(o)) { }
+ //! \cassign
+ io_req &operator=(const io_req &o) { static_cast<detail::io_req_impl<false>>(*this)=o; return *this; }
+ //! \massign
+ io_req &operator=(io_req &&o) noexcept { static_cast<detail::io_req_impl<false>>(*this)=std::move(o); return *this; }
+ //! \io_req1 \param _length The number of items to transfer
+ io_req(future<> _precondition, T *v, size_t _length, off_t _where) : detail::io_req_impl<false>(std::move(_precondition), to_asio_buffers(v, _length), _where) { }
+ //! \io_req1
+ template<class U> io_req(future<> _precondition, U &v, off_t _where) : detail::io_req_impl<false>(std::move(_precondition), to_asio_buffers(v), _where) { }
+ //! \io_req1 \tparam N The number of items in the array
+ template<class U, size_t N> io_req(future<> _precondition, U (&v)[N], off_t _where) : detail::io_req_impl<false>(std::move(_precondition), to_asio_buffers(v), _where) { }
+};
+/*!
+\brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes.
+
+\tparam "class T" Any readable (if const) or writable (if non-const) type T as specified by its to_asio_buffers() overload.
+\ingroup io_req
+*/
+template<class T> struct io_req<const T> : public detail::io_req_impl<true>
+{
+#ifdef DOXYGEN_SHOULD_SKIP_THIS
+ //! A precondition containing an open file handle for this operation
+ future<> precondition;
+ //! A sequence of const Boost.ASIO buffers to write from
+ std::vector<asio::const_buffer> buffers;
+ //! The offset at which to write
+ off_t where;
+#endif
+ //! \constr
+ io_req() { }
+ //! \cconstr
+ io_req(const io_req &o) : detail::io_req_impl<true>(o) { }
+ //! \mconstr
+ io_req(io_req &&o) noexcept : detail::io_req_impl<true>(std::move(o)) { }
+ //! \cconstr
+ io_req(const io_req<T> &o) : detail::io_req_impl<true>(o) { }
+ //! \mconstr
+ io_req(io_req<T> &&o) noexcept : detail::io_req_impl<true>(std::move(o)) { }
+ //! \cassign
+ io_req &operator=(const io_req &o) { static_cast<detail::io_req_impl<true>>(*this)=o; return *this; }
+ //! \massign
+ io_req &operator=(io_req &&o) noexcept { static_cast<detail::io_req_impl<true>>(*this)=std::move(o); return *this; }
+ //! \io_req1 \param _length The number of items to transfer
+ io_req(future<> _precondition, const T *v, size_t _length, off_t _where) : detail::io_req_impl<true>(std::move(_precondition), to_asio_buffers(v, _length), _where) { }
+ //! \io_req1
+ template<class U> io_req(future<> _precondition, const U &v, off_t _where) : detail::io_req_impl<true>(std::move(_precondition), to_asio_buffers(v), _where) { }
+ //! \io_req1 \tparam N The number of items in the array
+ template<class U, size_t N> io_req(future<> _precondition, const U (&v)[N], off_t _where) : detail::io_req_impl<true>(std::move(_precondition), to_asio_buffers(v), _where) { }
+};
+/*!
+\brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes.
+\ingroup io_req
+*/
+template<> struct io_req<void> : public detail::io_req_impl<false>
+{
+#ifdef DOXYGEN_SHOULD_SKIP_THIS
+ //! A precondition containing an open file handle for this operation
+ future<> precondition;
+ //! A sequence of mutable Boost.ASIO buffers to read into
+ std::vector<asio::mutable_buffer> buffers;
+ //! The offset from which to read
+ off_t where;
+#endif
+ //! \constr
+ io_req() { }
+ //! \cconstr
+ io_req(const io_req &o) : detail::io_req_impl<false>(o) { }
+ //! \mconstr
+ io_req(io_req &&o) noexcept : detail::io_req_impl<false>(std::move(o)) { }
+ //! \cassign
+ io_req &operator=(const io_req &o) { static_cast<detail::io_req_impl<false>>(*this)=o; return *this; }
+ //! \massign
+ io_req &operator=(io_req &&o) noexcept { static_cast<detail::io_req_impl<false>>(*this)=std::move(o); return *this; }
+ //! \io_req1 \param _length The number of items to transfer
+ io_req(future<> _precondition, void *v, size_t _length, off_t _where) : detail::io_req_impl<false>(std::move(_precondition), to_asio_buffers(v, _length), _where) { }
+};
+/*!
+\brief A convenience bundle of precondition, data and where for reading into a T as specified by its to_asio_buffers() overload. Data \b MUST stay around until the operation completes.
+\ingroup io_req
+*/
+template<> struct io_req<const void> : public detail::io_req_impl<true>
+{
+#ifdef DOXYGEN_SHOULD_SKIP_THIS
+ //! A precondition containing an open file handle for this operation
+ future<> precondition;
+ //! A sequence of const Boost.ASIO buffers to write from
+ std::vector<asio::const_buffer> buffers;
+ //! The offset at which to write
+ off_t where;
+#endif
+ //! \constr
+ io_req() { }
+ //! \cconstr
+ io_req(const io_req &o) : detail::io_req_impl<true>(o) { }
+ //! \mconstr
+ io_req(io_req &&o) noexcept : detail::io_req_impl<true>(std::move(o)) { }
+ //! \cconstr
+ io_req(const io_req<void> &o) : detail::io_req_impl<true>(o) { }
+ //! \mconstr
+ io_req(io_req<void> &&o) noexcept : detail::io_req_impl<true>(std::move(o)) { }
+ //! \cassign
+ io_req &operator=(const io_req &o) { static_cast<detail::io_req_impl<true>>(*this)=o; return *this; }
+ //! \massign
+ io_req &operator=(io_req &&o) noexcept { static_cast<detail::io_req_impl<true>>(*this)=std::move(o); return *this; }
+ //! \io_req1 \param _length The number of items to transfer
+ io_req(future<> _precondition, const void *v, size_t _length, off_t _where) : detail::io_req_impl<true>(std::move(_precondition), to_asio_buffers(v, _length), _where) { }
+};
+
+namespace detail
+{
+ template<class T, bool is_container=detail::is_container<T>::value> struct make_io_req
+ {
+ typedef typename std::remove_pointer<typename std::decay<T>::type>::type _T;
+ typedef io_req<_T> type;
+ template<class U> type operator()(future<> _precondition, U &&v, off_t _where) const
+ {
+ return type(std::move(_precondition), std::forward<U>(v), _where);
+ }
+ template<class U> type operator()(future<> _precondition, U &&v, size_t _length, off_t _where) const
+ {
+ return type(std::move(_precondition), std::forward<U>(v), _length, _where);
+ }
+ };
+ // If T is a container and that container's value_type is const, make sure we only create an asio::const_buffer
+ template<class T> struct make_io_req<T, true>
+ {
+ typedef typename detail::is_container<T>::type container_value_type;
+ static BOOST_CONSTEXPR_OR_CONST bool is_container_contents_const=std::is_const<container_value_type>::value || std::is_base_of<asio::const_buffer, container_value_type>::value;
+ typedef typename std::remove_pointer<typename std::decay<T>::type>::type __T;
+ typedef typename std::conditional<is_container_contents_const, typename std::add_const<__T>::type, __T>::type _T;
+ typedef io_req<_T> type;
+ template<class U> type operator()(future<> _precondition, U &&v, off_t _where) const
+ {
+ return type(std::move(_precondition), std::forward<U>(v), _where);
+ }
+ template<class U> type operator()(future<> _precondition, U &&v, size_t _length, off_t _where) const
+ {
+ return type(std::move(_precondition), std::forward<U>(v), _length, _where);
+ }
+ };
+}
+/*! \brief Convenience instantiator of a io_req, letting the compiler deduce the template specialisation to use.
+
+\return An io_req matching the supplied parameter type.
+\io_req1
+\ingroup make_io_req
+\qbk{distinguish, length deducing}
+\qbk{
+[heading Example]
+[readwrite_example]
+}
+*/
+template<class T> inline auto make_io_req(future<> _precondition, T &&v, off_t _where) -> decltype(detail::make_io_req<T>()(std::move(_precondition), std::forward<T>(v), _where))
+{
+ return detail::make_io_req<T>()(std::move(_precondition), std::forward<T>(v), _where);
+}
+/*! \brief Convenience instantiator of a io_req, letting the compiler deduce the template specialisation to use.
+
+\return An io_req matching the supplied parameter type.
+\io_req1
+\ingroup make_io_req
+\qbk{distinguish, length deducing}
+\qbk{
+[heading Example]
+[readwrite_example]
+}
+*/
+template<class T> inline io_req<const std::initializer_list<T>> make_io_req(future<> _precondition, const std::initializer_list<T> &v, off_t _where)
+{
+ return io_req<const std::initializer_list<T>>(std::move(_precondition), v, _where);
+}
+/*! \brief Convenience instantiator of a io_req, letting the compiler deduce the template specialisation to use.
+
+\return An io_req matching the supplied parameter type.
+\io_req1
+\param _length The number of bytes to transfer
+\ingroup make_io_req
+\qbk{distinguish, length specifying}
+\qbk{
+[heading Example]
+[readwrite_example]
+}
+*/
+template<class T> inline auto make_io_req(future<> _precondition, T &&v, size_t _length, off_t _where) -> decltype(detail::make_io_req<T>()(std::move(_precondition), std::forward<T>(v), _length, _where))
+{
+ return detail::make_io_req<T>()(std::move(_precondition), std::forward<T>(v), _length, _where);
+}
+
+
+/*! \struct enumerate_req
+\brief A convenience bundle of precondition, number of items to enumerate, item pattern match and metadata to prefetch.
+
+You should note that shell globs must use a restricted form for portability:
+
+* Microsoft Windows NT oddly does not specify what wildcards are permitted, but I think the documentation for the kernel
+function FsRtlIsNameInExpression() is probably sound: * means zero or more characters, ? means any one character. Do not
+use <, > or " as these have special MS-DOS compatibility inducing consequences. Do not use ^ as this is the Windows
+wildcard escape character.
+
+* POSIX further extends NT's wildcards with \\[seq\\] which is a subset of characters and \\[!seq\\] which is not any subset of
+characters. Here a \\ is the wildcard escape character.
+*/
+struct enumerate_req
+{
+ future<> precondition; //!< A precondition for this operation.
+ size_t maxitems; //!< The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+ bool restart; //!< Restarts the enumeration for this open directory handle.
+ path glob; //!< An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+ metadata_flags metadata; //!< The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+ //! How to do deleted file elimination on Windows
+ enum class filter
+ {
+ none, //!< Do no filtering at all
+ fastdeleted //!< Filter out AFIO deleted files based on their filename (fast and fairly reliable)
+ };
+ filter filtering; //!< Any filtering you want AFIO to do for you.
+ //! \constr
+ enumerate_req() : maxitems(0), restart(false), metadata(metadata_flags::None), filtering(filter::fastdeleted) { }
+ /*! \brief Constructs an instance.
+
+ \param _precondition The precondition for this operation.
+ \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+ \param _restart Restarts the enumeration for this open directory handle.
+ \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+ \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+ \param _filtering Any filtering you want AFIO to do for you.
+ */
+ enumerate_req(future<> _precondition, size_t _maxitems=2, bool _restart=true, path _glob=path(), metadata_flags _metadata=metadata_flags::None, filter _filtering=filter::fastdeleted) : precondition(std::move(_precondition)), maxitems(_maxitems), restart(_restart), glob(std::move(_glob)), metadata(_metadata), filtering(_filtering) { _validate(); }
+ /*! \brief Constructs an instance.
+
+ \param _precondition The precondition for this operation.
+ \param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+ \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+ \param _restart Restarts the enumeration for this open directory handle.
+ \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+ \param _filtering Any filtering you want AFIO to do for you.
+ */
+ enumerate_req(future<> _precondition, path _glob, size_t _maxitems=2, bool _restart=true, metadata_flags _metadata=metadata_flags::None, filter _filtering=filter::fastdeleted) : precondition(std::move(_precondition)), maxitems(_maxitems), restart(_restart), glob(std::move(_glob)), metadata(_metadata), filtering(_filtering) { _validate(); }
+ /*! \brief Constructs an instance.
+
+ \param _precondition The precondition for this operation.
+ \param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+ \param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+ \param _restart Restarts the enumeration for this open directory handle.
+ \param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+ \param _filtering Any filtering you want AFIO to do for you.
+ */
+ enumerate_req(future<> _precondition, metadata_flags _metadata, size_t _maxitems=2, bool _restart=true, path _glob=path(), filter _filtering=filter::fastdeleted) : precondition(std::move(_precondition)), maxitems(_maxitems), restart(_restart), glob(std::move(_glob)), metadata(_metadata), filtering(_filtering) { _validate(); }
+ //! Validates contents
+ bool validate() const
+ {
+ if(!maxitems) return false;
+ return !precondition.valid() || precondition.validate();
+ }
+private:
+ void _validate() const
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if(!validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ }
+};
+
+// Undocumented deliberately
+struct lock_req
+{
+ future<> precondition;
+ enum class Type { unknown, read_lock, write_lock, unlock } type;
+ off_t offset, length;
+ //chrono::time_point<chrono::steady_clock> deadline;
+ lock_req() : type(Type::unknown), offset(0), length(0) { }
+ lock_req(future<> _precondition, Type _type=Type::write_lock) : precondition(_precondition), type(_type), offset(0), length((off_t)-1) { _validate(); }
+ lock_req(future<> _precondition, std::nullptr_t) : precondition(_precondition), type(Type::unlock), offset(0), length((off_t)-1) { _validate(); }
+ lock_req(future<> _precondition, Type _type, off_t _offset, off_t _length) : precondition(_precondition), type(_type), offset(_offset), length(_length) { _validate(); }
+ lock_req(future<> _precondition, off_t _offset, off_t _length, Type _type=Type::write_lock) : precondition(_precondition), type(_type), offset(_offset), length(_length) { _validate(); }
+ //! Validates contents
+ bool validate() const
+ {
+ if(type==Type::unknown) return false;
+ if(offset+length<offset) return false;
+ return !precondition.valid() || precondition.validate();
+ }
+private:
+ void _validate() const
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if(!validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ }
+};
+
+
+
+namespace detail {
+ template<bool iswrite, class T> struct async_file_io_dispatcher_rwconverter
+ {
+ typedef detail::io_req_impl<iswrite> return_type;
+ const std::vector<return_type> &operator()(const std::vector<io_req<T>> &ops) const
+ {
+ typedef io_req<T> reqT;
+ static_assert(std::is_convertible<reqT, return_type>::value, "io_req<T> is not convertible to detail::io_req_impl<constness>");
+ static_assert(sizeof(return_type)==sizeof(reqT), "io_req<T> does not have the same size as detail::io_req_impl<constness>");
+ return reinterpret_cast<const std::vector<return_type> &>(ops);
+ }
+ };
+}
+
+#if defined(BOOST_AFIO_ENABLE_BENCHMARKING_COMPLETION) // Only really used for benchmarking
+inline future<> dispatcher::completion(const future<> &req, const std::pair<async_op_flags, dispatcher::completion_t *> &callback)
+{
+ std::vector<future<>> r;
+ std::vector<std::pair<async_op_flags, dispatcher::completion_t *>> i;
+ r.reserve(1); i.reserve(1);
+ r.push_back(req);
+ i.push_back(callback);
+ auto ret(std::move(completion(r, i).front()));
+ return ret;
+}
+#endif
+inline future<> dispatcher::completion(const future<> &req, const std::pair<async_op_flags, std::function<dispatcher::completion_t>> &callback)
+{
+ std::vector<future<>> r;
+ std::vector<std::pair<async_op_flags, std::function<dispatcher::completion_t>>> i;
+ r.reserve(1); i.reserve(1);
+ r.push_back(req);
+ i.push_back(callback);
+ auto ret(std::move(completion(r, i).front()));
+ return ret;
+}
+namespace detail {
+ template<class tasktype> std::pair<bool, handle_ptr> doCall(size_t, future<> _, std::shared_ptr<tasktype> c)
+ {
+ (*c)();
+ return std::make_pair(true, _.get_handle(true));
+ }
+}
+template<class R> inline std::vector<future<R>> dispatcher::call(const std::vector<future<>> &ops, const std::vector<std::function<R()>> &callables)
+{
+ typedef packaged_task<R()> tasktype;
+ std::vector<stl_future<R>> retfutures;
+ std::vector<std::pair<async_op_flags, std::function<completion_t>>> callbacks;
+ retfutures.reserve(callables.size());
+ callbacks.reserve(callables.size());
+
+ for(auto &t: callables)
+ {
+ std::shared_ptr<tasktype> c(std::make_shared<tasktype>(std::function<R()>(t)));
+ retfutures.push_back(c->get_future());
+ callbacks.push_back(std::make_pair(async_op_flags::none, std::bind(&detail::doCall<tasktype>, std::placeholders::_1, std::placeholders::_2, std::move(c))));
+ }
+ auto _ret(completion(ops, callbacks));
+ std::vector<future<R>> ret;
+ ret.reserve(_ret.size());
+ for (size_t n = 0; n < _ret.size(); n++)
+ ret.push_back(future<R>(std::move(_ret[n]), std::move(retfutures[n])));
+ return ret;
+}
+template<class R> inline future<R> dispatcher::call(const future<> &req, std::function<R()> callback)
+{
+ std::vector<future<>> i;
+ std::vector<std::function<R()>> c;
+ i.reserve(1); c.reserve(1);
+ i.push_back(req);
+ c.push_back(std::move(callback));
+ auto ret(std::move(call(i, c).front()));
+ return ret;
+}
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+template<class C, class... Args> inline future<typename detail::vs2013_variadic_overload_resolution_workaround<C, Args...>::type> dispatcher::call(const future<> &req, C callback, Args... args)
+#else
+template<class C, class... Args> inline future<typename std::result_of<C(Args...)>::type> dispatcher::call(const future<> &req, C callback, Args... args)
+#endif
+{
+ typedef typename std::result_of<C(Args...)>::type rettype;
+ return call(req, std::function<rettype()>(std::bind<rettype>(callback, args...)));
+}
+
+inline future<> dispatcher::adopt(handle_ptr h)
+{
+ std::vector<handle_ptr> i;
+ i.reserve(1);
+ i.push_back(std::move(h));
+ auto ret(std::move(adopt(i).front()));
+ return ret;
+}
+inline future<> dispatcher::dir(const path_req &req)
+{
+ std::vector<path_req> i;
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(dir(i).front()));
+ return ret;
+}
+inline future<> dispatcher::rmdir(const path_req &req)
+{
+ std::vector<path_req> i;
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(rmdir(i).front()));
+ return ret;
+}
+inline future<> dispatcher::file(const path_req &req)
+{
+ std::vector<path_req> i;
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(file(i).front()));
+ return ret;
+}
+inline future<> dispatcher::rmfile(const path_req &req)
+{
+ std::vector<path_req> i;
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(rmfile(i).front()));
+ return ret;
+}
+inline future<> dispatcher::symlink(const path_req &req, const future<> &target)
+{
+ std::vector<path_req> i(1, req);
+ std::vector<future<>> t(1, target);
+ auto ret(std::move(symlink(i, t).front()));
+ return ret;
+}
+inline future<> dispatcher::rmsymlink(const path_req &req)
+{
+ std::vector<path_req> i;
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(rmsymlink(i).front()));
+ return ret;
+}
+inline future<> dispatcher::sync(const future<> &req)
+{
+ std::vector<future<>> i;
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(sync(i).front()));
+ return ret;
+}
+inline future<> dispatcher::zero(const future<> &req, const std::vector<std::pair<off_t, off_t>> &ranges)
+{
+ std::vector<future<>> i;
+ std::vector<std::vector<std::pair<off_t, off_t>>> r;
+ i.reserve(1);
+ i.push_back(req);
+ r.reserve(1);
+ r.push_back(ranges);
+ auto ret(std::move(zero(i, r).front()));
+ return ret;
+}
+inline future<> dispatcher::close(const future<> &req)
+{
+ std::vector<future<>> i;
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(close(i).front()));
+ return ret;
+}
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+inline future<> dispatcher::read(const detail::io_req_impl<false> &req)
+{
+ std::vector<detail::io_req_impl<false>> i;
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(read(i).front()));
+ return ret;
+}
+inline future<> dispatcher::write(const detail::io_req_impl<true> &req)
+{
+ std::vector<detail::io_req_impl<true>> i;
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(write(i).front()));
+ return ret;
+}
+#endif
+template<class T> inline std::vector<future<>> dispatcher::read(const std::vector<io_req<T>> &ops)
+{
+ return read(detail::async_file_io_dispatcher_rwconverter<false, T>()(ops));
+}
+template<class T> inline std::vector<future<>> dispatcher::write(const std::vector<io_req<T>> &ops)
+{
+ return write(detail::async_file_io_dispatcher_rwconverter<true, T>()(ops));
+}
+inline future<> dispatcher::truncate(const future<> &op, off_t newsize)
+{
+ std::vector<future<>> o;
+ std::vector<off_t> i;
+ o.reserve(1);
+ o.push_back(op);
+ i.reserve(1);
+ i.push_back(newsize);
+ auto ret(std::move(truncate(o, i).front()));
+ return ret;
+}
+inline future<std::pair<std::vector<directory_entry>, bool>> dispatcher::enumerate(const enumerate_req &req)
+{
+ std::vector<enumerate_req> i;
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(enumerate(i).front()));
+ return ret;
+}
+inline future<std::vector<std::pair<off_t, off_t>>> dispatcher::extents(const future<> &op)
+{
+ std::vector<future<>> o;
+ o.reserve(1);
+ o.push_back(op);
+ auto ret(std::move(extents(o).front()));
+ return ret;
+}
+inline future<statfs_t> dispatcher::statfs(const future<> &op, const fs_metadata_flags &req)
+{
+ std::vector<future<>> o;
+ std::vector<fs_metadata_flags> i;
+ o.reserve(1);
+ o.push_back(op);
+ i.reserve(1);
+ i.push_back(req);
+ auto ret(std::move(statfs(o, i).front()));
+ return ret;
+}
+inline future<> dispatcher::depends(future<> precondition, future<> op)
+{
+ std::pair<async_op_flags, std::function<dispatcher::completion_t>> callback(std::make_pair(async_op_flags::immediate,
+ [BOOST_AFIO_LAMBDA_MOVE_CAPTURE(op)](size_t, future<>) { return std::make_pair(true, op.get_handle()); }));
+ std::vector<future<>> r;
+ std::vector<std::pair<async_op_flags, std::function<dispatcher::completion_t>>> i;
+ r.reserve(1); i.reserve(1);
+ r.push_back(precondition);
+ i.push_back(std::move(callback));
+ auto ret(std::move(completion(r, i).front()));
+ return ret;
+}
+
+namespace detail
+{
+ template<class T> struct async_dir
+ {
+ T path;
+ file_flags flags;
+ async_dir(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { }
+ future<> operator()(future<> f=future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ path_req req(!dispatcher ? (
+ dispatcher = current_dispatcher().get(),
+ path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
+ ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if (!req.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ auto ret(std::move(dispatcher->dir(std::vector<path_req>(1, std::move(req))).front()));
+ return ret;
+ }
+ };
+ template<class T> struct async_rmdir
+ {
+ T path;
+ file_flags flags;
+ async_rmdir(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { }
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ path_req req(!dispatcher ? (
+ dispatcher = current_dispatcher().get(),
+ path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
+ ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if (!req.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ auto ret(std::move(dispatcher->rmdir(std::vector<path_req>(1, std::move(req))).front()));
+ return ret;
+ }
+ };
+ template<class T> struct async_file
+ {
+ T path;
+ file_flags flags;
+ async_file(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { }
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ path_req req(!dispatcher ? (
+ dispatcher = current_dispatcher().get(),
+ path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
+ ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if (!req.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ auto ret(std::move(dispatcher->file(std::vector<path_req>(1, std::move(req))).front()));
+ return ret;
+ }
+ };
+ template<class T> struct async_rmfile
+ {
+ T path;
+ file_flags flags;
+ async_rmfile(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { }
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ path_req req(!dispatcher ? (
+ dispatcher = current_dispatcher().get(),
+ path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
+ ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if (!req.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ auto ret(std::move(dispatcher->rmfile(std::vector<path_req>(1, std::move(req))).front()));
+ return ret;
+ }
+ };
+ template<class T> struct async_symlink
+ {
+ T path;
+ file_flags flags;
+ future<> target;
+ async_symlink(T _path, file_flags _flags, future<> _target) : path(std::move(_path)), flags(_flags), target(std::move(_target)) { }
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ path_req req(!dispatcher ? (
+ dispatcher = current_dispatcher().get(),
+ path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
+ ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if (!req.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ auto ret(std::move(dispatcher->symlink(std::vector<path_req>(1, std::move(req)), std::vector<future<>>(1, std::move(target))).front()));
+ return ret;
+ }
+ };
+ template<class T> struct async_rmsymlink
+ {
+ T path;
+ file_flags flags;
+ async_rmsymlink(T _path, file_flags _flags) : path(std::move(_path)), flags(_flags) { }
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ path_req req(!dispatcher ? (
+ dispatcher = current_dispatcher().get(),
+ path_req(path_req::absolute(std::move(f), std::move(path), std::move(flags)))
+ ) : path_req(path_req::relative(std::move(f), std::move(path), std::move(flags))));
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if (!req.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ auto ret(std::move(dispatcher->rmsymlink(std::vector<path_req>(1, std::move(req))).front()));
+ return ret;
+ }
+ };
+ struct async_sync
+ {
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ if (!dispatcher)
+ dispatcher = current_dispatcher().get();
+ auto ret(std::move(dispatcher->sync(std::vector<future<>>(1, std::move(f))).front()));
+ return ret;
+ }
+ };
+ struct async_close
+ {
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ if (!dispatcher)
+ dispatcher = current_dispatcher().get();
+ auto ret(std::move(dispatcher->close(std::vector<future<>>(1, std::move(f))).front()));
+ return ret;
+ }
+ };
+ struct async_read
+ {
+ io_req_impl<false> req;
+ template<class U> async_read(U &&v, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward<U>(v), _where)) { }
+ template<class U> async_read(U &&v, size_t _length, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward<U>(v), _length, _where)) { }
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ if (!dispatcher)
+ dispatcher = current_dispatcher().get();
+ req.precondition = f;
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if (!req.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ auto ret(std::move(dispatcher->read(std::vector<io_req_impl<false>>(1, std::move(req))).front()));
+ return ret;
+ }
+ };
+ struct async_write
+ {
+ io_req_impl<true> req;
+ template<class U> async_write(U &&v, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward<U>(v), _where)) { }
+ template<class U> async_write(U &&v, size_t _length, off_t _where) : req(BOOST_AFIO_V2_NAMESPACE::make_io_req(future<>(), std::forward<U>(v), _length, _where)) { }
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ if (!dispatcher)
+ dispatcher = current_dispatcher().get();
+ req.precondition = f;
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if (!req.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ auto ret(std::move(dispatcher->write(std::vector<io_req_impl<true>>(1, std::move(req))).front()));
+ return ret;
+ }
+ };
+ struct async_truncate
+ {
+ off_t _size;
+ async_truncate(off_t size) : _size(size) { }
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ if (!dispatcher)
+ dispatcher = current_dispatcher().get();
+ auto ret(std::move(dispatcher->truncate(std::vector<future<>>(1, std::move(f)), std::vector<off_t>(1, _size)).front()));
+ return ret;
+ }
+ };
+ struct async_enumerate
+ {
+ size_t maxitems;
+ bool restart;
+ path glob;
+ metadata_flags metadata;
+ enumerate_req::filter filtering;
+ async_enumerate(size_t _maxitems, bool _restart, path _glob, metadata_flags _metadata, enumerate_req::filter _filtering) : maxitems(_maxitems), restart(_restart), glob(_glob), metadata(_metadata), filtering(_filtering) { }
+ future<std::pair<std::vector<directory_entry>, bool>> operator()(future<> f = future<>())
+ {
+ enumerate_req req(std::move(f), maxitems, restart, std::move(glob), metadata, filtering);
+ dispatcher *dispatcher = f.parent();
+ if (!dispatcher)
+ dispatcher = current_dispatcher().get();
+#if BOOST_AFIO_VALIDATE_INPUTS
+ if (!req.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+#endif
+ auto ret(std::move(dispatcher->enumerate(std::vector<enumerate_req>(1, std::move(req))).front()));
+ return ret;
+ }
+ };
+ struct async_zero
+ {
+ std::vector<std::pair<off_t, off_t>> ranges;
+ async_zero(std::vector<std::pair<off_t, off_t>> _ranges) : ranges(_ranges) { }
+ future<> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ if (!dispatcher)
+ dispatcher = current_dispatcher().get();
+ auto ret(std::move(dispatcher->zero(std::vector<future<>>(1, std::move(f)), std::vector<std::vector<std::pair<off_t, off_t>>>(1, std::move(ranges))).front()));
+ return ret;
+ }
+ };
+ struct async_extents
+ {
+ future<std::vector<std::pair<off_t, off_t>>> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ if (!dispatcher)
+ dispatcher = current_dispatcher().get();
+ auto ret(std::move(dispatcher->extents(std::vector<future<>>(1, std::move(f))).front()));
+ return ret;
+ }
+ };
+ struct async_statfs
+ {
+ fs_metadata_flags req;
+ async_statfs(fs_metadata_flags _req) : req(_req) { }
+ future<statfs_t> operator()(future<> f = future<>())
+ {
+ dispatcher *dispatcher = f.parent();
+ if (!dispatcher)
+ dispatcher = current_dispatcher().get();
+ auto ret(std::move(dispatcher->statfs(std::vector<future<>>(1, std::move(f)), std::vector<fs_metadata_flags>(1, req)).front()));
+ return ret;
+ }
+ };
+ template<class T> struct _is_not_handle : public std::true_type { };
+ template<class T> struct _is_not_handle<future<T>> : public std::false_type { };
+ template<> struct _is_not_handle<handle_ptr> : public std::false_type { };
+ template<class T> struct is_not_handle : public _is_not_handle<typename std::decay<T>::type> { };
+}
+
+/*! \brief Asynchronous directory creation and open after an optional precondition.
+
+\docs_dir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\return A future<void>
+\param _precondition The precondition to use.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup dir
+\qbk{distinguish, relative}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T> inline future<> async_dir(future<> _precondition, T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_dir<T>(std::move(_path), _flags)(std::move(_precondition));
+}
+/*! \brief Asynchronous directory creation and open after an optional precondition.
+
+\docs_dir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\return A future<void>
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup dir
+\qbk{distinguish, absolute}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename=typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_dir(T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_dir<T>(std::move(_path), _flags)(future<>());
+}
+/*! \brief Synchronous directory creation and open after an optional precondition.
+
+\docs_dir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the directory.
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup dir
+\qbk{distinguish, relative throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T> inline handle_ptr dir(future<> _precondition, T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_dir<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous directory creation and open after an optional precondition.
+
+\docs_dir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the directory.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup dir
+\qbk{distinguish, absolute throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr dir(T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_dir<T>(std::move(_path), _flags)(future<>()).get_handle();
+}
+/*! \brief Synchronous directory creation and open after an optional precondition.
+
+\docs_dir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the directory.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup dir
+\qbk{distinguish, relative non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..Race free up to the containing directory.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T> inline handle_ptr dir(error_code &_ec, future<> _precondition, T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_dir<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec);
+}
+/*! \brief Synchronous directory creation and open after an optional precondition.
+
+\docs_dir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the directory.
+\param _ec Error code to set.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup dir
+\qbk{distinguish, absolute non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr dir(error_code &_ec, T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_dir<T>(std::move(_path), _flags)(future<>()).get_handle(_ec);
+}
+
+/*! \brief Asynchronous directory deletion after an optional precondition.
+
+\docs_rmdir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A future<void>
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup rmdir
+\qbk{distinguish, relative}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T=path> inline future<> async_rmdir(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
+{
+ return detail::async_rmdir<T>(std::move(_path), _flags)(std::move(_precondition));
+}
+/*! \brief Asynchronous directory deletion after an optional precondition.
+
+\docs_rmdir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A future<void>
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup rmdir
+\qbk{distinguish, absolute}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_rmdir(T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_rmdir<T>(std::move(_path), _flags)(future<>());
+}
+/*! \brief Synchronous directory deletion after an optional precondition.
+
+\docs_rmdir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup rmdir
+\qbk{distinguish, relative throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T=path> inline void rmdir(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
+{
+ detail::async_rmdir<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous directory deletion after an optional precondition.
+
+\docs_rmdir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup rmdir
+\qbk{distinguish, absolute throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmdir(T _path, file_flags _flags = file_flags::none)
+{
+ detail::async_rmdir<T>(std::move(_path), _flags)(future<>()).get_handle();
+}
+/*! \brief Synchronous directory deletion after an optional precondition.
+
+\docs_rmdir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup rmdir
+\qbk{distinguish, relative non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T=path> inline void rmdir(error_code &_ec, future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
+{
+ detail::async_rmdir<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec);
+}
+/*! \brief Synchronous directory deletion after an optional precondition.
+
+\docs_rmdir
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\param _ec Error code to set.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup rmdir
+\qbk{distinguish, absolute non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if directory deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmdir(error_code &_ec, T _path, file_flags _flags = file_flags::none)
+{
+ detail::async_rmdir<T>(std::move(_path), _flags)(future<>()).get_handle(_ec);
+}
+
+/*! \brief Asynchronous file creation and open after an optional precondition.
+
+\docs_file
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A future<void>
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup file
+\qbk{distinguish, relative}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T> inline future<> async_file(future<> _precondition, T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_file<T>(std::move(_path), _flags)(std::move(_precondition));
+}
+/*! \brief Asynchronous file creation and open after an optional precondition.
+
+\docs_file
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A future<void>
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup file
+\qbk{distinguish, absolute}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_file(T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_file<T>(std::move(_path), _flags)(future<>());
+}
+/*! \brief Synchronous file creation and open after an optional precondition.
+
+\docs_file
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the file.
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup file
+\qbk{distinguish, relative throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T> inline handle_ptr file(future<> _precondition, T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_file<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous file creation and open after an optional precondition.
+
+\docs_file
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the file.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup file
+\qbk{distinguish, absolute throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr file(T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_file<T>(std::move(_path), _flags)(future<>()).get_handle();
+}
+/*! \brief Synchronous file creation and open after an optional precondition.
+
+\docs_file
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the file.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup file
+\qbk{distinguish, relative non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, Windows..Race free up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T> inline handle_ptr file(error_code &_ec, future<> _precondition, T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_file<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec);
+}
+/*! \brief Synchronous file creation and open after an optional precondition.
+
+\docs_file
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the file.
+\param _ec Error code to set.
+\param _path The filing system path to use.
+\param _flags The flags to use.
+\ingroup file
+\qbk{distinguish, absolute non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr file(error_code &_ec, T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_file<T>(std::move(_path), _flags)(future<>()).get_handle(_ec);
+}
+
+/*! \brief Asynchronous file deletion after an optional precondition.
+
+\docs_rmfile
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\return A future<void>
+\param _precondition The precondition to use.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmfile
+\qbk{distinguish, relative}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T=path> inline future<> async_rmfile(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
+{
+ return detail::async_rmfile<T>(std::move(_path), _flags)(std::move(_precondition));
+}
+/*! \brief Asynchronous file deletion after an optional precondition.
+
+\docs_rmfile
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\return A future<void>
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmfile
+\qbk{distinguish, absolute}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_rmfile(T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_rmfile<T>(std::move(_path), _flags)(future<>());
+}
+/*! \brief Synchronous file deletion after an optional precondition.
+
+\docs_rmfile
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\param _precondition The precondition to use.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmfile
+\qbk{distinguish, relative throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T=path> inline void rmfile(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
+{
+ detail::async_rmfile<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous file deletion after an optional precondition.
+
+\docs_rmfile
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmfile
+\qbk{distinguish, absolute throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmfile(T _path, file_flags _flags = file_flags::none)
+{
+ detail::async_rmfile<T>(std::move(_path), _flags)(future<>()).get_handle();
+}
+/*! \brief Synchronous file deletion after an optional precondition.
+
+\docs_rmfile
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmfile
+\qbk{distinguish, relative non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T=path> inline void rmfile(error_code &_ec, future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
+{
+ detail::async_rmfile<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec);
+}
+/*! \brief Synchronous file deletion after an optional precondition.
+
+\docs_rmfile
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\param _ec Error code to set.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmfile
+\qbk{distinguish, absolute non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if file deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmfile(error_code &_ec, T _path, file_flags _flags = file_flags::none)
+{
+ detail::async_rmfile<T>(std::move(_path), _flags)(future<>()).get_handle(_ec);
+}
+
+
+/*! \brief Asynchronous symlink creation and open after a precondition.
+
+\docs_symlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A future<void>
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _target The item to link to if creating.
+\param _flags The flags to use.
+\ingroup symlink
+\qbk{distinguish, relative}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T> inline future<> async_symlink(future<> _precondition, T _path, future<> _target=future<>(), file_flags _flags = file_flags::none)
+{
+ return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(std::move(_precondition));
+}
+/*! \brief Asynchronous symlink creation and open after a precondition.
+
+\docs_symlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A future<void>
+\param _path The filing system path to use.
+\param _target The item to link to if creating.
+\param _flags The flags to use.
+\ingroup symlink
+\qbk{distinguish, absolute}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_symlink(T _path, future<> _target=future<>(), file_flags _flags = file_flags::none)
+{
+ return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(future<>());
+}
+/*! \brief Synchronous symlink creation and open after a precondition..
+
+\docs_symlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the symlink.
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _target The item to link to if creating.
+\param _flags The flags to use.
+\ingroup symlink
+\qbk{distinguish, relative throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T> inline handle_ptr symlink(future<> _precondition, T _path, future<> _target = future<>(), file_flags _flags = file_flags::none)
+{
+ return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous symlink creation and open after a precondition.
+
+\docs_symlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the symlink.
+\param _path The filing system path to use.
+\param _target The item to link to if creating.
+\param _flags The flags to use.
+\ingroup symlink
+\qbk{distinguish, absolute throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr symlink(T _path, future<> _target = future<>(), file_flags _flags = file_flags::none)
+{
+ return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(future<>()).get_handle();
+}
+/*! \brief Synchronous symlink creation and open after a precondition.
+
+\docs_symlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the symlink.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param _path The filing system path to use.
+\param _target The item to link to if creating.
+\param _flags The flags to use.
+\ingroup symlink
+\qbk{distinguish, relative non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, Windows..Link creation is race free up to the containing directory. Destination is unavoidably racy.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T> inline handle_ptr symlink(error_code &_ec, future<> _precondition, T _path, future<> _target = future<>(), file_flags _flags = file_flags::none)
+{
+ return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(std::move(_precondition)).get_handle(_ec);
+}
+/*! \brief Synchronous symlink creation and open after a precondition.
+
+\docs_symlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to use.
+\return A handle to the symlink.
+\param _ec Error code to set.
+\param _path The filing system path to use.
+\param _target The item to link to if creating.
+\param _flags The flags to use.
+\ingroup symlink
+\qbk{distinguish, absolute non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink creation is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline handle_ptr symlink(error_code &_ec, T _path, future<> _target = future<>(), file_flags _flags = file_flags::none)
+{
+ return detail::async_symlink<T>(std::move(_path), _flags, std::move(_target))(future<>()).get_handle(_ec);
+}
+
+/*! \brief Asynchronous symlink deletion after an optional precondition.
+
+\docs_rmsymlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\return A future<void>
+\param _precondition The precondition to use.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmsymlink
+\qbk{distinguish, relative}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T=path> inline future<> async_rmsymlink(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
+{
+ return detail::async_rmsymlink<T>(std::move(_path), _flags)(std::move(_precondition));
+}
+/*! \brief Asynchronous symlink deletion after an optional precondition.
+
+\docs_rmsymlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\return A future<void>
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmsymlink
+\qbk{distinguish, absolute}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline future<> async_rmsymlink(T _path, file_flags _flags = file_flags::none)
+{
+ return detail::async_rmsymlink<T>(std::move(_path), _flags)(future<>());
+}
+/*! \brief Synchronous symlink deletion after an optional precondition.
+
+\docs_rmsymlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\param _precondition The precondition to use.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmsymlink
+\qbk{distinguish, relative throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T=path> inline void rmsymlink(future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
+{
+ detail::async_rmsymlink<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous symlink deletion after an optional precondition.
+
+\docs_rmsymlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmsymlink
+\qbk{distinguish, absolute throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmsymlink(T _path, file_flags _flags = file_flags::none)
+{
+ detail::async_rmsymlink<T>(std::move(_path), _flags)(future<>()).get_handle();
+}
+/*! \brief Synchronous symlink deletion after an optional precondition.
+
+\docs_rmsymlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmsymlink
+\qbk{distinguish, relative non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux..Race free up to the containing directory.]
+[raceguarantee Windows..Race free if handle open, else up to the containing directory.]
+[raceguarantee OS X..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T=path> inline void rmsymlink(error_code &_ec, future<> _precondition, T _path = path(), file_flags _flags = file_flags::none)
+{
+ detail::async_rmsymlink<T>(std::move(_path), _flags)(std::move(_precondition)).get_handle(_ec);
+}
+/*! \brief Synchronous symlink deletion after an optional precondition.
+
+\docs_rmsymlink
+\ntkernelnamespacenote
+
+\tparam "class T" The type of path to be used.
+\param _ec Error code to set.
+\param _path The filing system path to be used.
+\param _flags The flags to be used.
+\ingroup rmsymlink
+\qbk{distinguish, absolute non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X, Windows..No guarantees.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if symlink deletion is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+template<class T, typename = typename std::enable_if<detail::is_not_handle<T>::value>::type> inline void rmsymlink(error_code &_ec, T _path, file_flags _flags = file_flags::none)
+{
+ detail::async_rmsymlink<T>(std::move(_path), _flags)(future<>()).get_handle(_ec);
+}
+
+
+/*! \brief Asynchronous content synchronisation with physical storage after a preceding operation.
+
+\docs_sync
+
+\return A future<void>
+\param _precondition The precondition to use.
+\ingroup sync
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+inline future<> async_sync(future<> _precondition)
+{
+ return detail::async_sync()(std::move(_precondition));
+}
+/*! \brief Synchronous content synchronisation with physical storage after a preceding operation.
+
+\docs_sync
+
+\param _precondition The precondition to use.
+\ingroup sync
+\qbk{distinguish, throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+inline void sync(future<> _precondition)
+{
+ detail::async_sync()(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous content synchronisation with physical storage after a preceding operation.
+
+\docs_sync
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\ingroup sync
+\qbk{distinguish, non throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if content synchronisation is constant time (which is extremely unlikely).}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+inline void sync(error_code &_ec, future<> _precondition)
+{
+ detail::async_sync()(std::move(_precondition)).get_handle(_ec);
+}
+
+
+/*! \brief Asynchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation.
+
+\docs_zero
+
+\return A future<void>
+\param _precondition The precondition to use.
+\param ranges A sequence of extents to zero and deallocate
+\ingroup zero
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.}
+\exceptionmodelfree
+\qexample{extents_example}
+*/
+inline future<> async_zero(future<> _precondition, std::vector<std::pair<off_t, off_t>> ranges)
+{
+ return detail::async_zero(std::move(ranges))(std::move(_precondition));
+}
+/*! \brief Synchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation.
+
+\docs_zero
+
+\param _precondition The precondition to use.
+\param ranges A sequence of extents to zero and deallocate
+\ingroup zero
+\qbk{distinguish, throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.}
+\exceptionmodelfree
+\qexample{extents_example}
+*/
+inline void zero(future<> _precondition, std::vector<std::pair<off_t, off_t>> ranges)
+{
+ detail::async_zero(std::move(ranges))(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous zeroing and deallocation of physical storage ("hole punching") after a preceding operation.
+
+\docs_zero
+
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param ranges A sequence of extents to zero and deallocate
+\ingroup zero
+\qbk{distinguish, non throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if deallocation is constant time.}
+\exceptionmodelfree
+\qexample{extents_example}
+*/
+inline void zero(error_code &_ec, future<> _precondition, std::vector<std::pair<off_t, off_t>> ranges)
+{
+ detail::async_zero(std::move(ranges))(std::move(_precondition)).get_handle(_ec);
+}
+
+
+/*! \brief Asynchronous file or directory handle close after a preceding operation.
+
+\docs_close
+
+\return A future<void>
+\param _precondition The precondition to use.
+\ingroup close
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+inline future<> async_close(future<> _precondition)
+{
+ return detail::async_close()(std::move(_precondition));
+}
+/*! \brief Synchronous file or directory handle close after a preceding operation.
+
+\docs_close
+
+\param _precondition The precondition to use.
+\ingroup close
+\qbk{distinguish, throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+inline void close(future<> _precondition)
+{
+ detail::async_close()(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous file or directory handle close after a preceding operation.
+
+\docs_close
+
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\ingroup close
+\qbk{distinguish, non throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if closing handles is constant time.}
+\exceptionmodelfree
+\qexample{filedir_example}
+*/
+inline void close(error_code &_ec, future<> _precondition)
+{
+ detail::async_close()(std::move(_precondition)).get_handle(_ec);
+}
+
+
+/*! \brief Asynchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+\docs_read
+\direct_io_note
+
+\tparam "class T" Any type.
+\return A future<void>
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _where The file offset to do the i/o
+\ingroup read
+\qbk{distinguish, length deducing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline future<> async_read(future<> _precondition, T &&v, off_t _where)
+{
+ return detail::async_read(std::forward<T>(v), _where)(std::move(_precondition));
+}
+/*! \brief Asynchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+\docs_read
+\direct_io_note
+
+\tparam "class T" Any type.
+\return A future<void>
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _length The length of the item
+\param _where The file offset to do the i/o
+\ingroup read
+\qbk{distinguish, length specifying}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline future<> async_read(future<> _precondition, T &&v, size_t _length, off_t _where)
+{
+ return detail::async_read(std::forward<T>(v), _length, _where)(std::move(_precondition));
+}
+/*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+\docs_read
+\direct_io_note
+
+\tparam "class T" Any type.
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _where The file offset to do the i/o
+\ingroup read
+\qbk{distinguish, length deducing throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline void read(future<> _precondition, T &&v, off_t _where)
+{
+ detail::async_read(std::forward<T>(v), _where)(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+\docs_read
+\direct_io_note
+
+\tparam "class T" Any type.
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _length The length of the item
+\param _where The file offset to do the i/o
+\ingroup read
+\qbk{distinguish, length specifying throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline void read(future<> _precondition, T &&v, size_t _length, off_t _where)
+{
+ detail::async_read(std::forward<T>(v), _length, _where)(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+\docs_read
+\direct_io_note
+
+\tparam "class T" Any type.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _where The file offset to do the i/o
+\ingroup read
+\qbk{distinguish, length deducing non throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline void read(error_code &_ec, future<> _precondition, T &&v, off_t _where)
+{
+ detail::async_read(std::forward<T>(v), _where)(std::move(_precondition)).get_handle(_ec);
+}
+/*! \brief Synchronous data read after a preceding operation, where offset and total data read must not exceed the present file size.
+
+\docs_read
+\direct_io_note
+
+\tparam "class T" Any type.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _length The length of the item
+\param _where The file offset to do the i/o
+\ingroup read
+\qbk{distinguish, length specifying non throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if reading data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline void read(error_code &_ec, future<> _precondition, T &&v, size_t _length, off_t _where)
+{
+ detail::async_read(std::forward<T>(v), _length, _where)(std::move(_precondition)).get_handle(_ec);
+}
+
+
+/*! \brief Asynchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+\docs_write
+\direct_io_note
+
+\tparam "class T" Any type.
+\return A future<void>
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _where The file offset to do the i/o
+\ingroup write
+\qbk{distinguish, length deducing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline future<> async_write(future<> _precondition, T &&v, off_t _where)
+{
+ return detail::async_write(std::forward<T>(v), _where)(std::move(_precondition));
+}
+/*! \brief Asynchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+\docs_write
+\direct_io_note
+
+\tparam "class T" Any type.
+\return A future<void>
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _length The length of the item
+\param _where The file offset to do the i/o
+\ingroup write
+\qbk{distinguish, length specifying}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline future<> async_write(future<> _precondition, T &&v, size_t _length, off_t _where)
+{
+ return detail::async_write(std::forward<T>(v), _length, _where)(std::move(_precondition));
+}
+/*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+\docs_write
+\direct_io_note
+
+\tparam "class T" Any type.
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _where The file offset to do the i/o
+\ingroup write
+\qbk{distinguish, length deducing throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline void write(future<> _precondition, T &&v, off_t _where)
+{
+ detail::async_write(std::forward<T>(v), _where)(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+\docs_write
+\direct_io_note
+
+\tparam "class T" Any type.
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _length The length of the item
+\param _where The file offset to do the i/o
+\ingroup write
+\qbk{distinguish, length specifying throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline void write(future<> _precondition, T &&v, size_t _length, off_t _where)
+{
+ detail::async_write(std::forward<T>(v), _length, _where)(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+\docs_write
+\direct_io_note
+
+\tparam "class T" Any type.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _where The file offset to do the i/o
+\ingroup write
+\qbk{distinguish, length deducing non throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline void write(error_code &_ec, future<> _precondition, T &&v, off_t _where)
+{
+ detail::async_write(std::forward<T>(v), _where)(std::move(_precondition)).get_handle(_ec);
+}
+/*! \brief Synchronous data write after a preceding operation, where offset and total data written must not exceed the present file size.
+
+\docs_write
+\direct_io_note
+
+\tparam "class T" Any type.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param v Some item understood by `to_asio_buffers()`
+\param _length The length of the item
+\param _where The file offset to do the i/o
+\ingroup write
+\qbk{distinguish, length specifying non throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if writing data is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+template<class T> inline void write(error_code &_ec, future<> _precondition, T &&v, size_t _length, off_t _where)
+{
+ detail::async_write(std::forward<T>(v), _length, _where)(std::move(_precondition)).get_handle(_ec);
+}
+
+
+/*! \brief Asynchronous file length truncation after a preceding operation.
+
+\docs_truncate
+
+\return A future<void>
+\param _precondition The precondition to use.
+\param newsize The new size for the file.
+\ingroup truncate
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+inline future<> async_truncate(future<> _precondition, off_t newsize)
+{
+ return detail::async_truncate(newsize)(std::move(_precondition));
+}
+/*! \brief Synchronous file length truncation after a preceding operation.
+
+\docs_truncate
+
+\param _precondition The precondition to use.
+\param newsize The new size for the file.
+\ingroup truncate
+\qbk{distinguish, throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+inline void truncate(future<> _precondition, off_t newsize)
+{
+ auto h=detail::async_truncate(newsize)(std::move(_precondition)).get_handle();
+}
+/*! \brief Synchronous file length truncation after a preceding operation.
+
+\docs_truncate
+
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param newsize The new size for the file.
+\ingroup truncate
+\qbk{distinguish, non throwing}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete if truncating file lengths is constant time.}
+\exceptionmodelfree
+\qexample{readwrite_example}
+*/
+inline void truncate(error_code &_ec, future<> _precondition, off_t newsize)
+{
+ auto h=detail::async_truncate(newsize)(std::move(_precondition)).get_handle(_ec);
+}
+
+
+/*! \brief Asynchronous directory enumeration after a preceding operation.
+
+\docs_enumerate
+
+\return A `future<std::pair<std::vector<directory_entry>, bool>>`
+\param _precondition The precondition to use.
+\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+\param _restart Restarts the enumeration for this open directory handle.
+\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+\param _filtering Any filtering you want AFIO to do for you.
+\ingroup enumerate
+\qbk{distinguish, maxitems first}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
+\exceptionmodelfree
+\qexample{enumerate_example}
+*/
+inline future<std::pair<std::vector<directory_entry>, bool>> async_enumerate(future<> _precondition, size_t _maxitems = 2, bool _restart = true, path _glob = path(),
+ metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
+{
+ return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition));
+}
+/*! \brief Synchronous directory enumeration after a preceding operation.
+
+\docs_enumerate
+
+\return A vector of results and a bool indicating if there is more.
+\param _precondition The precondition to use.
+\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+\param _restart Restarts the enumeration for this open directory handle.
+\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+\param _filtering Any filtering you want AFIO to do for you.
+\ingroup enumerate
+\qbk{distinguish, max items first throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
+\exceptionmodelfree
+\qexample{enumerate_example}
+*/
+inline std::pair<std::vector<directory_entry>, bool> enumerate(future<> _precondition, size_t _maxitems = 2, bool _restart = true, path _glob = path(),
+ metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
+{
+ return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)).get();
+}
+/*! \brief Synchronous directory enumeration after a preceding operation.
+
+\docs_enumerate
+
+\return A vector of results and a bool indicating if there is more.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+\param _restart Restarts the enumeration for this open directory handle.
+\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+\param _filtering Any filtering you want AFIO to do for you.
+\ingroup enumerate
+\qbk{distinguish, max items first non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
+\exceptionmodelfree
+\qexample{enumerate_example}
+*/
+inline std::pair<std::vector<directory_entry>, bool> enumerate(error_code &_ec, future<> _precondition, size_t _maxitems = 2, bool _restart = true, path _glob = path(),
+ metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
+{
+ auto ret= detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition));
+ if(!(_ec=ret.get_error()))
+ return ret.get();
+ return std::pair<std::vector<directory_entry>, bool>();
+}
+/*! \brief Asynchronous directory enumeration after a preceding operation.
+
+\docs_enumerate
+
+\return A `future<std::pair<std::vector<directory_entry>, bool>>`
+\param _precondition The precondition to use.
+\param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+\param _restart Restarts the enumeration for this open directory handle.
+\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+\param _filtering Any filtering you want AFIO to do for you.
+\ingroup enumerate
+\qbk{distinguish, glob first}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
+\exceptionmodelfree
+\qexample{enumerate_example}
+*/
+inline future<std::pair<std::vector<directory_entry>, bool>> async_enumerate(future<> _precondition, path _glob, size_t _maxitems = 2, bool _restart = true,
+ metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
+{
+ return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition));
+}
+/*! \brief Synchronous directory enumeration after a preceding operation.
+
+\docs_enumerate
+
+\return A vector of results and a bool indicating if there is more.
+\param _precondition The precondition to use.
+\param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+\param _restart Restarts the enumeration for this open directory handle.
+\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+\param _filtering Any filtering you want AFIO to do for you.
+\ingroup enumerate
+\qbk{distinguish, glob first throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
+\exceptionmodelfree
+\qexample{enumerate_example}
+*/
+inline std::pair<std::vector<directory_entry>, bool> enumerate(future<> _precondition, path _glob, size_t _maxitems = 2, bool _restart = true,
+ metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
+{
+ return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)).get();
+}
+/*! \brief Synchronous directory enumeration after a preceding operation.
+
+\docs_enumerate
+
+\return A vector of results and a bool indicating if there is more.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param _glob A shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+\param _restart Restarts the enumeration for this open directory handle.
+\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+\param _filtering Any filtering you want AFIO to do for you.
+\ingroup enumerate
+\qbk{distinguish, glob first non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
+\exceptionmodelfree
+\qexample{enumerate_example}
+*/
+inline std::pair<std::vector<directory_entry>, bool> enumerate(error_code &_ec, future<> _precondition, path _glob, size_t _maxitems = 2, bool _restart = true,
+ metadata_flags _metadata = metadata_flags::None, enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
+{
+ auto ret = detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition));
+ if (!(_ec = ret.get_error()))
+ return ret.get();
+ return std::pair<std::vector<directory_entry>, bool>();
+}
+/*! \brief Asynchronous directory enumeration after a preceding operation.
+
+\docs_enumerate
+
+\return A `future<std::pair<std::vector<directory_entry>, bool>>`
+\param _precondition The precondition to use.
+\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+\param _restart Restarts the enumeration for this open directory handle.
+\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+\param _filtering Any filtering you want AFIO to do for you.
+\ingroup enumerate
+\qbk{distinguish, metadata first}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
+\exceptionmodelfree
+\qexample{enumerate_example}
+*/
+inline future<std::pair<std::vector<directory_entry>, bool>> async_enumerate(future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2, bool _restart = true,
+ path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
+{
+ return detail::async_enumerate(_maxitems, _restart, _glob, _metadata, _filtering)(std::move(_precondition));
+}
+/*! \brief Synchronous directory enumeration after a preceding operation.
+
+\docs_enumerate
+
+\return A vector of results and a bool indicating if there is more.
+\param _precondition The precondition to use.
+\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+\param _restart Restarts the enumeration for this open directory handle.
+\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+\param _filtering Any filtering you want AFIO to do for you.
+\ingroup enumerate
+\qbk{distinguish, metadata first throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
+\exceptionmodelfree
+\qexample{enumerate_example}
+*/
+inline std::pair<std::vector<directory_entry>, bool> enumerate(future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2,
+ bool _restart = true, path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
+{
+ return detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition)).get();
+}
+/*! \brief Synchronous directory enumeration after a preceding operation.
+
+\docs_enumerate
+
+\return A vector of results and a bool indicating if there is more.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param _metadata The metadata to prefetch for each item enumerated. AFIO may fetch more metadata than requested if it is cost free.
+\param _maxitems The maximum number of items to return in this request. Note that setting to one will often invoke two syscalls.
+\param _restart Restarts the enumeration for this open directory handle.
+\param _glob An optional shell glob by which to filter the items returned. Done kernel side on Windows, user side on POSIX.
+\param _filtering Any filtering you want AFIO to do for you.
+\ingroup enumerate
+\qbk{distinguish, metadata first non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Race free per batch of up to ['maxitems] for ino and type only. Remember that
+many filing systems will recycle inodes such that a created file will get the inode of a just deleted file, so
+comparing inodes for equivalence to a direntry() won't help you.]
+[raceguarantee Windows..Race free per batch of up to ['maxitems] for ino, type, atim, mtim, ctim, size, allocated,
+birthtim, sparse, compressed.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of entries in each directory.}
+\exceptionmodelfree
+\qexample{enumerate_example}
+*/
+inline std::pair<std::vector<directory_entry>, bool> enumerate(error_code &_ec, future<> _precondition, metadata_flags _metadata, size_t _maxitems = 2,
+ bool _restart = true, path _glob = path(), enumerate_req::filter _filtering = enumerate_req::filter::fastdeleted)
+{
+ auto ret = detail::async_enumerate(_maxitems, _restart, std::move(_glob), _metadata, _filtering)(std::move(_precondition));
+ if (!(_ec = ret.get_error()))
+ return ret.get();
+ return std::pair<std::vector<directory_entry>, bool>();
+}
+
+
+/*! \brief Asynchronous extent enumeration after a preceding operation.
+
+\docs_extents
+
+\return A `future<std::vector<std::pair<off_t, off_t>>>`
+\param _precondition The precondition to use.
+\ingroup extents
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
+before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
+into single extents.]
+[raceguarantee Windows..Race free.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.}
+\exceptionmodelfree
+\qexample{extents_example}
+*/
+inline future<std::vector<std::pair<off_t, off_t>>> async_extents(future<> _precondition)
+{
+ return detail::async_extents()(std::move(_precondition));
+}
+/*! \brief Synchronous extent enumeration after a preceding operation.
+
+\docs_extents
+
+\return A vector of extents
+\param _precondition The precondition to use.
+\ingroup extents
+\qbk{distinguish, throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
+before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
+into single extents.]
+[raceguarantee Windows..Race free.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.}
+\exceptionmodelfree
+\qexample{extents_example}
+*/
+inline std::vector<std::pair<off_t, off_t>> extents(future<> _precondition)
+{
+ return detail::async_extents()(std::move(_precondition)).get();
+}
+/*! \brief Synchronous extent enumeration after a preceding operation.
+
+\docs_extents
+
+\return A vector of extents
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\ingroup extents
+\qbk{distinguish, non throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, Linux, OS X..Very racy, even individual extent offset and length can race. The following filters are applied
+before returning results: (i) Any extent whose end appears before its start is retried (ii) Sequences of contiguous extents are merged
+into single extents.]
+[raceguarantee Windows..Race free.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(M) to complete where M is the average number of extents in each file.}
+\exceptionmodelfree
+\qexample{extents_example}
+*/
+inline std::vector<std::pair<off_t, off_t>> extents(error_code &_ec, future<> _precondition)
+{
+ auto ret = detail::async_extents()(std::move(_precondition));
+ if (!(_ec = ret.get_error()))
+ return ret.get();
+ return std::vector<std::pair<off_t, off_t>>();
+}
+
+
+/*! \brief Asynchronous volume enumeration after a preceding operation.
+
+\docs_statfs
+
+\return A `future<statfs_t>`
+\param _precondition The precondition to use.
+\param req A metadata request.
+\ingroup statfs
+\raceguarantees{
+[raceguarantee FreeBSD, OS X..Race free.]
+[raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
+flags.rdonly, flags.noexec, flags.nosuid.]
+[raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
+is fetched separately.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
+\exceptionmodelfree
+\qexample{statfs_example}
+*/
+inline future<statfs_t> async_statfs(future<> _precondition, fs_metadata_flags req)
+{
+ return detail::async_statfs(req)(std::move(_precondition));
+}
+/*! \brief Synchronous volume enumeration after a preceding operation.
+
+\docs_statfs
+
+\return The volume metadata requested.
+\param _precondition The precondition to use.
+\param req A metadata request.
+\ingroup statfs
+\qbk{distinguish, throwing}
+\raceguarantees{
+[raceguarantee FreeBSD, OS X..Race free.]
+[raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
+flags.rdonly, flags.noexec, flags.nosuid.]
+[raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
+is fetched separately.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
+\exceptionmodelfree
+\qexample{statfs_example}
+*/
+inline statfs_t statfs(future<> _precondition, fs_metadata_flags req)
+{
+ return detail::async_statfs(req)(std::move(_precondition)).get();
+}
+/*! \brief Synchronous volume enumeration after a preceding operation.
+
+\docs_statfs
+
+\return The volume metadata requested.
+\param _ec Error code to set.
+\param _precondition The precondition to use.
+\param req A metadata request.
+\ingroup statfs
+\qbk{distinguish, nonthrowing}
+\raceguarantees{
+[raceguarantee FreeBSD, OS X..Race free.]
+[raceguarantee Linux..The following items are fetched in a single snapshot: bsize, iosize, blocks, bfree, bavail, files, ffree, namemax, fsid,
+flags.rdonly, flags.noexec, flags.nosuid.]
+[raceguarantee Windows..The following snapshot categories apply: (i) flags, namemax, fstypename (ii) bsize, blocks, bfree, bavail. Everything else
+is fetched separately.]
+}
+\complexity{Amortised O(1) to dispatch. Amortised O(1) to complete.}
+\exceptionmodelfree
+\qexample{statfs_example}
+*/
+inline statfs_t statfs(error_code &_ec, future<> _precondition, fs_metadata_flags req)
+{
+ auto ret = detail::async_statfs(req)(std::move(_precondition));
+ if (!(_ec = ret.get_error()))
+ return ret.get();
+ return statfs_t();
+}
+
+/*! \brief Make ready a future after a precondition future readies.
+
+\return A future which returns out after precondition signals.
+\param precondition The future which must signal before the returned future signals.
+\param out The future to return.
+\ingroup async_file_io_dispatcher
+*/
+inline future<> depends(future<> precondition, future<> out)
+{
+ return precondition.parent()->depends(precondition, out);
+}
+
+//! Utility routines often useful when using AFIO
+namespace utils
+{
+ /*! \brief Returns the page sizes of this architecture which is useful for calculating direct i/o multiples.
+
+ \param only_actually_available Only return page sizes actually available to the user running this process
+ \return The page sizes of this architecture.
+ \ingroup utils
+ \complexity{Whatever the system API takes (one would hope constant time).}
+ \exceptionmodel{Any error from the operating system or std::bad_alloc.}
+ */
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC std::vector<size_t> page_sizes(bool only_actually_available = true) noexcept;
+
+ /*! \brief Returns a reasonable default size for page_allocator, typically the closest page size from
+ page_sizes() to 1Mb.
+
+ \return A value of a TLB large page size close to 1Mb.
+ \ingroup utils
+ \complexity{Whatever the system API takes (one would hope constant time).}
+ \exceptionmodel{Any error from the operating system or std::bad_alloc.}
+ */
+ inline size_t file_buffer_default_size() noexcept
+ {
+ static size_t size;
+ if (!size)
+ {
+ std::vector<size_t> sizes(page_sizes(true));
+ for (auto &i : sizes)
+ if (i >= 1024 * 1024)
+ {
+ size = i;
+ break;
+ }
+ if (!size)
+ size = 1024 * 1024;
+ }
+ return size;
+ }
+
+ /*! \brief Fills the buffer supplied with cryptographically strong randomness. Uses the OS kernel API.
+
+ \param buffer A buffer to fill
+ \param bytes How many bytes to fill
+ \ingroup utils
+ \complexity{Whatever the system API takes.}
+ \exceptionmodel{Any error from the operating system.}
+ */
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC void random_fill(char *buffer, size_t bytes);
+
+ /*! \brief Converts a number to a hex string. Out buffer can be same as in buffer.
+
+ Note that the character range used is a 16 item table of:
+
+ 0123456789abcdef
+
+ This lets one pack one byte of input into two bytes of output.
+
+ \ingroup utils
+ \complexity{O(N) where N is the length of the number.}
+ \exceptionmodel{Throws exception if output buffer is too small for input.}
+ */
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 6293) // MSVC sanitiser warns that we wrap n in the for loop
+#endif
+ inline size_t to_hex_string(char *out, size_t outlen, const char *_in, size_t inlen)
+ {
+ unsigned const char *in = (unsigned const char *) _in;
+ static BOOST_CONSTEXPR_OR_CONST char table[] = "0123456789abcdef";
+ if(outlen<inlen*2)
+ BOOST_AFIO_THROW(std::invalid_argument("Output buffer too small."));
+ for (size_t n = inlen - 2; n <= inlen - 2; n-=2)
+ {
+ out[n * 2 + 3] = table[(in[n+1] >> 4) & 0xf];
+ out[n * 2 + 2] = table[in[n+1] & 0xf];
+ out[n * 2 + 1] = table[(in[n] >> 4) & 0xf];
+ out[n * 2 + 0] = table[in[n] & 0xf];
+ }
+ if(inlen&1)
+ {
+ out[1] = table[(in[0] >> 4) & 0xf];
+ out[0] = table[in[0] & 0xf];
+ }
+ return inlen*2;
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ //! \overload
+ inline std::string to_hex_string(std::string in)
+ {
+ std::string out(in.size() * 2, ' ');
+ to_hex_string(const_cast<char *>(out.data()), out.size(), in.data(), in.size());
+ return out;
+ }
+
+ /*! \brief Converts a hex string to a number. Out buffer can be same as in buffer.
+
+ Note that this routine is about 43% slower than to_hex_string(), half of which is due to input validation.
+
+ \ingroup utils
+ \complexity{O(N) where N is the length of the string.}
+ \exceptionmodel{Throws exception if output buffer is too small for input or input size is not multiple of two.}
+ */
+ inline size_t from_hex_string(char *out, size_t outlen, const char *in, size_t inlen)
+ {
+ if (inlen % 2)
+ BOOST_AFIO_THROW(std::invalid_argument("Input buffer not multiple of two."));
+ if (outlen<inlen / 2)
+ BOOST_AFIO_THROW(std::invalid_argument("Output buffer too small."));
+ bool is_invalid=false;
+ auto fromhex = [&is_invalid](char c) -> unsigned char
+ {
+#if 1
+ // ASCII starting from 48 is 0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
+ // 48 65 97
+ static BOOST_CONSTEXPR_OR_CONST unsigned char table[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, // +10 = 58
+ 255, 255, 255, 255, 255, 255, 255, // +7 = 65
+ 10, 11, 12, 13, 14, 15, // +6 = 71
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, // +26 = 97
+ 10, 11, 12, 13, 14, 15
+ };
+ unsigned char r=255;
+ if(c>=48 && c<=102)
+ r=table[c-48];
+ if(r==255)
+ is_invalid=true;
+ return r;
+#else
+ if(c>='0' && c<='9')
+ return c-'0';
+ if(c>='a' && c<='f')
+ return c-'a'+10;
+ if(c>='A' && c<='F')
+ return c-'A'+10;
+ BOOST_AFIO_THROW(std::invalid_argument("Input is not hexadecimal."));
+#endif
+ };
+ for (size_t n = 0; n<inlen/2; n+=4)
+ {
+ unsigned char c[8];
+ c[0]= fromhex(in[n * 2]);
+ c[1]= fromhex(in[n * 2 + 1]);
+ c[2]= fromhex(in[n * 2 + 2]);
+ c[3]= fromhex(in[n * 2 + 3]);
+ out[n]=(c[1]<<4)|c[0];
+ c[4]= fromhex(in[n * 2 + 4]);
+ c[5]= fromhex(in[n * 2 + 5]);
+ out[n+1]=(c[3]<<4)|c[2];
+ c[6]= fromhex(in[n * 2 + 6]);
+ c[7]= fromhex(in[n * 2 + 7]);
+ out[n+2]=(c[5]<<4)|c[4];
+ out[n+3]=(c[7]<<4)|c[6];
+ }
+ for (size_t n = inlen/2-(inlen/2)%4; n<inlen/2; n++)
+ {
+ unsigned char c1 = fromhex(in[n * 2]), c2 = fromhex(in[n * 2 + 1]);
+ out[n]=(c2<<4)|c1;
+ }
+ if(is_invalid)
+ BOOST_AFIO_THROW(std::invalid_argument("Input is not hexadecimal."));
+ return inlen/2;
+ }
+
+ /*! \brief Returns a cryptographically random string capable of being used as a filename. Essentially random_fill() + to_hex_string().
+
+ \param randomlen The number of bytes of randomness to use for the string.
+ \return A string representing the randomness at a 2x ratio, so if 32 bytes were requested, this string would be 64 bytes long.
+ \ingroup utils
+ \complexity{Whatever the system API takes.}
+ \exceptionmodel{Any error from the operating system.}
+ */
+ inline std::string random_string(size_t randomlen)
+ {
+ size_t outlen = randomlen*2;
+ std::string ret(outlen, 0);
+ random_fill(const_cast<char *>(ret.data()), randomlen);
+ to_hex_string(const_cast<char *>(ret.data()), outlen, ret.data(), randomlen);
+ return ret;
+ }
+
+#ifndef BOOST_AFIO_SECDEC_INTRINSICS
+# if defined(__GCC__) || defined(__clang__)
+# define BOOST_AFIO_SECDEC_INTRINSICS 1
+# elif defined(_MSC_VER) && (defined(_M_X64) || _M_IX86_FP==1)
+# define BOOST_AFIO_SECDEC_INTRINSICS 1
+# endif
+#endif
+#ifndef BOOST_AFIO_SECDEC_INTRINSICS
+# define BOOST_AFIO_SECDEC_INTRINSICS 0
+#endif
+ /*! \class secded_ecc
+ \brief Calculates the single error correcting double error detecting (SECDED) Hamming Error Correcting Code for a \em blocksize block of bytes. For example, a secdec_ecc<8> would be the very common 72,64 Hamming code used in ECC RAM, or secdec_ecc<4096> would be for a 32784,32768 Hamming code.
+
+ Did you know that some non-ECC RAM systems can see 1e-12 flips/bit/hour, which is 3.3 bits flipped in a 16Gb RAM system
+ per 24 hours). See Schroeder, Pinheiro and Weber (2009) 'DRAM Errors in the Wild: A Large-Scale Field Study'.
+
+ After construction during which lookup tables are built, no state is modified and therefore this class is safe for static
+ storage (indeed if C++ 14 is available, the constructor is constexpr). The maximum number of bits in a code is a good four
+ billion, I did try limiting it to 65536 for performance but it wasn't worth it, and one might want > 8Kb blocks maybe.
+ As with all SECDED ECC, undefined behaviour occurs when more than two bits of error are present or the ECC supplied
+ is incorrect. You should combine this SECDED with a robust hash which can tell you definitively if a buffer is error
+ free or not rather than relying on this to correctly do so.
+
+ The main intended use case for this routine is calculating the ECC on data being written to disc, and hence that is
+ where performance has been maximised. It is not expected that this routine will be frequently called on data being read
+ from disc i.e. only when its hash doesn't match its contents which should be very rare, and then a single bit heal using this routine is attempted
+ before trying again with the hash. Care was taken that really enormous SECDEDs are fast, in fact tuning was mostly
+ done for the 32784,32768 code which can heal one bad bit per 4Kb page as the main thing we have in mind is achieving
+ reliable filing system code on computers without ECC RAM and in which sustained large quantities of random disc i/o produce
+ a worrying number of flipped bits in a 24 hour period (anywhere between 0 and 3 on my hardware here, average is about 0.8).
+
+ Performance of the fixed block size routine where you supply whole chunks of \em blocksize is therefore \b particularly excellent
+ as I spent a lot of time tuning it for Ivy Bridge and later out of order architectures: an
+ amazing 22 cycles per byte for the 32784,32768 code, which is a testament to modern out of order CPUs (remember SECDED inherently must work a bit
+ at a time, so that's just 2.75 amortised CPU cycles per bit which includes a table load, a bit test, and a conditional XOR)
+ i.e. it's pushing about 1.5 ops per clock cycle. On my 3.9Ghz i7-3770K here, I see about 170Mb/sec per CPU core.
+
+ The variable length routine is necessarily much slower as it must work in single bytes, and achieves 72 cycles per byte,
+ or 9 cycles per bit (64Mb/sec per CPU core).
+
+ \ingroup utils
+ \complexity{O(N) where N is the blocksize}
+ \exceptionmodel{Throws constexpr exceptions in constructor only, otherwise entirely noexcept.}
+ */
+ template<size_t blocksize> class secded_ecc
+ {
+ public:
+ typedef unsigned int result_type; //!< The largest ECC which can be calculated
+ private:
+ static BOOST_CONSTEXPR_OR_CONST size_t bits_per_byte=8;
+ typedef unsigned char unit_type; // The batch unit of processing
+ result_type bitsvalid;
+ // Many CPUs (x86) are slow doing variable bit shifts, so keep a table
+ result_type ecc_twospowers[sizeof(result_type)*bits_per_byte];
+ unsigned short ecc_table[blocksize*bits_per_byte];
+ static bool _is_single_bit_set(result_type x)
+ {
+#ifndef _MSC_VER
+#if defined(__i386__) || defined(__x86_64__)
+#ifndef __SSE4_2__
+ // Do a once off runtime check
+ static int have_popcnt=[]{
+ size_t cx, dx;
+#if defined(__x86_64__)
+ asm("cpuid": "=c" (cx), "=d" (dx) : "a" (1), "b" (0), "c" (0), "d" (0));
+#else
+ asm("pushl %%ebx\n\tcpuid\n\tpopl %%ebx\n\t": "=c" (cx), "=d" (dx) : "a" (1), "c" (0), "d" (0));
+#endif
+ return (dx&(1<<26))!=0/*SSE2*/ && (cx&(1<<23))!=0/*POPCNT*/;
+ }();
+ if(have_popcnt)
+#endif
+ {
+ unsigned count;
+ asm("popcnt %1,%0" : "=r"(count) : "rm"(x) : "cc");
+ return count==1;
+ }
+#endif
+ return __builtin_popcount(x)==1;
+#else
+ x -= (x >> 1) & 0x55555555;
+ x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
+ x = (x + (x >> 4)) & 0x0f0f0f0f;
+ unsigned int count=(x * 0x01010101)>>24;
+ return count==1;
+#if 0
+ x -= (x >> 1) & 0x5555555555555555ULL;
+ x = (x & 0x3333333333333333ULL) + ((x >> 2) & 0x3333333333333333ULL);
+ x = (x + (x >> 4)) & 0x0f0f0f0f0f0f0f0fULL;
+ unsigned long long count=(x * 0x0101010101010101ULL)>>56;
+ return count==1;
+#endif
+#endif
+ }
+ public:
+ //! Constructs an instance, configuring the necessary lookup tables
+ BOOST_CXX14_CONSTEXPR secded_ecc()
+ {
+ for(size_t n=0; n<sizeof(result_type)*bits_per_byte; n++)
+ ecc_twospowers[n]=((result_type)1<<n);
+ result_type length=blocksize*bits_per_byte;
+ // This is (data bits + parity bits + 1) <= 2^(parity bits)
+ for(result_type p=1; p<sizeof(result_type)*bits_per_byte; p++)
+ if((length+p+1)<=ecc_twospowers[p])
+ {
+ bitsvalid=p;
+ break;
+ }
+ if((bits_per_byte-1+bitsvalid)/bits_per_byte>sizeof(result_type))
+ BOOST_AFIO_THROW(std::runtime_error("ECC would exceed the size of result_type!"));
+ for(result_type i=0; i<blocksize*bits_per_byte; i++)
+ {
+ // Make a code bit
+ result_type b=i+1;
+#if BOOST_AFIO_SECDEC_INTRINSICS && 0 // let constexpr do its thing
+#ifdef _MSC_VER
+ unsigned long _topbit;
+ _BitScanReverse(&_topbit, b);
+ result_type topbit=_topbit;
+#else
+ result_type topbit=bits_per_byte*sizeof(result_type)-__builtin_clz(b);
+#endif
+ b+=topbit;
+ if(b>=ecc_twospowers[topbit]) b++;
+ //while(b>ecc_twospowers(_topbit+1)) _topbit++;
+ //b+=_topbit;
+ //if(b>=ecc_twospowers(_topbit)) b++;
+#else
+ for(size_t p=0; ecc_twospowers[p]<(b+1); p++)
+ b++;
+#endif
+ ecc_table[i]=(unsigned short) b;
+ if(b>(unsigned short)-1)
+ BOOST_AFIO_THROW(std::runtime_error("Precalculated table has exceeded its bounds"));
+ }
+ }
+ //! The number of bits valid in result_type
+ constexpr result_type result_bits_valid() const noexcept
+ {
+ return bitsvalid;
+ }
+ //! Accumulate ECC from fixed size buffer
+ result_type operator()(result_type ecc, const char *buffer) const noexcept
+ {
+ if(blocksize<sizeof(unit_type)*8)
+ return (*this)(ecc, buffer, blocksize);
+ // Process in lumps of eight
+ const unit_type *_buffer=(const unit_type *) buffer;
+//#pragma omp parallel for reduction(^:ecc)
+ for(size_t i=0; i<blocksize; i+=sizeof(unit_type)*8)
+ {
+ union { unsigned long long v; unit_type c[8]; };
+ result_type prefetch[8];
+ v=*(unsigned long long *)(&_buffer[0+i/sizeof(unit_type)]); // min 1 cycle
+#define BOOST_AFIO_ROUND(n) \
+ prefetch[0]=ecc_table[(i+0)*8+n]; \
+ prefetch[1]=ecc_table[(i+1)*8+n]; \
+ prefetch[2]=ecc_table[(i+2)*8+n]; \
+ prefetch[3]=ecc_table[(i+3)*8+n]; \
+ prefetch[4]=ecc_table[(i+4)*8+n]; \
+ prefetch[5]=ecc_table[(i+5)*8+n]; \
+ prefetch[6]=ecc_table[(i+6)*8+n]; \
+ prefetch[7]=ecc_table[(i+7)*8+n]; \
+ if(c[0]&((unit_type)1<<n))\
+ ecc^=prefetch[0];\
+ if(c[1]&((unit_type)1<<n))\
+ ecc^=prefetch[1];\
+ if(c[2]&((unit_type)1<<n))\
+ ecc^=prefetch[2];\
+ if(c[3]&((unit_type)1<<n))\
+ ecc^=prefetch[3];\
+ if(c[4]&((unit_type)1<<n))\
+ ecc^=prefetch[4];\
+ if(c[5]&((unit_type)1<<n))\
+ ecc^=prefetch[5];\
+ if(c[6]&((unit_type)1<<n))\
+ ecc^=prefetch[6];\
+ if(c[7]&((unit_type)1<<n))\
+ ecc^=prefetch[7];
+ BOOST_AFIO_ROUND(0) // prefetch = min 8, bit test and xor = min 16, total = 24
+ BOOST_AFIO_ROUND(1)
+ BOOST_AFIO_ROUND(2)
+ BOOST_AFIO_ROUND(3)
+ BOOST_AFIO_ROUND(4)
+ BOOST_AFIO_ROUND(5)
+ BOOST_AFIO_ROUND(6)
+ BOOST_AFIO_ROUND(7)
+ #undef BOOST_AFIO_ROUND // total should be 1+(8*24/3)=65
+ }
+ return ecc;
+ }
+ result_type operator()(const char *buffer) const noexcept { return (*this)(0, buffer); }
+ //! Accumulate ECC from partial buffer where \em length <= \em blocksize
+ result_type operator()(result_type ecc, const char *buffer, size_t length) const noexcept
+ {
+ const unit_type *_buffer=(const unit_type *) buffer;
+//#pragma omp parallel for reduction(^:ecc)
+ for(size_t i=0; i<length; i+=sizeof(unit_type))
+ {
+ unit_type c=_buffer[i/sizeof(unit_type)]; // min 1 cycle
+ if(!c) // min 1 cycle
+ continue;
+ char bitset[bits_per_byte*sizeof(unit_type)];
+ result_type prefetch[bits_per_byte*sizeof(unit_type)];
+ // Most compilers will roll this out
+ for(size_t n=0; n<bits_per_byte*sizeof(unit_type); n++) // min 16 cycles
+ {
+ bitset[n]=!!(c&((unit_type)1<<n));
+ prefetch[n]=ecc_table[i*bits_per_byte+n]; // min 8 cycles
+ }
+ result_type localecc=0;
+ for(size_t n=0; n<bits_per_byte*sizeof(unit_type); n++)
+ {
+ if(bitset[n]) // min 8 cycles
+ localecc^=prefetch[n]; // min 8 cycles
+ }
+ ecc^=localecc; // min 1 cycle. Total cycles = min 43 cycles/byte
+ }
+ return ecc;
+ }
+ result_type operator()(const char *buffer, size_t length) const noexcept { return (*this)(0, buffer, length); }
+ //! Given the original ECC and the new ECC for a buffer, find the bad bit. Return (result_type)-1 if not found (e.g. ECC corrupt)
+ result_type find_bad_bit(result_type good_ecc, result_type bad_ecc) const noexcept
+ {
+ result_type length=blocksize*bits_per_byte, eccdiff=good_ecc^bad_ecc;
+ if(_is_single_bit_set(eccdiff))
+ return (result_type)-1;
+ for(result_type i=0, b=1; i<length; i++, b++)
+ {
+ // Skip parity bits
+ while(_is_single_bit_set(b))
+ b++;
+ if(b==eccdiff)
+ return i;
+ }
+ return (result_type)-1;
+ }
+ //! The outcomes from verify()
+ enum verify_status
+ {
+ corrupt=0, //!< The buffer had more than a single bit corrupted or the ECC was invalid
+ okay=1, //!< The buffer had no errors
+ healed=2 //!< The buffer was healed
+ };
+ //! Verifies and heals when possible a buffer, returning non zero if the buffer is error free
+ verify_status verify(char *buffer, result_type good_ecc) const noexcept
+ {
+ result_type this_ecc=(*this)(0, buffer);
+ if(this_ecc==good_ecc)
+ return verify_status::okay; // no errors
+ result_type badbit=find_bad_bit(good_ecc, this_ecc);
+ if((result_type)-1==badbit)
+ return verify_status::corrupt; // parity corrupt?
+ buffer[badbit/bits_per_byte]^=(unsigned char) ecc_twospowers[badbit%bits_per_byte];
+ this_ecc=(*this)(0, buffer);
+ if(this_ecc==good_ecc)
+ return healed; // error healed
+ // Put the bit back
+ buffer[badbit/bits_per_byte]^=(unsigned char) ecc_twospowers[badbit%bits_per_byte];
+ return verify_status::corrupt; // more than one bit was corrupt
+ }
+ };
+
+ namespace detail
+ {
+ struct large_page_allocation
+ {
+ void *p;
+ size_t page_size_used;
+ size_t actual_size;
+ large_page_allocation() : p(nullptr), page_size_used(0), actual_size(0) { }
+ large_page_allocation(void *_p, size_t pagesize, size_t actual) : p(_p), page_size_used(pagesize), actual_size(actual) { }
+ };
+ inline large_page_allocation calculate_large_page_allocation(size_t bytes)
+ {
+ large_page_allocation ret;
+ auto pagesizes(page_sizes());
+ do
+ {
+ ret.page_size_used=pagesizes.back();
+ pagesizes.pop_back();
+ } while(!pagesizes.empty() && !(bytes/ret.page_size_used));
+ ret.actual_size=(bytes+ret.page_size_used-1)&~(ret.page_size_used-1);
+ return ret;
+ }
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC large_page_allocation allocate_large_pages(size_t bytes);
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC void deallocate_large_pages(void *p, size_t bytes);
+ }
+ /*! \class page_allocator
+ \brief An STL allocator which allocates large TLB page memory.
+ \ingroup utils
+
+ If the operating system is configured to allow it, this type of memory is particularly efficient for doing
+ large scale file i/o. This is because the kernel must normally convert the scatter gather buffers you pass
+ into extended scatter gather buffers as the memory you see as contiguous may not, and probably isn't, actually be
+ contiguous in physical memory. Regions returned by this allocator \em may be allocated contiguously in physical
+ memory and therefore the kernel can pass through your scatter gather buffers unmodified.
+
+ A particularly useful combination with this allocator is with the page_sizes() member function of __afio_dispatcher__.
+ This will return which pages sizes are possible, and which page sizes are enabled for this user. If writing a
+ file copy routine for example, using this allocator with the largest page size as the copy chunk makes a great
+ deal of sense.
+
+ Be aware that as soon as the allocation exceeds a large page size, most systems allocate in multiples of the large
+ page size, so if the large page size were 2Mb and you allocate 2Mb + 1 byte, 4Mb is actually consumed.
+ */
+ template <typename T>
+ class page_allocator
+ {
+ public:
+ typedef T value_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef std::true_type propagate_on_container_move_assignment;
+ typedef std::true_type is_always_equal;
+
+ template <class U>
+ struct rebind { typedef page_allocator<U> other; };
+
+ page_allocator() noexcept
+ {}
+
+ template <class U>
+ page_allocator(const page_allocator<U>&) noexcept
+ {}
+
+ size_type
+ max_size() const noexcept
+ { return size_type(~0) / sizeof(T); }
+
+ pointer
+ address(reference x) const noexcept
+ { return std::addressof(x); }
+
+ const_pointer
+ address(const_reference x) const noexcept
+ { return std::addressof(x); }
+
+ pointer
+ allocate(size_type n, const void *hint = 0)
+ {
+ if(n>max_size())
+ throw std::bad_alloc();
+ auto mem(detail::allocate_large_pages(n * sizeof(T)));
+ if (mem.p == nullptr)
+ throw std::bad_alloc();
+ return reinterpret_cast<pointer>(mem.p);
+ }
+
+ void
+ deallocate(pointer p, size_type n)
+ {
+ if(n>max_size())
+ throw std::bad_alloc();
+ detail::deallocate_large_pages(p, n * sizeof(T));
+ }
+
+ template <class U, class ...Args>
+ void
+ construct(U* p, Args&&... args)
+ { ::new(reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...); }
+
+ template <class U> void
+ destroy(U* p)
+ { p->~U(); }
+ };
+ template <>
+ class page_allocator<void>
+ {
+ public:
+ typedef void value_type;
+ typedef void* pointer;
+ typedef const void* const_pointer;
+ typedef std::true_type propagate_on_container_move_assignment;
+ typedef std::true_type is_always_equal;
+
+ template <class U>
+ struct rebind { typedef page_allocator<U> other; };
+ };
+ template<class T, class U> inline bool operator==(const page_allocator<T> &, const page_allocator<U> &) noexcept { return true; }
+}
+
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+// Specialise std::hash<> for directory_entry
+#ifndef BOOST_AFIO_DISABLE_STD_HASH_SPECIALIZATION
+#include <functional>
+namespace std
+{
+ template<> struct hash<BOOST_AFIO_V2_NAMESPACE::path>
+ {
+ public:
+ size_t operator()(const BOOST_AFIO_V2_NAMESPACE::path &p) const
+ {
+ return BOOST_AFIO_V2_NAMESPACE::path_hash()(p);
+ }
+ };
+ template<> struct hash<BOOST_AFIO_V2_NAMESPACE::directory_entry>
+ {
+ public:
+ size_t operator()(const BOOST_AFIO_V2_NAMESPACE::directory_entry &p) const
+ {
+ return BOOST_AFIO_V2_NAMESPACE::directory_entry_hash()(p);
+ }
+ };
+
+}//namesapce std
+#endif
+
+#ifdef BOOST_MSVC
+#pragma warning(pop)
+#endif
+
+#if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#undef BOOST_AFIO_VALIDATE_INPUTS // Let BOOST_AFIO_NEVER_VALIDATE_INPUTS take over
+#define BOOST_AFIO_HEADER_INCLUDED 1
+#include "detail/impl/afio.ipp"
+#undef BOOST_AFIO_HEADER_INCLUDED
+#endif
+
+#endif
+#endif
diff --git a/attic/include/boost/afio/v2/config.hpp b/attic/include/boost/afio/v2/config.hpp
new file mode 100644
index 00000000..35dda16d
--- /dev/null
+++ b/attic/include/boost/afio/v2/config.hpp
@@ -0,0 +1,491 @@
+/*
+* File: config.hpp
+* Author: Paul Kirth
+*
+* Created on June 18, 2013, 7:30 PM
+
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#if !defined(BOOST_AFIO_HEADERS_ONLY) && !defined(BOOST_ALL_DYN_LINK)
+#define BOOST_AFIO_HEADERS_ONLY 1
+#endif
+
+// Fix up mingw weirdness
+#if !defined(WIN32) && defined(_WIN32)
+# define WIN32 1
+#endif
+// Boost ASIO needs this
+#if !defined(_WIN32_WINNT) && defined(WIN32)
+# define _WIN32_WINNT 0x0501
+#endif
+#if defined(WIN32) && _WIN32_WINNT<0x0501
+# error _WIN32_WINNT must at least be set to Windows XP for Boost ASIO to compile
+#endif
+// Get Mingw to assume we are on at least Windows 2000
+#if __MSVCRT_VERSION__ < 0x601
+# undef __MSVCRT_VERSION__
+# define __MSVCRT_VERSION__ 0x601
+#endif
+// Pull in detection of __MINGW64_VERSION_MAJOR
+#ifdef __MINGW32__
+# include <_mingw.h>
+#endif
+
+#include "../bindlib/include/import.h"
+
+#ifndef __cpp_exceptions
+# error Boost.AFIO needs C++ exceptions to be turned on
+#endif
+#ifndef __cpp_alias_templates
+# error Boost.AFIO needs template alias support in the compiler
+#endif
+#ifndef __cpp_variadic_templates
+# error Boost.AFIO needs variadic template support in the compiler
+#endif
+#ifndef __cpp_noexcept
+# error Boost.AFIO needs noexcept support in the compiler
+#endif
+#ifndef __cpp_constexpr
+# error Boost.AFIO needs constexpr (C++ 11) support in the compiler
+#endif
+#ifndef __cpp_thread_local
+# error Boost.AFIO needs thread_local support in the compiler
+#endif
+
+#if defined(BOOST_AFIO_LATEST_VERSION) && BOOST_AFIO_LATEST_VERSION < 2
+# error You need to include the latest version of Boost.AFIO before any earlier versions within the same translation unit
+#endif
+#ifndef BOOST_AFIO_LATEST_VERSION
+# define BOOST_AFIO_LATEST_VERSION 2
+#endif
+
+#undef BOOST_AFIO_V2_STL11_IMPL
+#undef BOOST_AFIO_V2_FILESYSTEM_IMPL
+#undef BOOST_AFIO_V2_ASIO_IMPL
+#undef BOOST_AFIO_V2
+#undef BOOST_AFIO_V2_NAMESPACE
+#undef BOOST_AFIO_V2_NAMESPACE_BEGIN
+#undef BOOST_AFIO_V2_NAMESPACE_END
+
+// Default to the C++ 11 STL for atomic, chrono, mutex and thread except on Mingw32
+#if (defined(BOOST_AFIO_USE_BOOST_THREAD) && BOOST_AFIO_USE_BOOST_THREAD) || (defined(__MINGW32__) && !defined(__MINGW64__) && !defined(__MINGW64_VERSION_MAJOR))
+# if defined(BOOST_OUTCOME_USE_BOOST_THREAD) && BOOST_OUTCOME_USE_BOOST_THREAD != 1
+# error You must configure Boost.Monad and Boost.AFIO to both use Boost.Thread together or both not at all.
+# endif
+# define BOOST_OUTCOME_USE_BOOST_THREAD 1
+# define BOOST_AFIO_V2_STL11_IMPL boost
+# ifndef BOOST_THREAD_VERSION
+# define BOOST_THREAD_VERSION 3
+# endif
+# if BOOST_THREAD_VERSION < 3
+# error Boost.AFIO requires that Boost.Thread be configured to v3 or later
+# endif
+#else
+# if defined(BOOST_OUTCOME_USE_BOOST_THREAD) && BOOST_OUTCOME_USE_BOOST_THREAD != 0
+# error You must configure Boost.Monad and Boost.AFIO to both use Boost.Thread together or both not at all.
+# endif
+# define BOOST_OUTCOME_USE_BOOST_THREAD 0
+# define BOOST_AFIO_V2_STL11_IMPL std
+# ifndef BOOST_AFIO_USE_BOOST_THREAD
+# define BOOST_AFIO_USE_BOOST_THREAD 0
+# endif
+#endif
+// Default to the C++ 11 STL if on MSVC (Dinkumware ships a copy), else Boost
+#ifndef BOOST_AFIO_USE_BOOST_FILESYSTEM
+# if _MSC_VER >= 1900 // >= VS 14
+# define BOOST_AFIO_USE_BOOST_FILESYSTEM 0
+# endif
+#endif
+#ifndef BOOST_AFIO_USE_BOOST_FILESYSTEM
+# define BOOST_AFIO_USE_BOOST_FILESYSTEM 1
+#endif
+#if BOOST_AFIO_USE_BOOST_FILESYSTEM
+# define BOOST_AFIO_V2_FILESYSTEM_IMPL boost
+# define BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS 1
+#else
+# define BOOST_AFIO_V2_FILESYSTEM_IMPL std
+#endif
+#ifdef ASIO_STANDALONE
+# if defined(BOOST_OUTCOME_USE_BOOST_ERROR_CODE) && BOOST_OUTCOME_USE_BOOST_ERROR_CODE != 0
+# error You must configure Boost.Monad to use the STL error_code if using standalone ASIO
+# endif
+# define BOOST_OUTCOME_USE_BOOST_ERROR_CODE 0
+# define BOOST_AFIO_V2_ASIO_IMPL asio
+# undef BOOST_AFIO_USE_BOOST_ASIO
+# define BOOST_AFIO_USE_BOOST_ASIO 0
+#else
+# if defined(BOOST_OUTCOME_USE_BOOST_ERROR_CODE) && BOOST_OUTCOME_USE_BOOST_ERROR_CODE != 1
+# error You must configure Boost.Monad to use the Boost error code if using Boost.ASIO
+# endif
+# define BOOST_OUTCOME_USE_BOOST_ERROR_CODE 1
+# define BOOST_AFIO_V2_ASIO_IMPL boost
+# undef BOOST_AFIO_USE_BOOST_ASIO
+# define BOOST_AFIO_USE_BOOST_ASIO 1
+// Pull in Boost now before Monad does to avoid warnings caused by APIBind emulating Boost
+# include "boost/thread/future.hpp"
+#endif
+#if BOOST_AFIO_LATEST_VERSION == 2
+# define BOOST_AFIO_V2 (boost), (afio), (BOOST_BINDLIB_NAMESPACE_VERSION(v2, BOOST_AFIO_V2_STL11_IMPL, BOOST_AFIO_V2_FILESYSTEM_IMPL, BOOST_AFIO_V2_ASIO_IMPL), inline)
+#else
+# define BOOST_AFIO_V2 (boost), (afio), (BOOST_BINDLIB_NAMESPACE_VERSION(v2, BOOST_AFIO_V2_STL11_IMPL, BOOST_AFIO_V2_FILESYSTEM_IMPL, BOOST_AFIO_V2_ASIO_IMPL))
+#endif
+#define BOOST_AFIO_V2_NAMESPACE BOOST_BINDLIB_NAMESPACE (BOOST_AFIO_V2)
+#define BOOST_AFIO_V2_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2)
+#define BOOST_AFIO_V2_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2)
+
+// From automated matrix generator
+#undef BOOST_AFIO_NEED_DEFINE
+#undef BOOST_AFIO_NEED_DEFINE_DESCRIPTION
+#if !BOOST_AFIO_USE_BOOST_THREAD && !BOOST_AFIO_USE_BOOST_FILESYSTEM && !BOOST_AFIO_USE_BOOST_ASIO
+# ifndef BOOST_AFIO_NEED_DEFINE_000
+# define BOOST_AFIO_NEED_DEFINE_DESCRIPTION "BOOST_AFIO_USE_BOOST_THREAD=0 BOOST_AFIO_USE_BOOST_FILESYSTEM=0 BOOST_AFIO_USE_BOOST_ASIO=0"
+# define BOOST_AFIO_NEED_DEFINE_000
+# define BOOST_AFIO_NEED_DEFINE 1
+# endif
+#elif BOOST_AFIO_USE_BOOST_THREAD && !BOOST_AFIO_USE_BOOST_FILESYSTEM && !BOOST_AFIO_USE_BOOST_ASIO
+# ifndef BOOST_AFIO_NEED_DEFINE_100
+# define BOOST_AFIO_NEED_DEFINE_DESCRIPTION "BOOST_AFIO_USE_BOOST_THREAD=1 BOOST_AFIO_USE_BOOST_FILESYSTEM=0 BOOST_AFIO_USE_BOOST_ASIO=0"
+# define BOOST_AFIO_NEED_DEFINE_100
+# define BOOST_AFIO_NEED_DEFINE 1
+# endif
+#elif !BOOST_AFIO_USE_BOOST_THREAD && BOOST_AFIO_USE_BOOST_FILESYSTEM && !BOOST_AFIO_USE_BOOST_ASIO
+# ifndef BOOST_AFIO_NEED_DEFINE_010
+# define BOOST_AFIO_NEED_DEFINE_DESCRIPTION "BOOST_AFIO_USE_BOOST_THREAD=0 BOOST_AFIO_USE_BOOST_FILESYSTEM=1 BOOST_AFIO_USE_BOOST_ASIO=0"
+# define BOOST_AFIO_NEED_DEFINE_010
+# define BOOST_AFIO_NEED_DEFINE 1
+# endif
+#elif BOOST_AFIO_USE_BOOST_THREAD && BOOST_AFIO_USE_BOOST_FILESYSTEM && !BOOST_AFIO_USE_BOOST_ASIO
+# ifndef BOOST_AFIO_NEED_DEFINE_110
+# define BOOST_AFIO_NEED_DEFINE_DESCRIPTION "BOOST_AFIO_USE_BOOST_THREAD=1 BOOST_AFIO_USE_BOOST_FILESYSTEM=1 BOOST_AFIO_USE_BOOST_ASIO=0"
+# define BOOST_AFIO_NEED_DEFINE_110
+# define BOOST_AFIO_NEED_DEFINE 1
+# endif
+#elif !BOOST_AFIO_USE_BOOST_THREAD && !BOOST_AFIO_USE_BOOST_FILESYSTEM && BOOST_AFIO_USE_BOOST_ASIO
+# ifndef BOOST_AFIO_NEED_DEFINE_001
+# define BOOST_AFIO_NEED_DEFINE_DESCRIPTION "BOOST_AFIO_USE_BOOST_THREAD=0 BOOST_AFIO_USE_BOOST_FILESYSTEM=0 BOOST_AFIO_USE_BOOST_ASIO=1"
+# define BOOST_AFIO_NEED_DEFINE_001
+# define BOOST_AFIO_NEED_DEFINE 1
+# endif
+#elif BOOST_AFIO_USE_BOOST_THREAD && !BOOST_AFIO_USE_BOOST_FILESYSTEM && BOOST_AFIO_USE_BOOST_ASIO
+# ifndef BOOST_AFIO_NEED_DEFINE_101
+# define BOOST_AFIO_NEED_DEFINE_DESCRIPTION "BOOST_AFIO_USE_BOOST_THREAD=1 BOOST_AFIO_USE_BOOST_FILESYSTEM=0 BOOST_AFIO_USE_BOOST_ASIO=1"
+# define BOOST_AFIO_NEED_DEFINE_101
+# define BOOST_AFIO_NEED_DEFINE 1
+# endif
+#elif !BOOST_AFIO_USE_BOOST_THREAD && BOOST_AFIO_USE_BOOST_FILESYSTEM && BOOST_AFIO_USE_BOOST_ASIO
+# ifndef BOOST_AFIO_NEED_DEFINE_011
+# define BOOST_AFIO_NEED_DEFINE_DESCRIPTION "BOOST_AFIO_USE_BOOST_THREAD=0 BOOST_AFIO_USE_BOOST_FILESYSTEM=1 BOOST_AFIO_USE_BOOST_ASIO=1"
+# define BOOST_AFIO_NEED_DEFINE_011
+# define BOOST_AFIO_NEED_DEFINE 1
+# endif
+#elif BOOST_AFIO_USE_BOOST_THREAD && BOOST_AFIO_USE_BOOST_FILESYSTEM && BOOST_AFIO_USE_BOOST_ASIO
+# ifndef BOOST_AFIO_NEED_DEFINE_111
+# define BOOST_AFIO_NEED_DEFINE_DESCRIPTION "BOOST_AFIO_USE_BOOST_THREAD=1 BOOST_AFIO_USE_BOOST_FILESYSTEM=1 BOOST_AFIO_USE_BOOST_ASIO=1"
+# define BOOST_AFIO_NEED_DEFINE_111
+# define BOOST_AFIO_NEED_DEFINE 1
+# endif
+#endif
+
+#ifdef BOOST_AFIO_NEED_DEFINE
+#undef BOOST_AFIO_AFIO_H
+
+#define BOOST_STL11_ATOMIC_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL11_ATOMIC_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL11_ATOMIC_MAP_NO_ATOMIC_CHAR32_T // missing VS14
+#define BOOST_STL11_ATOMIC_MAP_NO_ATOMIC_CHAR16_T // missing VS14
+#define BOOST_STL11_CHRONO_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11, inline), (chrono))
+#define BOOST_STL11_CHRONO_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11, inline), (chrono))
+#define BOOST_STL11_CONDITION_VARIABLE_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL11_CONDITION_VARIABLE_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL1z_FILESYSTEM_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl1z, inline), (filesystem))
+#define BOOST_STL1z_FILESYSTEM_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl1z, inline), (filesystem))
+// Match Dinkumware's TR2 implementation
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_SYMLINK_OPTION
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_COPY_OPTION
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_CHANGE_EXTENSION
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_WRECURSIVE_DIRECTORY_ITERATOR
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_EXTENSION
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_TYPE_PRESENT
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_PORTABLE_FILE_NAME
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_PORTABLE_DIRECTORY_NAME
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_PORTABLE_POSIX_NAME
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_LEXICOGRAPHICAL_COMPARE
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_WINDOWS_NAME
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_PORTABLE_NAME
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_BASENAME
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_COMPLETE
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_IS_REGULAR
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_INITIAL_PATH
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_PERMISSIONS_PRESENT
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_CODECVT_ERROR_CATEGORY
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_WPATH
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_SYMBOLIC_LINK_EXISTS
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_COPY_DIRECTORY
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_NATIVE
+#define BOOST_STL1z_FILESYSTEM_MAP_NO_UNIQUE_PATH
+#define BOOST_STL11_FUTURE_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL11_FUTURE_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL11_FUTURE_MAP_NO_FUTURE
+#define BOOST_STL11_MUTEX_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL11_MUTEX_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL1z_NETWORKING_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl1z, inline), (asio))
+#define BOOST_STL1z_NETWORKING_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl1z, inline), (asio))
+#define BOOST_STL1z_NETWORKING_MAP_NO_V4_MAPPED_T
+#define BOOST_STL1z_NETWORKING_MAP_NO_EXECUTOR_ARG_T
+#define BOOST_STL1z_NETWORKING_MAP_NO_WRAP
+#define BOOST_STL1z_NETWORKING_MAP_NO_MAKE_WORK
+#define BOOST_STL1z_NETWORKING_MAP_NO_ASYNC_COMPLETION
+#define BOOST_STL1z_NETWORKING_MAP_NO_EXECUTOR_WORK
+#define BOOST_STL1z_NETWORKING_MAP_NO_EXECUTION_CONTEXT
+#define BOOST_STL1z_NETWORKING_MAP_NO_POST
+#define BOOST_STL1z_NETWORKING_MAP_NO_EXECUTOR
+#define BOOST_STL1z_NETWORKING_MAP_NO_USES_EXECUTOR
+#define BOOST_STL1z_NETWORKING_MAP_NO_BAD_ADDRESS_CAST
+#define BOOST_STL1z_NETWORKING_MAP_NO_BAD_EXECUTOR
+#define BOOST_STL1z_NETWORKING_MAP_NO_GET_ASSOCIATED_EXECUTOR
+#define BOOST_STL1z_NETWORKING_MAP_NO_ASSOCIATED_EXECUTOR
+#define BOOST_STL1z_NETWORKING_MAP_NO_GET_ASSOCIATED_ALLOCATOR
+#define BOOST_STL1z_NETWORKING_MAP_NO_ASSOCIATED_ALLOCATOR
+#define BOOST_STL1z_NETWORKING_MAP_NO_IS_EXECUTOR
+#define BOOST_STL1z_NETWORKING_MAP_NO_MAKE_ADDRESS_V4
+#define BOOST_STL1z_NETWORKING_MAP_NO_MAKE_ADDRESS_V6
+#define BOOST_STL1z_NETWORKING_MAP_NO_MAKE_ADDRESS
+#define BOOST_STL1z_NETWORKING_MAP_NO_ADDRESS_CAST
+#define BOOST_STL1z_NETWORKING_MAP_NO_DEFER
+#define BOOST_STL1z_NETWORKING_MAP_NO_DISPATCH
+#define BOOST_STL1z_NETWORKING_MAP_NO_THREAD_POOL
+#define BOOST_STL1z_NETWORKING_MAP_NO_IS_MUTABLE_BUFFER_SEQUENCE
+#define BOOST_STL1z_NETWORKING_MAP_NO_IS_CONST_BUFFER_SEQUENCE
+#define BOOST_STL1z_NETWORKING_MAP_NO_EXECUTOR_WRAPPER
+#define BOOST_STL1z_NETWORKING_MAP_NO_SYSTEM_EXECUTOR
+#define BOOST_STL1z_NETWORKING_MAP_NO_HANDLER_TYPE
+#define BOOST_STL1z_NETWORKING_MAP_NO_STRAND
+#define BOOST_STL1z_NETWORKING_MAP_NO_READ_SIZE_HELPER
+#define BOOST_STL11_RATIO_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL11_RATIO_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL11_THREAD_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11, inline))
+#define BOOST_STL11_THREAD_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11, inline))
+#include BOOST_BINDLIB_INCLUDE_STL11(../bindlib, BOOST_AFIO_V2_STL11_IMPL, atomic)
+#include BOOST_BINDLIB_INCLUDE_STL11(../bindlib, BOOST_AFIO_V2_STL11_IMPL, chrono)
+#include BOOST_BINDLIB_INCLUDE_STL11(../bindlib, BOOST_AFIO_V2_STL11_IMPL, condition_variable)
+#include BOOST_BINDLIB_INCLUDE_STL1z(../bindlib, BOOST_AFIO_V2_FILESYSTEM_IMPL, filesystem)
+#include BOOST_BINDLIB_INCLUDE_STL11(../bindlib, BOOST_AFIO_V2_STL11_IMPL, future)
+#include BOOST_BINDLIB_INCLUDE_STL11(../bindlib, BOOST_AFIO_V2_STL11_IMPL, mutex)
+#include BOOST_BINDLIB_INCLUDE_STL1z(../bindlib, BOOST_AFIO_V2_ASIO_IMPL, networking)
+#include BOOST_BINDLIB_INCLUDE_STL11(../bindlib, BOOST_AFIO_V2_STL11_IMPL, ratio)
+#include BOOST_BINDLIB_INCLUDE_STL11(../bindlib, BOOST_AFIO_V2_STL11_IMPL, thread)
+
+#define BOOST_STL1z_NETWORKING_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl1z, inline), (asio))
+#define BOOST_STL1z_NETWORKING_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl1z, inline), (asio))
+BOOST_STL1z_NETWORKING_MAP_NAMESPACE_BEGIN
+// Need to bind in asio::windows
+#ifdef WIN32
+# if !BOOST_AFIO_USE_BOOST_ASIO
+namespace error = ::asio::error;
+namespace windows = ::asio::windows;
+# else
+namespace error = ::boost::asio::error;
+namespace windows = ::boost::asio::windows;
+# endif
+#endif
+BOOST_STL1z_NETWORKING_MAP_NAMESPACE_END
+#undef BOOST_STL1z_NETWORKING_MAP_NAMESPACE_BEGIN
+#undef BOOST_STL1z_NETWORKING_MAP_NAMESPACE_END
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+// Map an error category
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+using std::to_string;
+
+#if BOOST_AFIO_USE_BOOST_THREAD
+//using boost::make_exception_ptr;
+template<class T> inline boost::exception_ptr make_exception_ptr(T e)
+{
+ try
+ {
+ throw e;
+ }
+ catch(...)
+ {
+ return boost::current_exception();
+ }
+}
+template<class _Res> using stl_future = ::boost::future<_Res>;
+#else
+using std::make_exception_ptr;
+template<class _Res> using stl_future = ::std::future<_Res>;
+#endif
+
+#if !BOOST_AFIO_USE_BOOST_ASIO
+typedef asio::error_code error_code;
+using std::generic_category;
+using std::system_category;
+using std::system_error;
+#else
+typedef boost::system::error_code error_code;
+using boost::system::generic_category;
+using boost::system::system_category;
+using boost::system::system_error;
+#endif
+#if defined(_MSC_VER) && 0
+// Stupid MSVC doesn't resolve namespace binds correctly ...
+#if !BOOST_AFIO_USE_BOOST_ASIO
+namespace asio {
+ namespace asio = ::asio;
+ namespace detail { namespace asio = ::asio; }
+ //namespace windows { namespace asio = ::asio; }
+}
+namespace asio_handler_cont_helpers { namespace asio = ::asio; }
+namespace asio_handler_alloc_helpers { namespace asio = ::asio; }
+namespace asio_handler_invoke_helpers { namespace asio = ::asio; }
+
+#else
+namespace boost { namespace asio {
+ namespace asio = ::boost::asio;
+ namespace detail { namespace asio = ::boost::asio; }
+ namespace windows { namespace asio = ::boost::asio; }
+} }
+#endif
+#endif
+BOOST_AFIO_V2_NAMESPACE_END
+#endif
+
+#include "../outcome/include/boost/outcome.hpp"
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+ using BOOST_OUTCOME_V1_NAMESPACE::is_lockable_locked;
+ using spins_to_sleep = BOOST_OUTCOME_V1_NAMESPACE::spins_to_sleep;
+ template<size_t _0> using spins_to_yield = BOOST_OUTCOME_V1_NAMESPACE::spins_to_yield<_0>;
+ template<size_t _0, bool _1=true> using spins_to_loop = BOOST_OUTCOME_V1_NAMESPACE::spins_to_loop<_0, _1>;
+ using null_spin_policy = BOOST_OUTCOME_V1_NAMESPACE::null_spin_policy;
+ template<class T> using spinlockbase = BOOST_OUTCOME_V1_NAMESPACE::spinlockbase<T>;
+ template<class T> using lockable_ptr = BOOST_OUTCOME_V1_NAMESPACE::lockable_ptr<T>;
+ template<typename T, template<class> class spinpolicy2=spins_to_loop<125>::policy, template<class> class spinpolicy3=spins_to_yield<250>::policy, template<class> class spinpolicy4=spins_to_sleep::policy> using spinlock = BOOST_OUTCOME_V1_NAMESPACE::spinlock<T, spinpolicy2, spinpolicy3, spinpolicy4>;
+ template<typename R> using outcome = BOOST_OUTCOME_V1_NAMESPACE::outcome<R>;
+ template<typename R> using result = BOOST_OUTCOME_V1_NAMESPACE::result<R>;
+ template<typename R> using option = BOOST_OUTCOME_V1_NAMESPACE::option<R>;
+ template<typename R> using lightweight_promise = BOOST_OUTCOME_V1_NAMESPACE::lightweight_futures::promise<R>;
+ template<typename R> using lightweight_future = BOOST_OUTCOME_V1_NAMESPACE::lightweight_futures::future<R>;
+ template<typename R> using is_lightweight_future = BOOST_OUTCOME_V1_NAMESPACE::lightweight_futures::is_future<R>;
+ using BOOST_OUTCOME_V1_NAMESPACE::empty;
+ using BOOST_OUTCOME_V1_NAMESPACE::make_option;
+ using BOOST_OUTCOME_V1_NAMESPACE::make_outcome;
+ using BOOST_OUTCOME_V1_NAMESPACE::make_result;
+ using BOOST_OUTCOME_V1_NAMESPACE::make_errored_outcome;
+ using BOOST_OUTCOME_V1_NAMESPACE::make_errored_result;
+ using BOOST_OUTCOME_V1_NAMESPACE::monad_errc;
+ using BOOST_OUTCOME_V1_NAMESPACE::monad_category;
+BOOST_AFIO_V2_NAMESPACE_END
+
+///////////////////////////////////////////////////////////////////////////////
+// Set up dll import/export options
+#if (defined(BOOST_AFIO_DYN_LINK) || defined(BOOST_ALL_DYN_LINK)) && \
+ !defined(BOOST_AFIO_STATIC_LINK)
+
+# if defined(BOOST_AFIO_SOURCE)
+# undef BOOST_AFIO_HEADERS_ONLY
+# define BOOST_AFIO_DECL BOOST_SYMBOL_EXPORT
+# define BOOST_AFIO_BUILD_DLL
+# else
+# define BOOST_AFIO_DECL
+# endif
+#else
+# define BOOST_AFIO_DECL
+#endif // building a shared library
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Auto library naming
+#if !defined(BOOST_AFIO_SOURCE) && !defined(BOOST_ALL_NO_LIB) && \
+ !defined(BOOST_AFIO_NO_LIB) && !AFIO_STANDALONE && !BOOST_AFIO_HEADERS_ONLY
+
+#define BOOST_LIB_NAME boost_afio
+
+// tell the auto-link code to select a dll when required:
+#if defined(BOOST_ALL_DYN_LINK) || defined(BOOST_AFIO_DYN_LINK)
+#define BOOST_DYN_LINK
+#endif
+
+#include <boost/config/auto_link.hpp>
+
+#endif // auto-linking disabled
+
+//#define BOOST_THREAD_VERSION 4
+//#define BOOST_THREAD_PROVIDES_VARIADIC_THREAD
+//#define BOOST_THREAD_DONT_PROVIDE_FUTURE
+//#define BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK
+#if BOOST_AFIO_HEADERS_ONLY == 1
+# define BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC inline
+# define BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC inline
+# define BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC inline virtual
+// GCC gets upset if inline virtual functions aren't defined
+# ifdef BOOST_GCC
+# define BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC { BOOST_AFIO_THROW_FATAL(std::runtime_error("Attempt to call pure virtual member function")); abort(); }
+# else
+# define BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC =0;
+# endif
+#else
+# define BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC extern BOOST_AFIO_DECL
+# define BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC
+# define BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC virtual
+# define BOOST_AFIO_HEADERS_ONLY_VIRTUAL_UNDEFINED_SPEC =0;
+#endif
+
+#if defined(__has_feature)
+# if __has_feature(thread_sanitizer)
+# define BOOST_AFIO_DISABLE_THREAD_SANITIZE __attribute__((no_sanitize_thread))
+# endif
+#endif
+#ifndef BOOST_AFIO_DISABLE_THREAD_SANITIZE
+# define BOOST_AFIO_DISABLE_THREAD_SANITIZE
+#endif
+
+#ifndef BOOST_AFIO_LAMBDA_MOVE_CAPTURE
+# ifdef __cpp_init_captures
+# define BOOST_AFIO_LAMBDA_MOVE_CAPTURE(var) var=std::move(var)
+# else
+# define BOOST_AFIO_LAMBDA_MOVE_CAPTURE(var) var
+# endif
+#endif
+
+#ifndef BOOST_AFIO_THREAD_LOCAL
+# ifdef __cpp_thread_local
+# define BOOST_AFIO_THREAD_LOCAL thread_local
+# elif defined(_MSC_VER)
+# define BOOST_AFIO_THREAD_LOCAL __declspec(thread)
+# elif defined(__GNUC__)
+# define BOOST_AFIO_THREAD_LOCAL __thread
+# else
+# error Unknown compiler, cannot set BOOST_AFIO_THREAD_LOCAL
+# endif
+#endif
+
+#ifndef BOOST_AFIO_LOG_FATAL_EXIT
+#define BOOST_AFIO_LOG_FATAL_EXIT(expr) std::cerr << expr
+#endif
+
+#endif // BOOST_AFIO_NEED_DEFINE
+
diff --git a/attic/include/boost/afio/v2/detail/ErrorHandling.hpp b/attic/include/boost/afio/v2/detail/ErrorHandling.hpp
new file mode 100644
index 00000000..9467591e
--- /dev/null
+++ b/attic/include/boost/afio/v2/detail/ErrorHandling.hpp
@@ -0,0 +1,81 @@
+/* NiallsCPP11Utilities
+(C) 2012 Niall Douglas http://www.nedprod.com/
+File Created: Nov 2012
+
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+#include <stdexcept>
+#include <string>
+
+#if defined(BOOST_MSVC) && BOOST_MSVC<=1800 && !defined(__func__)
+#define __func__ __FUNCTION__
+#endif
+
+#ifdef BOOST_AFIO_EXCEPTION_DISABLESOURCEINFO
+#define BOOST_AFIO_EXCEPTION_FILE(p) (const char *) 0
+#define BOOST_AFIO_EXCEPTION_FUNCTION(p) (const char *) 0
+#define BOOST_AFIO_EXCEPTION_LINE(p) 0
+#else
+#define BOOST_AFIO_EXCEPTION_FILE(p) __FILE__
+#define BOOST_AFIO_EXCEPTION_FUNCTION(p) __func__
+#define BOOST_AFIO_EXCEPTION_LINE(p) __LINE__
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+ class path;
+ namespace detail{
+
+#ifdef WIN32
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC void int_throwWinError(const char *file, const char *function, int lineno, unsigned code, std::function<BOOST_AFIO_V2_NAMESPACE::path()> filename);
+ extern "C" unsigned __stdcall GetLastError();
+#define BOOST_AFIO_ERRGWIN(code) { BOOST_AFIO_V2_NAMESPACE::detail::int_throwWinError(BOOST_AFIO_EXCEPTION_FILE(0), BOOST_AFIO_EXCEPTION_FUNCTION(0), BOOST_AFIO_EXCEPTION_LINE(0), code, nullptr); }
+#define BOOST_AFIO_ERRGWINFN(code, filename) { BOOST_AFIO_V2_NAMESPACE::detail::int_throwWinError(BOOST_AFIO_EXCEPTION_FILE(0), BOOST_AFIO_EXCEPTION_FUNCTION(0), BOOST_AFIO_EXCEPTION_LINE(0), code, std::function<BOOST_AFIO_V2_NAMESPACE::path()>(filename)); }
+#define BOOST_AFIO_ERRHWIN(exp) { unsigned __errcode=(unsigned)(exp); if(!__errcode) BOOST_AFIO_ERRGWIN(GetLastError()); }
+#define BOOST_AFIO_ERRHWINFN(exp, filename) { unsigned __errcode=(unsigned)(exp); if(!__errcode) BOOST_AFIO_ERRGWINFN(GetLastError(), filename); }
+
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC void int_throwNTError(const char *file, const char *function, int lineno, unsigned code, std::function<BOOST_AFIO_V2_NAMESPACE::path()> filename);
+#define BOOST_AFIO_ERRGNT(code) { BOOST_AFIO_V2_NAMESPACE::detail::int_throwNTError(BOOST_AFIO_EXCEPTION_FILE(0), BOOST_AFIO_EXCEPTION_FUNCTION(0), BOOST_AFIO_EXCEPTION_LINE(0), code, nullptr); }
+#define BOOST_AFIO_ERRGNTFN(code, filename) { BOOST_AFIO_V2_NAMESPACE::detail::int_throwNTError(BOOST_AFIO_EXCEPTION_FILE(0), BOOST_AFIO_EXCEPTION_FUNCTION(0), BOOST_AFIO_EXCEPTION_LINE(0), code, std::function<BOOST_AFIO_V2_NAMESPACE::path()>(filename)); }
+#define BOOST_AFIO_ERRHNT(exp) { unsigned __errcode=(unsigned)(exp); if(0/*STATUS_SUCCESS*/!=__errcode) BOOST_AFIO_ERRGNT(__errcode); }
+#define BOOST_AFIO_ERRHNTFN(exp, filename) { unsigned __errcode=(unsigned)(exp); if(0/*STATUS_SUCCESS*/!=__errcode) BOOST_AFIO_ERRGNTFN(__errcode, filename); }
+#endif // WIN32
+
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC void int_throwOSError(const char *file, const char *function, int lineno, int code, std::function<BOOST_AFIO_V2_NAMESPACE::path()> filename);
+#define BOOST_AFIO_ERRGOS(code) { BOOST_AFIO_V2_NAMESPACE::detail::int_throwOSError(BOOST_AFIO_EXCEPTION_FILE(code), BOOST_AFIO_EXCEPTION_FUNCTION(code), BOOST_AFIO_EXCEPTION_LINE(code), code, nullptr); }
+#define BOOST_AFIO_ERRGOSFN(code, filename) { BOOST_AFIO_V2_NAMESPACE::detail::int_throwOSError(BOOST_AFIO_EXCEPTION_FILE(code), BOOST_AFIO_EXCEPTION_FUNCTION(code), BOOST_AFIO_EXCEPTION_LINE(code), code, std::function<BOOST_AFIO_V2_NAMESPACE::path()>(filename)); }
+ /*! Use this macro to wrap POSIX, UNIX or CLib functions. On BOOST_WINDOWS, the includes anything in
+ MSVCRT which sets errno
+ */
+#define BOOST_AFIO_ERRHOS(exp) { int __errcode=(int)(exp); if(__errcode<0) BOOST_AFIO_ERRGOS(errno); }
+ /*! Use this macro to wrap POSIX, UNIX or CLib functions taking a filename. On BOOST_WINDOWS, the includes anything in
+ MSVCRT which sets errno
+ */
+#define BOOST_AFIO_ERRHOSFN(exp, filename) { int __errcode=(int)(exp); if(__errcode<0) BOOST_AFIO_ERRGOSFN(errno, filename); }
+ }//namespace detail
+
+BOOST_AFIO_V2_NAMESPACE_END
+
diff --git a/attic/include/boost/afio/v2/detail/Undoer.hpp b/attic/include/boost/afio/v2/detail/Undoer.hpp
new file mode 100644
index 00000000..57ab5983
--- /dev/null
+++ b/attic/include/boost/afio/v2/detail/Undoer.hpp
@@ -0,0 +1,97 @@
+/*
+ * File: Undoer.hpp
+ * Author: Niall Douglas
+ *(C) 2012 Niall Douglas http://www.nedprod.com/
+ * Created on June 25, 2013, 11:35 AM
+
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+/*! \file Undoer.hpp
+\brief Declares Undoer class and implementation
+*/
+
+#include <utility>
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+ namespace detail{
+
+ namespace Impl {
+ template<typename T, bool iscomparable> struct is_nullptr { bool operator()(T c) const noexcept { return !c; } };
+ template<typename T> struct is_nullptr<T, false> { bool operator()(T) const noexcept { return false; } };
+ }
+ //! Compile-time safe detector of if \em v is nullptr (can cope with non-pointer convertibles)
+#if defined(__GNUC__) && (BOOST_GCC<41000 || defined(__MINGW32__))
+ template<typename T> bool is_nullptr(T v) noexcept { return Impl::is_nullptr<T, std::is_constructible<bool, T>::value>()(std::forward<T>(v)); }
+#else
+ template<typename T> bool is_nullptr(T v) noexcept { return Impl::is_nullptr<T, std::is_trivially_constructible<bool, T>::value>()(std::forward<T>(v)); }
+#endif
+
+
+ template<typename callable> class UndoerImpl
+ {
+ bool _dismissed;
+ callable undoer;
+ UndoerImpl() = delete;
+ UndoerImpl(const UndoerImpl &) = delete;
+ UndoerImpl &operator=(const UndoerImpl &) = delete;
+ explicit UndoerImpl(callable &&c) : _dismissed(false), undoer(std::move(c)) { }
+ void int_trigger() { if(!_dismissed && !is_nullptr(undoer)) { undoer(); _dismissed=true; } }
+ public:
+ UndoerImpl(UndoerImpl &&o) noexcept : _dismissed(o._dismissed), undoer(std::move(o.undoer)) { o._dismissed=true; }
+ UndoerImpl &operator=(UndoerImpl &&o) noexcept { int_trigger(); _dismissed=o._dismissed; undoer=std::move(o.undoer); o._dismissed=true; return *this; }
+ template<typename _callable> friend UndoerImpl<_callable> Undoer(_callable c);
+ ~UndoerImpl() { int_trigger(); }
+ //! Returns if the Undoer is dismissed
+ bool dismissed() const { return _dismissed; }
+ //! Dismisses the Undoer
+ void dismiss(bool d=true) { _dismissed=d; }
+ //! Undismisses the Undoer
+ void undismiss(bool d=true) { _dismissed=!d; }
+ };//UndoerImpl
+
+
+ /*! \brief Alexandrescu style rollbacks, a la C++ 11.
+
+ Example of usage:
+ \code
+ auto resetpos=Undoer([&s]() { s.seekg(0, std::ios::beg); });
+ ...
+ resetpos.dismiss();
+ \endcode
+ */
+ template<typename callable> inline UndoerImpl<callable> Undoer(callable c)
+ {
+ //static_assert(!std::is_function<callable>::value && !std::is_member_function_pointer<callable>::value && !std::is_member_object_pointer<callable>::value && !has_call_operator<callable>::value, "Undoer applied to a type not providing a call operator");
+ auto foo=UndoerImpl<callable>(std::move(c));
+ return foo;
+ }//Undoer
+
+ }//namespace detail
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+
diff --git a/attic/include/boost/afio/v2/detail/Utility.hpp b/attic/include/boost/afio/v2/detail/Utility.hpp
new file mode 100644
index 00000000..ea8acdf2
--- /dev/null
+++ b/attic/include/boost/afio/v2/detail/Utility.hpp
@@ -0,0 +1,295 @@
+/*
+ * File: Utility.hpp
+ * Author: ned14
+ *
+ * Created on June 25, 2013, 12:43 PM
+
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#if !ASIO_STANDALONE
+#include "boost/exception/diagnostic_information.hpp"
+#endif
+
+//! \def BOOST_AFIO_TYPEALIGNMENT(bytes) The markup this compiler uses to mark a type as having some given alignment
+#ifndef BOOST_AFIO_TYPEALIGNMENT
+#ifdef __cpp_alignas
+#define BOOST_AFIO_TYPEALIGNMENT(bytes) alignas(bytes)
+#else
+#ifdef _MSC_VER
+#define BOOST_AFIO_TYPEALIGNMENT(bytes) __declspec(align(bytes))
+#elif defined(__GNUC__)
+#define BOOST_AFIO_TYPEALIGNMENT(bytes) __attribute__((aligned(bytes)))
+#else
+#define BOOST_AFIO_TYPEALIGNMENT(bytes) unknown_type_alignment_markup_for_this_compiler
+#endif
+#endif
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+ namespace detail {
+#ifdef _MSC_VER
+ static inline int win32_exception_filter()
+ {
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+ static inline void set_threadname(const char *threadName)
+ {
+ const DWORD MS_VC_EXCEPTION=0x406D1388;
+
+#pragma pack(push,8)
+ typedef struct tagTHREADNAME_INFO
+ {
+ DWORD dwType; // Must be 0x1000.
+ LPCSTR szName; // Pointer to name (in user addr space).
+ DWORD dwThreadID; // Thread ID (-1=caller thread).
+ DWORD dwFlags; // Reserved for stl_future use, must be zero.
+ } THREADNAME_INFO;
+#pragma pack(pop)
+ THREADNAME_INFO info;
+ info.dwType = 0x1000;
+ info.szName = threadName;
+ info.dwThreadID = (DWORD) -1;
+ info.dwFlags = 0;
+
+ __try
+ {
+ RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*) &info);
+ }
+ __except(win32_exception_filter())
+ {
+ int a=1;
+ }
+ }
+#elif defined(__linux__)
+ static inline void set_threadname(const char *threadName)
+ {
+ pthread_setname_np(pthread_self(), threadName);
+ }
+#else
+ static inline void set_threadname(const char *threadName)
+ {
+ }
+#endif
+
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC void print_fatal_exception_message_to_stderr(const char *msg);
+ }
+BOOST_AFIO_V2_NAMESPACE_END
+
+#ifndef BOOST_AFIO_THROW_FATAL
+// Need some portable way of throwing a really absolutely definitely fatal exception
+// If we guaranteed had noexcept, this would be easy, but for compilers without noexcept
+// we'll bounce through extern "C" as well just to be sure
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4297) // function assumed not to throw an exception but does __declspec(nothrow) or throw() was specified on the function
+#endif
+#ifdef BOOST_AFIO_COMPILING_FOR_GCOV
+#define BOOST_AFIO_THROW_FATAL(x) std::terminate()
+#else
+namespace boost { namespace afio { namespace fatal_exception_throw {
+ template<class T> inline void do_throw_fatal_exception(const T &v) noexcept
+ {
+ BOOST_AFIO_V2_NAMESPACE::detail::print_fatal_exception_message_to_stderr(v.what());
+ throw v;
+ }
+ extern "C" inline void boost_afio_do_throw_fatal_exception(std::function<void()> impl) noexcept{ impl(); }
+ template<class T> inline void throw_fatal_exception(const T &v) noexcept
+ {
+ // In case the extern "C" fails to terminate, trap and terminate here
+ try
+ {
+ std::function<void()> doer=std::bind(&do_throw_fatal_exception<T>, std::ref(v));
+ boost_afio_do_throw_fatal_exception(doer);
+ }
+ catch(...)
+ {
+ std::terminate(); // Sadly won't produce much of a useful error message
+ }
+ }
+} } }
+#define BOOST_AFIO_THROW_FATAL(x) boost::afio::fatal_exception_throw::throw_fatal_exception(x)
+#endif
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+#define BOOST_AFIO_THROW(x) throw x
+#define BOOST_AFIO_RETHROW throw
+#endif // BOOST_AFIO_THROW_FATAL
+
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+ namespace detail {
+ // Support for make_unique. I keep wishing it was already here!
+ template<class T, class... Args>
+ std::unique_ptr<T> make_unique(Args &&... args){
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+ }
+
+ // Support for combining hashes.
+ template <class T>
+ inline void hash_combine(std::size_t& seed, const T& v)
+ {
+ std::hash<T> hasher;
+ seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
+ }
+
+ template<class T> struct decay_preserving_cv {
+ typedef typename std::remove_reference<T>::type U;
+ typedef typename std::conditional<
+ std::is_array<U>::value,
+ typename std::remove_extent<U>::type*,
+ typename std::conditional<
+ std::is_function<U>::value,
+ typename std::add_pointer<U>::type,
+ U
+ >::type
+ >::type type;
+ };
+ // Support for SFINAE detection of iterator/pointer ranges (Can it dereference? Can it increment?)
+// template<class T, typename = void> struct is_rangeable : std::false_type { };
+// template<class T> struct is_rangeable<T, decltype(*std::declval<T&>(), ++std::declval<T&>(), void())> : std::true_type { };
+ // Support for SFINAE detection of containers (does it have begin() and end()?), made considerably more complex by needing MSVC to work.
+ template<class T> inline auto is_container_impl(T) -> decltype(*std::begin(std::declval<T>()), *std::end(std::declval<T>()), bool()) { return true; }
+ inline int is_container_impl(...) { return 0; }
+ template<class T, typename=decltype(is_container_impl(std::declval<T>()))> struct is_container : std::false_type { };
+ template<class T> struct is_container<T, bool> : std::true_type
+ {
+ typedef decltype(*std::begin(*((typename std::remove_reference<T>::type *) nullptr))) raw_type; //!< The raw type (probably a (const) lvalue ref) returned by *it
+ typedef typename decay_preserving_cv<raw_type>::type type; //!< The type held by the container, still potentially const if container does not permit write access
+ };
+
+ // Debug printing of exception info
+ inline std::ostream &output_exception_info(std::ostream &os, const std::exception &e)
+ {
+ return os << "Exception: '" << e.what() << "'";
+ }
+#if !ASIO_STANDALONE
+ inline std::ostream &output_exception_info(std::ostream &os, const boost::exception &e)
+ {
+ return os << "Exception: '" << boost::current_exception_diagnostic_information() << "'";
+ }
+#endif
+ inline std::ostream &output_exception_info(std::ostream &os)
+ {
+ try { throw; }
+ catch(const std::exception &e) { return output_exception_info(os, e); }
+#if !ASIO_STANDALONE
+ catch(const boost::exception &e) { return output_exception_info(os, e); }
+#endif
+ catch(...) { return os << "Exception : 'unknown type'"; }
+ }
+
+ } // namespace
+
+#if BOOST_AFIO_USE_BOOST_THREAD
+ typedef boost::exception_ptr exception_ptr;
+ using boost::current_exception;
+ using boost::rethrow_exception;
+#else
+ typedef std::exception_ptr exception_ptr;
+ using std::current_exception;
+ using std::rethrow_exception;
+#endif
+ // Get an exception ptr from a stl_future
+ template<typename T> inline exception_ptr get_exception_ptr(stl_future<T> &f)
+ {
+#if BOOST_AFIO_USE_BOOST_THREAD && BOOST_VERSION>=105500
+ // Thanks to Vicente for adding this to Boost.Thread
+ return f.get_exception_ptr();
+#else
+ // This seems excessive but I don't see any other legal way to extract the exception ...
+ bool success=false;
+ try
+ {
+ f.get();
+ success=true;
+ }
+ catch(...)
+ {
+ exception_ptr e(current_exception());
+ assert(e);
+ return e;
+ }
+ return exception_ptr();
+#endif
+ }
+ template<typename T> inline exception_ptr get_exception_ptr(const shared_future<T> &f)
+ {
+#if BOOST_AFIO_USE_BOOST_THREAD && BOOST_VERSION>=105500
+ // Thanks to Vicente for adding this to Boost.Thread
+ return const_cast<shared_future<T> &>(f).get_exception_ptr();
+#else
+ // This seems excessive but I don't see any other legal way to extract the exception ...
+ bool success=false;
+ try
+ {
+ // std::shared_future in older libstdc++ does not have a const get().
+ const_cast<shared_future<T> &>(f).get();
+ success=true;
+ }
+ catch(...)
+ {
+ exception_ptr e(current_exception());
+ assert(e);
+ return e;
+ }
+ return exception_ptr();
+#endif
+ }
+ // Is a stl_future ready?
+ template<typename T> inline bool is_ready(const stl_future<T> &f)
+ {
+#if BOOST_AFIO_USE_BOOST_THREAD
+ return f.is_ready();
+#else
+ return f.wait_for(chrono::seconds(0))==future_status::ready;
+#endif
+ }
+ template<typename T> inline bool is_ready(const shared_future<T> &f)
+ {
+#if BOOST_AFIO_USE_BOOST_THREAD
+ return f.is_ready();
+#else
+ return f.wait_for(chrono::seconds(0))==future_status::ready;
+#endif
+ }
+
+ struct filesystem_hash
+ {
+ std::hash<filesystem::path::string_type> hasher;
+ public:
+ size_t operator()(const filesystem::path& p) const
+ {
+ return hasher(p.native());
+ }
+ };
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+
diff --git a/attic/include/boost/afio/v2/detail/impl/ErrorHandling.ipp b/attic/include/boost/afio/v2/detail/impl/ErrorHandling.ipp
new file mode 100644
index 00000000..28c319b0
--- /dev/null
+++ b/attic/include/boost/afio/v2/detail/impl/ErrorHandling.ipp
@@ -0,0 +1,184 @@
+/* NiallsCPP11Utilities
+(C) 2012 Niall Douglas http://www.nedprod.com/
+File Created: Nov 2012
+*/
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "../../afio.hpp"
+#include <locale>
+#include <cstring>
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+namespace detail {
+ inline void push_exception(std::string name) noexcept
+ {
+#ifdef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+ try
+ {
+ if(!afio_exception_stack())
+ afio_exception_stack()=new afio_exception_stack_t;
+ afio_exception_stack_entry se;
+ se.name=std::move(name);
+ collect_stack(se.stack);
+ afio_exception_stack()->push_back(std::move(se));
+ } catch(...) { }
+#endif
+ }
+}
+BOOST_AFIO_V2_NAMESPACE_END
+
+#ifdef WIN32
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN 1
+#endif
+#include <Windows.h>
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+ namespace detail{
+
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_throwWinError(const char *file, const char *function, int lineno, unsigned code, std::function<BOOST_AFIO_V2_NAMESPACE::path()> filename)
+ {
+ if(ERROR_NOT_ENOUGH_MEMORY==code || ERROR_OUTOFMEMORY==code) BOOST_AFIO_THROW(std::bad_alloc());
+ error_code ec(code, system_category());
+ DWORD len;
+ char buffer[1024];
+ if(!(len=FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0, code, 0, buffer, sizeof(buffer), 0)))
+ {
+ strcpy(buffer, "unknown error code");
+ len=(DWORD) strlen(buffer);
+ }
+ // Remove annoying CRLF at end of message sometimes
+ while(10==buffer[len-1])
+ {
+ buffer[len-1]=0;
+ len--;
+ if(13==buffer[len-1])
+ {
+ buffer[len-1]=0;
+ len--;
+ }
+ }
+ std::string errstr(buffer, buffer+len);
+ errstr.append(" ("+to_string(code)+") in '"+std::string(file)+"':"+std::string(function)+":"+to_string(lineno));
+ // Add the filename where appropriate. This helps debugging a lot.
+ if(filename)
+ {
+ if(ERROR_FILE_NOT_FOUND==code || ERROR_PATH_NOT_FOUND==code)
+ {
+ errstr="File '"+filename().generic_string()+"' not found [Host OS Error: "+errstr+"]";
+ }
+ else if(ERROR_ACCESS_DENIED==code || ERROR_EA_ACCESS_DENIED==code)
+ {
+ errstr="Access to '"+filename().generic_string()+"' denied [Host OS Error: "+errstr+"]";
+ }
+ else
+ {
+ errstr.append(" [Path: '"+filename().generic_string()+"']");
+ }
+ }
+ BOOST_AFIO_DEBUG_PRINT("! %s\n", errstr.c_str());
+ push_exception(errstr);
+ BOOST_AFIO_THROW(system_error(ec, errstr));
+ }
+
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_throwNTError(const char *file, const char *function, int lineno, unsigned code, std::function<BOOST_AFIO_V2_NAMESPACE::path()> filename)
+ {
+ // system_category needs a win32 code, not NT kernel code
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 6387) // MSVC sanitiser warns on misuse of GetOverlappedResult
+#endif
+ {
+ DWORD br;
+ OVERLAPPED o;
+
+ o.Internal = code;
+ o.InternalHigh = 0;
+ o.Offset = 0;
+ o.OffsetHigh = 0;
+ o.hEvent = 0;
+ GetOverlappedResult(NULL, &o, &br, FALSE);
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ error_code ec(GetLastError(), system_category());
+ DWORD len;
+ char buffer[1024];
+ if(!(len=FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
+ GetModuleHandleA("NTDLL.DLL"), code, 0, buffer, sizeof(buffer), 0)))
+ {
+ strcpy(buffer, "unknown error code");
+ len=(DWORD) strlen(buffer);
+ }
+ // Remove annoying CRLF at end of message sometimes
+ while(10==buffer[len-1])
+ {
+ buffer[len-1]=0;
+ len--;
+ if(13==buffer[len-1])
+ {
+ buffer[len-1]=0;
+ len--;
+ }
+ }
+ std::string errstr(buffer, buffer+len);
+ errstr.append(" ("+to_string(code)+") in '"+std::string(file)+"':"+std::string(function)+":"+to_string(lineno));
+ // Add the filename where appropriate. This helps debugging a lot.
+ if(filename)
+ {
+ if(0xC000000F/*STATUS_NO_SUCH_FILE*/==code || 0xC000003A/*STATUS_OBJECT_PATH_NOT_FOUND*/==code)
+ {
+ errstr="File '"+filename().generic_string()+"' not found [Host OS Error: "+errstr+"]";
+ }
+ else if(0xC0000022/*STATUS_ACCESS_DENIED*/==code)
+ {
+ errstr="Access to '"+filename().generic_string()+"' denied [Host OS Error: "+errstr+"]";
+ }
+ else
+ {
+ errstr.append(" [Path: '"+filename().generic_string()+"']");
+ }
+ }
+ BOOST_AFIO_DEBUG_PRINT("! %s\n", errstr.c_str());
+ push_exception(errstr);
+ BOOST_AFIO_THROW(system_error(ec, errstr));
+ }
+
+ } // namespace detail
+BOOST_AFIO_V2_NAMESPACE_END
+
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+ namespace detail{
+
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void int_throwOSError(const char *file, const char *function, int lineno, int code, std::function<BOOST_AFIO_V2_NAMESPACE::path()> filename)
+ {
+ if(ENOMEM==code) BOOST_AFIO_THROW(std::bad_alloc());
+ error_code ec(code, generic_category());
+ std::string errstr(strerror(code));
+ errstr.append(" ("+to_string(code)+") in '"+std::string(file)+"':"+std::string(function)+":"+to_string(lineno));
+ // Add the filename where appropriate. This helps debugging a lot.
+ if(filename)
+ {
+ if(ENOENT==code || ENOTDIR==code)
+ {
+ errstr="File '"+filename().generic_string()+"' not found [Host OS Error: "+errstr+"]";
+ }
+ else if(EACCES==code)
+ {
+ errstr="Access to '"+filename().generic_string()+"' denied [Host OS Error: "+errstr+"]";
+ }
+ else
+ {
+ errstr.append(" [Path: '"+filename().generic_string()+"']");
+ }
+ }
+ BOOST_AFIO_DEBUG_PRINT("! %s\n", errstr.c_str());
+ push_exception(errstr);
+ BOOST_AFIO_THROW(system_error(ec, errstr));
+ }// end int_throwOSError
+ }// namespace detail
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/attic/include/boost/afio/v2/detail/impl/afio.ipp b/attic/include/boost/afio/v2/detail/impl/afio.ipp
new file mode 100644
index 00000000..744f2dac
--- /dev/null
+++ b/attic/include/boost/afio/v2/detail/impl/afio.ipp
@@ -0,0 +1,3670 @@
+/* async_file_io
+Provides a threadpool and asynchronous file i/o infrastructure based on Boost.ASIO, Boost.Iostreams and filesystem
+(C) 2013-2015 Niall Douglas http://www.nedprod.com/
+File Created: Mar 2013
+*/
+
+//#define BOOST_AFIO_USE_CONCURRENT_UNORDERED_MAP
+//#define BOOST_AFIO_MAX_NON_ASYNC_QUEUE_DEPTH 1
+
+#ifndef BOOST_AFIO_MAX_NON_ASYNC_QUEUE_DEPTH
+#define BOOST_AFIO_MAX_NON_ASYNC_QUEUE_DEPTH 8
+#endif
+//#define USE_POSIX_ON_WIN32 // Useful for testing
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4456) // declaration hides previous local declaration
+#pragma warning(disable: 4458) // declaration hides class member
+#pragma warning(disable: 4996) // This function or variable may be unsafe
+#endif
+
+// This always compiles in input validation for this file only (the header file
+// disables at the point of instance validation in release builds)
+#if !defined(BOOST_AFIO_NEVER_VALIDATE_INPUTS) && !defined(BOOST_AFIO_COMPILING_FOR_GCOV)
+#define BOOST_AFIO_VALIDATE_INPUTS 1
+#endif
+
+// Define this to have every allocated op take a backtrace from where it was allocated
+#ifndef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+ #if !defined(NDEBUG) || defined(BOOST_AFIO_RUNNING_IN_CI)
+ #define BOOST_AFIO_OP_STACKBACKTRACEDEPTH 8
+ #endif
+#endif
+// Right now only Windows, Linux and FreeBSD supported
+#if (!defined(__linux__) && !defined(__FreeBSD__) && !defined(WIN32)) || defined(BOOST_AFIO_COMPILING_FOR_GCOV)
+# undef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+#endif
+
+// Define this to have every part of AFIO print, in extremely terse text, what it is doing and why.
+#if (defined(BOOST_AFIO_DEBUG_PRINTING) && BOOST_AFIO_DEBUG_PRINTING)
+#ifndef BOOST_AFIO_DEBUG_PRINTING
+# define BOOST_AFIO_DEBUG_PRINTING 1
+#endif
+#ifdef WIN32
+#define BOOST_AFIO_DEBUG_PRINT(...) \
+ { \
+ char buffer[16384]; \
+ sprintf(buffer, __VA_ARGS__); \
+ fprintf(stderr, buffer); \
+ OutputDebugStringA(buffer); \
+ }
+#else
+#define BOOST_AFIO_DEBUG_PRINT(...) \
+ { \
+ fprintf(stderr, __VA_ARGS__); \
+ }
+#endif
+#else
+#define BOOST_AFIO_DEBUG_PRINT(...)
+#endif
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "../../afio.hpp"
+#ifndef BOOST_AFIO_DISABLE_VALGRIND
+# include "../valgrind/memcheck.h"
+#else
+# define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(addr, len)
+#endif
+
+#include <limits>
+#include <random>
+#include <unordered_map>
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+ template<class Key, class T, class Hash=std::hash<Key>, class Pred=std::equal_to<Key>, class Alloc=std::allocator<std::pair<const Key, T>>> using engine_unordered_map_t=std::unordered_map<Key, T, Hash, Pred, Alloc>;
+BOOST_AFIO_V2_NAMESPACE_END
+
+#include <sys/types.h>
+#ifdef __MINGW32__
+// Workaround bad headers as usual in mingw
+typedef __int64 off64_t;
+#endif
+#include <fcntl.h>
+#include <sys/stat.h>
+#ifdef __FreeBSD__
+// Unfortunately these spazz all over the place which causes ambiguous symbol resolution for C++ thread
+// So hack around the problem
+#define thread freebsd_thread
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#undef thread
+#define HAVE_STAT_FLAGS
+#define HAVE_STAT_GEN
+#define HAVE_BIRTHTIMESPEC
+#endif
+#ifdef __APPLE__
+#define HAVE_STAT_FLAGS
+#define HAVE_STAT_GEN
+#define HAVE_BIRTHTIMESPEC
+#endif
+#ifdef WIN32
+#ifndef S_IFSOCK
+#define S_IFSOCK 0xC000
+#endif
+#ifndef S_IFBLK
+#define S_IFBLK 0x6000
+#endif
+#ifndef S_IFIFO
+#define S_IFIFO 0x1000
+#endif
+#ifndef S_IFLNK
+#define S_IFLNK 0xA000
+#endif
+#endif
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+static inline filesystem::file_type to_st_type(uint16_t mode)
+{
+ switch(mode & S_IFMT)
+ {
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ case S_IFBLK:
+ return filesystem::file_type::block_file;
+ case S_IFCHR:
+ return filesystem::file_type::character_file;
+ case S_IFDIR:
+ return filesystem::file_type::directory_file;
+ case S_IFIFO:
+ return filesystem::file_type::fifo_file;
+ case S_IFLNK:
+ return filesystem::file_type::symlink_file;
+ case S_IFREG:
+ return filesystem::file_type::regular_file;
+ case S_IFSOCK:
+ return filesystem::file_type::socket_file;
+ default:
+ return filesystem::file_type::type_unknown;
+#else
+ case S_IFBLK:
+ return filesystem::file_type::block;
+ case S_IFCHR:
+ return filesystem::file_type::character;
+ case S_IFDIR:
+ return filesystem::file_type::directory;
+ case S_IFIFO:
+ return filesystem::file_type::fifo;
+ case S_IFLNK:
+ return filesystem::file_type::symlink;
+ case S_IFREG:
+ return filesystem::file_type::regular;
+ case S_IFSOCK:
+ return filesystem::file_type::socket;
+ default:
+ return filesystem::file_type::unknown;
+#endif
+ }
+}
+#ifdef AT_FDCWD
+static BOOST_CONSTEXPR_OR_CONST int at_fdcwd=AT_FDCWD;
+#else
+static BOOST_CONSTEXPR_OR_CONST int at_fdcwd=-100;
+#endif
+// Should give a compile time error on compilers with constexpr
+static inline BOOST_CONSTEXPR_OR_CONST int check_fdcwd(int dirh) { return (dirh!=at_fdcwd) ? throw std::runtime_error("dirh is not at_fdcwd on a platform which does not have XXXat() API support") : 0; }
+BOOST_AFIO_V2_NAMESPACE_END
+#ifdef WIN32
+// We also compile the posix compat layer for catching silly compile errors for POSIX
+#include <io.h>
+#include <direct.h>
+#ifdef AT_FDCWD
+#error AT_FDCWD should not be defined on MSVCRT, best check the thunk wrappers!
+#endif
+#define BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS 0
+#define BOOST_AFIO_POSIX_MKDIRAT(dirh, path, mode) (check_fdcwd(dirh), _wmkdir(path))
+#define BOOST_AFIO_POSIX_RMDIRAT(dirh, path) (check_fdcwd(dirh), _wrmdir(path))
+#define BOOST_AFIO_POSIX_STAT_STRUCT struct __stat64
+#define BOOST_AFIO_POSIX_STATAT(dirh, path, s) (check_fdcwd(dirh), _wstat64((path), (s)))
+#define BOOST_AFIO_POSIX_LSTATAT(dirh, path, s) (check_fdcwd(dirh), _wstat64((path), (s)))
+#define BOOST_AFIO_POSIX_FSTAT(fd, s) _fstat64((fd), (s))
+#define BOOST_AFIO_POSIX_OPENAT(dirh, path, flags, mode) (check_fdcwd(dirh), _wopen((path), (flags), (mode)))
+#define BOOST_AFIO_POSIX_CLOSE _close
+#define BOOST_AFIO_POSIX_RENAMEAT(olddirh, oldpath, newdirh, newpath) (check_fdcwd(olddirh), check_fdcwd(newdirh), _wrename((oldpath), (newpath)))
+#define AT_REMOVEDIR 0x200
+#define BOOST_AFIO_POSIX_UNLINKAT(dirh, path, flags) (check_fdcwd(dirh), (flags&AT_REMOVEDIR) ? _wrmdir(path) : _wunlink(path))
+#define BOOST_AFIO_POSIX_FSYNC _commit
+#define BOOST_AFIO_POSIX_FTRUNCATE winftruncate
+#define BOOST_AFIO_POSIX_MMAP(addr, size, prot, flags, fd, offset) (-1)
+#define BOOST_AFIO_POSIX_MUNMAP(addr, size) (-1)
+#include "nt_kernel_stuff.hpp"
+#else
+#include <dirent.h> /* Defines DT_* constants */
+#include <sys/syscall.h>
+#include <fnmatch.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+#include <sys/mount.h>
+#ifdef __linux__
+# include <sys/statfs.h>
+# include <mntent.h>
+#endif
+#include <limits.h>
+// Does this POSIX provides at(dirh) support?
+#ifdef AT_FDCWD
+#define BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS 1
+#define BOOST_AFIO_POSIX_MKDIRAT(dirh, path, mode) ::mkdirat((dirh), (path), (mode))
+#define BOOST_AFIO_POSIX_RMDIRAT(dirh, path) ::rmdir((dirh), (path))
+#define BOOST_AFIO_POSIX_STAT_STRUCT struct stat
+#define BOOST_AFIO_POSIX_STATAT(dirh, path, s) ::fstatat((dirh), (path), (s))
+#define BOOST_AFIO_POSIX_LSTATAT(dirh, path, s) ::fstatat((dirh), (path), (s), AT_SYMLINK_NOFOLLOW)
+#define BOOST_AFIO_POSIX_FSTAT ::fstat
+#define BOOST_AFIO_POSIX_OPENAT(dirh, path, flags, mode) ::openat((dirh), (path), (flags), (mode))
+#define BOOST_AFIO_POSIX_SYMLINKAT(from, dirh, to) ::symlinkat((from), (dirh), (to))
+#define BOOST_AFIO_POSIX_CLOSE ::close
+#define BOOST_AFIO_POSIX_RENAMEAT(olddirh, oldpath, newdirh, newpath) ::renameat((olddirh), (oldpath), (newdirh), (newpath))
+#define BOOST_AFIO_POSIX_LINKAT(olddirh, oldpath, newdirh, newpath, flags) ::linkat((olddirh), (oldpath), (newdirh), (newpath), (flags))
+#define BOOST_AFIO_POSIX_UNLINKAT(dirh, path, flags) ::unlinkat((dirh), (path), (flags))
+#define BOOST_AFIO_POSIX_FSYNC ::fsync
+#define BOOST_AFIO_POSIX_FTRUNCATE ::ftruncate
+#define BOOST_AFIO_POSIX_MMAP ::mmap
+#define BOOST_AFIO_POSIX_MUNMAP ::munmap
+#else
+#define BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS 0
+#define BOOST_AFIO_POSIX_MKDIRAT(dirh, path, mode) (check_fdcwd(dirh), ::mkdir((path), (mode)))
+#define BOOST_AFIO_POSIX_RMDIRAT(dirh, path) (check_fdcwd(dirh), ::rmdir(path))
+#define BOOST_AFIO_POSIX_STAT_STRUCT struct stat
+#define BOOST_AFIO_POSIX_STATAT(dirh, path, s) (check_fdcwd(dirh), ::stat((path), (s)))
+#define BOOST_AFIO_POSIX_LSTATAT(dirh, path, s) (check_fdcwd(dirh), ::lstat((path), (s)))
+#define BOOST_AFIO_POSIX_FSTAT ::fstat
+#define BOOST_AFIO_POSIX_OPENAT(dirh, path, flags, mode) (check_fdcwd(dirh), ::open((path), (flags), (mode)))
+#define BOOST_AFIO_POSIX_SYMLINKAT(from, dirh, to) (check_fdcwd(dirh), ::symlink((from), (to)))
+#define BOOST_AFIO_POSIX_CLOSE ::close
+#define BOOST_AFIO_POSIX_RENAMEAT(olddirh, oldpath, newdirh, newpath) (check_fdcwd(olddirh), check_fdcwd(newdirh), ::rename((oldpath), (newpath)))
+#define BOOST_AFIO_POSIX_LINKAT(olddirh, oldpath, newdirh, newpath, flags) (check_fdcwd(olddirh), check_fdcwd(newdirh), ::link((oldpath), (newpath)))
+#define AT_REMOVEDIR 0x200
+#define BOOST_AFIO_POSIX_UNLINKAT(dirh, path, flags) (check_fdcwd(dirh), (flags&AT_REMOVEDIR) ? ::rmdir(path) : ::unlink(path))
+#define BOOST_AFIO_POSIX_FSYNC ::fsync
+#define BOOST_AFIO_POSIX_FTRUNCATE ::ftruncate
+#define BOOST_AFIO_POSIX_MMAP ::mmap
+#define BOOST_AFIO_POSIX_MUNMAP ::munmap
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+static inline chrono::system_clock::time_point to_timepoint(struct timespec ts)
+{
+ // Need to have this self-adapt to the STL being used
+ static BOOST_CONSTEXPR_OR_CONST unsigned long long STL_TICKS_PER_SEC=(unsigned long long) chrono::system_clock::period::den/chrono::system_clock::period::num;
+ static BOOST_CONSTEXPR_OR_CONST unsigned long long multiplier=STL_TICKS_PER_SEC>=1000000000ULL ? STL_TICKS_PER_SEC/1000000000ULL : 1;
+ static BOOST_CONSTEXPR_OR_CONST unsigned long long divider=STL_TICKS_PER_SEC>=1000000000ULL ? 1 : 1000000000ULL/STL_TICKS_PER_SEC;
+ // We make the big assumption that the STL's system_clock is based on the time_t epoch 1st Jan 1970.
+ chrono::system_clock::duration duration(ts.tv_sec*STL_TICKS_PER_SEC+ts.tv_nsec*multiplier/divider);
+ return chrono::system_clock::time_point(duration);
+}
+static inline void fill_stat_t(stat_t &stat, BOOST_AFIO_POSIX_STAT_STRUCT s, metadata_flags wanted)
+{
+ using namespace boost::afio;
+ if(!!(wanted&metadata_flags::dev)) { stat.st_dev=s.st_dev; }
+ if(!!(wanted&metadata_flags::ino)) { stat.st_ino=s.st_ino; }
+ if(!!(wanted&metadata_flags::type)) { stat.st_type=to_st_type(s.st_mode); }
+ if(!!(wanted&metadata_flags::perms)) { stat.st_perms=s.st_mode & 0xfff; }
+ if(!!(wanted&metadata_flags::nlink)) { stat.st_nlink=s.st_nlink; }
+ if(!!(wanted&metadata_flags::uid)) { stat.st_uid=s.st_uid; }
+ if(!!(wanted&metadata_flags::gid)) { stat.st_gid=s.st_gid; }
+ if(!!(wanted&metadata_flags::rdev)) { stat.st_rdev=s.st_rdev; }
+#ifdef __ANDROID__
+ if(!!(wanted&metadata_flags::atim)) { stat.st_atim=to_timepoint(*((struct timespec *)&s.st_atime)); }
+ if(!!(wanted&metadata_flags::mtim)) { stat.st_mtim=to_timepoint(*((struct timespec *)&s.st_mtime)); }
+ if(!!(wanted&metadata_flags::ctim)) { stat.st_ctim=to_timepoint(*((struct timespec *)&s.st_ctime)); }
+#elif defined(__APPLE__)
+ if(!!(wanted&metadata_flags::atim)) { stat.st_atim=to_timepoint(s.st_atimespec); }
+ if(!!(wanted&metadata_flags::mtim)) { stat.st_mtim=to_timepoint(s.st_mtimespec); }
+ if(!!(wanted&metadata_flags::ctim)) { stat.st_ctim=to_timepoint(s.st_ctimespec); }
+#else // Linux and BSD
+ if(!!(wanted&metadata_flags::atim)) { stat.st_atim=to_timepoint(s.st_atim); }
+ if(!!(wanted&metadata_flags::mtim)) { stat.st_mtim=to_timepoint(s.st_mtim); }
+ if(!!(wanted&metadata_flags::ctim)) { stat.st_ctim=to_timepoint(s.st_ctim); }
+#endif
+ if(!!(wanted&metadata_flags::size)) { stat.st_size=s.st_size; }
+ if(!!(wanted&metadata_flags::allocated)) { stat.st_allocated=(off_t) s.st_blocks*512; }
+ if(!!(wanted&metadata_flags::blocks)) { stat.st_blocks=s.st_blocks; }
+ if(!!(wanted&metadata_flags::blksize)) { stat.st_blksize=s.st_blksize; }
+#ifdef HAVE_STAT_FLAGS
+ if(!!(wanted&metadata_flags::flags)) { stat.st_flags=s.st_flags; }
+#endif
+#ifdef HAVE_STAT_GEN
+ if(!!(wanted&metadata_flags::gen)) { stat.st_gen=s.st_gen; }
+#endif
+#ifdef HAVE_BIRTHTIMESPEC
+#if defined(__APPLE__)
+ if(!!(wanted&metadata_flags::birthtim)) { stat.st_birthtim=to_timepoint(s.st_birthtimespec); }
+#else
+ if(!!(wanted&metadata_flags::birthtim)) { stat.st_birthtim=to_timepoint(s.st_birthtim); }
+#endif
+#endif
+ if(!!(wanted&metadata_flags::sparse)) { stat.st_sparse=((off_t) s.st_blocks*512)<(off_t) s.st_size; }
+}
+BOOST_AFIO_V2_NAMESPACE_END
+#endif
+
+#ifdef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+# ifdef WIN32
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+typedef std::vector<void *> stack_type;
+inline void collect_stack(stack_type &stack)
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ stack.resize(BOOST_AFIO_OP_STACKBACKTRACEDEPTH);
+ stack.resize(RtlCaptureStackBackTrace(0, (ULONG) stack.size(), &stack.front(), nullptr));
+}
+inline void print_stack(std::ostream &s, const stack_type &stack)
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ size_t n=0;
+ for(void *addr: stack)
+ {
+ DWORD displ;
+ IMAGEHLP_LINE64 ihl={ sizeof(IMAGEHLP_LINE64) };
+ s << " " << ++n << ". 0x" << std::hex << addr << std::dec << ": ";
+ if(SymGetLineFromAddr64 && SymGetLineFromAddr64(GetCurrentProcess(), (size_t) addr, &displ, &ihl))
+ {
+ if(ihl.FileName && ihl.FileName[0])
+ {
+ char buffer[32769];
+ int len=WideCharToMultiByte(CP_UTF8, 0, ihl.FileName, -1, buffer, sizeof(buffer), nullptr, nullptr);
+ s.write(buffer, len-1);
+ s << ":" << ihl.LineNumber << " (+0x" << std::hex << displ << ")" << std::dec;
+ }
+ else
+ s << "unknown:0";
+ }
+ else
+ s << "completely unknown";
+ s << std::endl;
+ }
+}
+BOOST_AFIO_V2_NAMESPACE_END
+# else
+# include <dlfcn.h>
+// Set to 1 to use libunwind instead of glibc's stack backtracer
+# if 0
+# define UNW_LOCAL_ONLY
+# include <libunwind.h>
+# else
+# if defined(__linux__) || defined(__FreeBSD__) && !defined(__ANDROID__)
+# include <execinfo.h>
+# include <cxxabi.h>
+# endif
+# endif
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+typedef std::vector<void *> stack_type;
+inline void collect_stack(stack_type &stack)
+{
+#ifdef UNW_LOCAL_ONLY
+ // libunwind seems a bit more accurate than glibc's backtrace()
+ unw_context_t uc;
+ unw_cursor_t cursor;
+ unw_getcontext(&uc);
+ stack.reserve(BOOST_AFIO_OP_STACKBACKTRACEDEPTH);
+ unw_init_local(&cursor, &uc);
+ size_t n=0;
+ while(unw_step(&cursor)>0 && n++<BOOST_AFIO_OP_STACKBACKTRACEDEPTH)
+ {
+ unw_word_t ip;
+ unw_get_reg(&cursor, UNW_REG_IP, &ip);
+ stack.push_back((void *) ip);
+ }
+#else
+ stack.resize(BOOST_AFIO_OP_STACKBACKTRACEDEPTH);
+ stack.resize(backtrace(&stack.front(), stack.size()));
+#endif
+}
+extern "C" const char *__progname;
+inline void print_stack(std::ostream &s, const stack_type &stack)
+{
+ size_t n=0;
+ for(void *addr: stack)
+ {
+ s << " " << ++n << ". " << std::hex << addr << std::dec << ": ";
+ Dl_info info;
+ if(dladdr(addr, &info))
+ {
+ if(info.dli_fbase)
+ {
+ // This is hacky ...
+ char buffer2[4096];
+ sprintf(buffer2, "/usr/bin/addr2line -C -f -i -e \"%s\" %lx", info.dli_fname, (long)((size_t) addr - (size_t) info.dli_fbase));
+ //std::cout << buffer2 << std::endl;
+ FILE *ih=nullptr; // popen(buffer2, "r"); addr2line isn't currently working with my binaries on Linux nor FreeBSD, and it's very slow.
+ auto unih=detail::Undoer([&ih]{ if(ih) pclose(ih); });
+ bool done=false;
+ if(ih)
+ {
+ size_t length=fread(buffer2, 1, sizeof(buffer2), ih);
+ buffer2[length]=0;
+ std::string buffer;
+ for(size_t n=0; n<length-1; n++)
+ {
+ if(buffer2[n]=='\n')
+ buffer.append("\n ");
+ else
+ buffer.push_back(buffer2[n]);
+ }
+ if(buffer[0]!='?' && buffer[1]!='?')
+ {
+ s << buffer << " (+0x" << std::hex << ((size_t) addr - (size_t) info.dli_saddr) << ")" << std::dec;
+ done=true;
+ }
+ }
+ if(!done)
+ {
+ s << info.dli_fname << " (+0x" << std::hex << ((size_t) addr - (size_t) info.dli_fbase) << ") ";
+ if(info.dli_saddr)
+ {
+ char *demangled=nullptr;
+ auto undemangled=detail::Undoer([&demangled]{ if(demangled) free(demangled); });
+ int status;
+ if((demangled=abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status)))
+ {
+ s << demangled << " (0x" << info.dli_saddr << "+0x" << std::hex << ((size_t) addr - (size_t) info.dli_saddr) << ")" << std::dec;
+ done=true;
+ }
+ }
+ if(!done)
+ s << info.dli_sname << " (0x" << info.dli_saddr << "+0x" << std::hex << ((size_t) addr - (size_t) info.dli_saddr) << ")" << std::dec;
+ }
+ }
+ else
+ s << "unknown:0";
+ }
+ else
+ s << "completely unknown";
+ s << std::endl;
+ }
+}
+BOOST_AFIO_V2_NAMESPACE_END
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+struct afio_exception_stack_entry
+{
+ std::string name;
+ stack_type stack;
+};
+typedef std::vector<afio_exception_stack_entry> afio_exception_stack_t;
+inline afio_exception_stack_t *&afio_exception_stack()
+{
+ static BOOST_AFIO_THREAD_LOCAL afio_exception_stack_t *s;
+ return s;
+}
+BOOST_AFIO_V2_NAMESPACE_END
+#endif
+
+#include "ErrorHandling.ipp"
+
+#ifdef WIN32
+#ifndef IOV_MAX
+#define IOV_MAX 1024
+#endif
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+struct iovec {
+ void *iov_base; /* Starting address */
+ size_t iov_len; /* Number of bytes to transfer */
+};
+typedef ptrdiff_t ssize_t;
+static spinlock<bool> preadwritelock;
+inline ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset)
+{
+ off_t at=offset;
+ ssize_t transferred;
+ lock_guard<decltype(preadwritelock)> lockh(preadwritelock);
+ if(-1==_lseeki64(fd, offset, SEEK_SET)) return -1;
+ for(; iovcnt; iov++, iovcnt--, at+=(off_t) transferred)
+ if(-1==(transferred=_read(fd, iov->iov_base, (unsigned) iov->iov_len))) return -1;
+ return (ssize_t)(at-offset);
+}
+inline ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset)
+{
+ off_t at=offset;
+ ssize_t transferred;
+ lock_guard<decltype(preadwritelock)> lockh(preadwritelock);
+ if(-1==_lseeki64(fd, offset, SEEK_SET)) return -1;
+ for(; iovcnt; iov++, iovcnt--, at+=(off_t) transferred)
+ if(-1==(transferred=_write(fd, iov->iov_base, (unsigned) iov->iov_len))) return -1;
+ return (ssize_t)(at-offset);
+}
+inline ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
+{
+ off_t at=0;
+ ssize_t transferred;
+ lock_guard<decltype(preadwritelock)> lockh(preadwritelock);
+ for(; iovcnt; iov++, iovcnt--, at+=(off_t) transferred)
+ if(-1==(transferred=_write(fd, iov->iov_base, (unsigned) iov->iov_len))) return -1;
+ return (ssize_t)(at);
+}
+BOOST_AFIO_V2_NAMESPACE_END
+
+#elif !defined(IOV_MAX) || defined(__APPLE__) // Android lacks preadv() and pwritev()
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+#ifndef IOV_MAX
+#define IOV_MAX 1024
+struct iovec {
+ void *iov_base; /* Starting address */
+ size_t iov_len; /* Number of bytes to transfer */
+};
+typedef ptrdiff_t ssize_t;
+#endif
+inline ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset)
+{
+ off_t at=offset;
+ ssize_t transferred;
+ for(; iovcnt; iov++, iovcnt--, at+=(off_t) transferred)
+ if(-1==(transferred=pread(fd, iov->iov_base, (unsigned) iov->iov_len, at))) return -1;
+ return (ssize_t)(at-offset);
+}
+inline ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset)
+{
+ off_t at=offset;
+ ssize_t transferred;
+ for(; iovcnt; iov++, iovcnt--, at+=(off_t) transferred)
+ if(-1==(transferred=pwrite(fd, iov->iov_base, (unsigned) iov->iov_len, at))) return -1;
+ return (ssize_t)(at-offset);
+}
+BOOST_AFIO_V2_NAMESPACE_END
+
+#endif
+
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+std::shared_ptr<std_thread_pool> process_threadpool()
+{
+ // This is basically how many file i/o operations can occur at once
+ // Obviously the kernel also has a limit
+ static std::weak_ptr<std_thread_pool> shared;
+ static spinlock<bool> lock;
+ std::shared_ptr<std_thread_pool> ret(shared.lock());
+ if(!ret)
+ {
+ lock_guard<decltype(lock)> lockh(lock);
+ ret=shared.lock();
+ if(!ret)
+ {
+ shared=ret=std::make_shared<std_thread_pool>(BOOST_AFIO_MAX_NON_ASYNC_QUEUE_DEPTH);
+ }
+ }
+ return ret;
+}
+
+#ifndef BOOST_AFIO_COMPILING_FOR_GCOV
+// Experimental file region locking
+namespace detail {
+ struct process_lockfile_registry;
+ struct actual_lock_file;
+ template<class T> struct lock_file;
+ typedef spinlock<bool> process_lockfile_registry_lock_t;
+ static process_lockfile_registry_lock_t process_lockfile_registry_lock;
+ static std::unique_ptr<process_lockfile_registry> process_lockfile_registry_ptr;
+
+ struct process_lockfile_registry
+ {
+ std::unordered_map<handle *, lock_file<actual_lock_file> *> handle_to_lockfile;
+ std::unordered_map<path, std::weak_ptr<actual_lock_file>, path_hash> path_to_lockfile;
+ template<class T> static std::unique_ptr<T> open(handle *h)
+ {
+ lock_guard<process_lockfile_registry_lock_t> g(process_lockfile_registry_lock);
+ if(!process_lockfile_registry_ptr)
+ process_lockfile_registry_ptr=make_unique<process_lockfile_registry>();
+ auto p=detail::make_unique<T>(h);
+ process_lockfile_registry_ptr->handle_to_lockfile.insert(std::make_pair(h, (lock_file<actual_lock_file> *) p.get()));
+ return p;
+ }
+ };
+ struct actual_lock_file : std::enable_shared_from_this<actual_lock_file>
+ {
+ // TODO FIXME This needs to cope with sudden file relocations
+ BOOST_AFIO_V2_NAMESPACE::path path, lockfilepath;
+ protected:
+ actual_lock_file(BOOST_AFIO_V2_NAMESPACE::path p) : path(p), lockfilepath(p)
+ {
+ lockfilepath+=".afiolockfile";
+ }
+ public:
+ ~actual_lock_file()
+ {
+ lock_guard<process_lockfile_registry_lock_t> g(process_lockfile_registry_lock);
+ process_lockfile_registry_ptr->path_to_lockfile.erase(path);
+ }
+ virtual dispatcher::completion_returntype lock(size_t id, future<> op, lock_req req, void *)=0;
+ };
+ struct posix_actual_lock_file : public actual_lock_file
+ {
+ int h;
+#ifndef WIN32
+ std::vector<struct flock> local_locks;
+#endif
+ std::vector<lock_req> locks;
+ posix_actual_lock_file(BOOST_AFIO_V2_NAMESPACE::path p) : actual_lock_file(std::move(p)), h(0)
+ {
+#ifndef WIN32
+ bool done=false;
+ do
+ {
+ struct stat before, after;
+ BOOST_AFIO_ERRHOS(lstat(path.c_str(), &before));
+ BOOST_AFIO_ERRHOS(h=open(lockfilepath.c_str(), O_CREAT|O_RDWR, before.st_mode));
+ // Place a read lock on the final byte as a way of detecting when this lock file no longer in use
+ struct flock l;
+ l.l_type=F_RDLCK;
+ l.l_whence=SEEK_SET;
+ l.l_start=std::numeric_limits<decltype(l.l_start)>::max()-1;
+ l.l_len=1;
+ int retcode;
+ while(-1==(retcode=fcntl(h, F_SETLKW, &l)) && EINTR==errno);
+ // Between the time of opening the lock file and indicating we are using it did someone else delete it
+ // or even delete and recreate it?
+ if(-1!=fstat(h, &before) && -1!=lstat(lockfilepath.c_str(), &after) && before.st_ino==after.st_ino)
+ done=true;
+ else
+ ::close(h);
+ } while(!done);
+#endif
+ }
+ ~posix_actual_lock_file()
+ {
+#ifndef WIN32
+ // Place a write lock on the final byte as a way of detecting when this lock file no longer in use
+ struct flock l;
+ l.l_type=F_WRLCK;
+ l.l_whence=SEEK_SET;
+ l.l_start=std::numeric_limits<decltype(l.l_start)>::max()-1;
+ l.l_len=1;
+ int retcode;
+ while(-1==(retcode=fcntl(h, F_SETLK, &l)) && EINTR==errno);
+ if(-1!=retcode)
+ BOOST_AFIO_ERRHOS(unlink(lockfilepath.c_str()));
+ BOOST_AFIO_ERRHOS(::close(h));
+ lock_guard<process_lockfile_registry_lock_t> g(process_lockfile_registry_lock);
+ process_lockfile_registry_ptr->path_to_lockfile.erase(path);
+#endif
+ }
+ virtual dispatcher::completion_returntype lock(size_t id, future<> op, lock_req req, void *) override final
+ {
+#ifndef WIN32
+ struct flock l;
+ switch(req.type)
+ {
+ case lock_req::Type::read_lock:
+ l.l_type=F_RDLCK;
+ break;
+ case lock_req::Type::write_lock:
+ l.l_type=F_WRLCK;
+ break;
+ case lock_req::Type::unlock:
+ l.l_type=F_UNLCK;
+ break;
+ default:
+ BOOST_AFIO_THROW(std::invalid_argument("Lock type cannot be unknown"));
+ }
+ l.l_whence=SEEK_SET;
+ // We use the last byte for deleting the lock file on last use, so clamp to second last byte
+ if(req.offset==(off_t)(std::numeric_limits<decltype(l.l_start)>::max()-1))
+ BOOST_AFIO_THROW(std::invalid_argument("offset cannot be (1<<62)-1"));
+ l.l_start=(::off_t) req.offset;
+ ::off_t end=(::off_t) std::min(req.offset+req.length, (off_t)(std::numeric_limits<decltype(l.l_len)>::max()-1));
+ l.l_len=end-l.l_start;
+ // TODO FIXME: Run through local_locks with some async algorithm before dropping onto fcntl().
+ int retcode;
+ while(-1==(retcode=fcntl(h, F_SETLKW, &l)) && EINTR==errno)
+ /*empty*/;
+ BOOST_AFIO_ERRHOS(retcode);
+#endif
+ return std::make_pair(true, op.get_handle());
+ }
+ };
+ template<class T> struct lock_file
+ {
+ friend struct process_lockfile_registry;
+ handle *h;
+ std::vector<lock_req> locks;
+ std::shared_ptr<actual_lock_file> actual;
+ lock_file(handle *_h=nullptr) : h(_h)
+ {
+ if(h)
+ {
+ auto mypath=h->path(true);
+ auto it=process_lockfile_registry_ptr->path_to_lockfile.find(mypath);
+ if(process_lockfile_registry_ptr->path_to_lockfile.end()!=it)
+ actual=it->second.lock();
+ if(!actual)
+ {
+ if(process_lockfile_registry_ptr->path_to_lockfile.end()!=it)
+ process_lockfile_registry_ptr->path_to_lockfile.erase(it);
+ actual=std::make_shared<T>(mypath);
+ process_lockfile_registry_ptr->path_to_lockfile.insert(std::make_pair(mypath, actual));
+ }
+ }
+ }
+ dispatcher::completion_returntype lock(size_t id, future<> op, lock_req req)
+ {
+ return actual->lock(id, std::move(op), std::move(req), this);
+ }
+ ~lock_file()
+ {
+ lock_guard<process_lockfile_registry_lock_t> g(process_lockfile_registry_lock);
+ process_lockfile_registry_ptr->handle_to_lockfile.erase(h);
+ }
+ };
+ typedef lock_file<posix_actual_lock_file> posix_lock_file;
+}
+#endif
+
+
+namespace detail {
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void print_fatal_exception_message_to_stderr(const char *msg)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("FATAL EXCEPTION: " << msg << std::endl);
+#ifdef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+ stack_type stack;
+ collect_stack(stack);
+ std::stringstream stacktxt;
+ print_stack(stacktxt, stack);
+ BOOST_AFIO_LOG_FATAL_EXIT(stacktxt.str() << std::endl);
+#endif
+ }
+
+ // Returns a handle to the directory to be used as the base for the relative path fragment
+ template<class Impl, class Handle> handle_ptr decode_relative_path(path_req &req, bool force_absolute)
+ {
+ if(!req.is_relative)
+ return handle_ptr();
+ if(!req.precondition.valid())
+ return handle_ptr();
+ Handle *p=static_cast<Handle *>(&*req.precondition);
+retry:
+ path_req parentpath(p->path(true));
+ if(!force_absolute)
+ {
+#if BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS || defined(WIN32)
+ if(p->opened_as_dir()) // If base handle is a directory ...
+ {
+ if(req.path.empty()) // If path fragment means self, return containing directory and set path fragment to leafname
+ {
+ req.path=parentpath.path.filename();
+ parentpath.path=parentpath.path.parent_path();
+ }
+ else if(p->available_to_directory_cache())
+ return req.precondition.get_handle(); // always valid and a directory
+ try
+ {
+ return p->parent()->p->get_handle_to_dir(static_cast<Impl *>(p->parent()), 0, parentpath, &Impl::dofile);
+ }
+ catch(...)
+ {
+ // Path of p is stale, so loop
+ goto retry;
+ }
+ }
+ else
+ {
+ // If path fragment isn't empty or doesn't begin with ../, fail
+ if(req.path.empty())
+ req.path=parentpath.path.filename();
+ else
+ {
+ if(req.path.native()[0]!='.' || req.path.native()[1]!='.')
+ BOOST_AFIO_THROW(std::invalid_argument("Cannot use a path fragment inside a file, try prepending a ../"));
+ // Trim the preceding ..
+ auto &nativepath=const_cast<BOOST_AFIO_V2_NAMESPACE::path::string_type &>(req.path.native());
+ if(nativepath.size()>=3)
+ nativepath=nativepath.substr(3);
+ else
+ nativepath.clear();
+ }
+ handle_ptr dirh=p->container(); // quite likely empty
+ if(dirh)
+ return dirh;
+ else
+ return p->parent()->int_get_handle_to_containing_dir(static_cast<Impl *>(p->parent()), 0, parentpath, &Impl::dofile);
+ }
+ #endif
+ }
+ req.path=parentpath.path/req.path;
+ req.is_relative=false;
+ return handle_ptr();
+ }
+
+
+ struct async_io_handle_posix : public handle
+ {
+ int fd; // -999 is closed handle
+ bool has_been_added, DeleteOnClose, SyncOnClose, has_ever_been_fsynced;
+ dev_t st_dev; // Stored on first open. Used to detect races later.
+ ino_t st_ino;
+ typedef spinlock<bool> pathlock_t;
+ mutable pathlock_t pathlock; BOOST_AFIO_V2_NAMESPACE::path _path;
+#ifndef BOOST_AFIO_COMPILING_FOR_GCOV
+ std::unique_ptr<posix_lock_file> lockfile;
+#endif
+
+ async_io_handle_posix(dispatcher *_parent, const BOOST_AFIO_V2_NAMESPACE::path &path, file_flags flags, bool _DeleteOnClose, bool _SyncOnClose, int _fd) : handle(_parent, flags), fd(_fd), has_been_added(false), DeleteOnClose(_DeleteOnClose), SyncOnClose(_SyncOnClose), has_ever_been_fsynced(false), st_dev(0), st_ino(0), _path(path)
+ {
+ if(fd!=-999)
+ {
+ BOOST_AFIO_ERRHOSFN(fd, [&path]{return path;});
+ }
+ if(!(flags & file_flags::no_race_protection))
+ {
+ BOOST_AFIO_POSIX_STAT_STRUCT s={0};
+ if(-999==fd)
+ BOOST_AFIO_POSIX_LSTATAT(at_fdcwd, path.c_str(), &s); // Potential race between symlink creation and lstat here
+ else
+ BOOST_AFIO_POSIX_FSTAT(fd, &s);
+ st_dev=s.st_dev;
+ st_ino=s.st_ino;
+ }
+#ifndef BOOST_AFIO_COMPILING_FOR_GCOV
+ if(!!(flags & file_flags::os_lockable))
+ lockfile=process_lockfile_registry::open<posix_lock_file>(this);
+#endif
+ }
+ //! Returns the containing directory if my inode appears in it
+ inline handle_ptr int_verifymyinode();
+ void update_path(BOOST_AFIO_V2_NAMESPACE::path oldpath, BOOST_AFIO_V2_NAMESPACE::path newpath)
+ {
+ // For now, always zap the container
+ dirh.reset();
+ if(available_to_directory_cache())
+ parent()->int_directory_cached_handle_path_changed(oldpath, newpath, shared_from_this());
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void close() override final
+ {
+ BOOST_AFIO_DEBUG_PRINT("D %p\n", this);
+ int _fd=fd;
+ if(fd>=0)
+ {
+ if(SyncOnClose && write_count_since_fsync())
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_FSYNC(fd), [this]{return path();});
+ if(DeleteOnClose)
+ async_io_handle_posix::unlink();
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_CLOSE(fd), [this]{return path();});
+ fd=-999;
+ }
+ // Deregister AFTER close of file handle
+ if(has_been_added)
+ {
+ parent()->int_del_io_handle((void *) (size_t) _fd);
+ has_been_added=false;
+ }
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC open_states is_open() const override final
+ {
+ if(-999==fd)
+ return open_states::closed;
+ return available_to_directory_cache() ? open_states::opendir : open_states::open;
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void *native_handle() const override final { return (void *)(uintptr_t)fd; }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path(bool refresh=false) override final
+ {
+ if(refresh)
+ {
+#if !defined(WIN32)
+ if(-999==fd)
+ {
+#if 0
+ auto mycontainer=container();
+ if(mycontainer)
+ mycontainer->path(true);
+#endif
+ }
+ else
+ {
+ BOOST_AFIO_V2_NAMESPACE::path newpath;
+#if defined(__linux__)
+ // Linux keeps a symlink at /proc/self/fd/n
+ char in[PATH_MAX+1], out[32769];
+ sprintf(in, "/proc/self/fd/%d", fd);
+ ssize_t len;
+ if((len = readlink(in, out, sizeof(out)-1)) == -1)
+ BOOST_AFIO_ERRGOS(-1);
+ path::string_type ret(out, len);
+ // Linux prepends or appends a " (deleted)" when a fd is nameless
+ // TODO: Should I stat the target to be really sure?
+ if(ret.size()<10 || (ret.compare(0, 10, " (deleted)") && ret.compare(ret.size()-10, 10, " (deleted)")))
+ newpath=ret;
+#elif defined(__APPLE__)
+ // Apple returns the previous path when deleted, so lstat to be sure
+ struct stat s;
+ BOOST_AFIO_ERRHOS(fstat(fd, &s));
+ if(s.st_nlink)
+ {
+ // Apple also annoyingly returns some random hard link when nlink>1, so disable fetching new when that is the case
+ if(s.st_nlink>1)
+ {
+ {
+ lock_guard<pathlock_t> g(pathlock);
+ newpath=_path;
+ }
+ struct stat ls;
+ bool exists=(-1!=::lstat(newpath.c_str(), &ls)) && s.st_dev==ls.st_dev && s.st_ino==ls.st_ino;
+ if(!exists)
+ newpath.clear();
+ }
+ else
+ {
+ char buffer[PATH_MAX+1];
+ BOOST_AFIO_ERRHOS(fcntl(fd, F_GETPATH, buffer));
+ newpath=path::string_type(buffer);
+ }
+ }
+#elif defined(__FreeBSD__)
+ // Unfortunately this call is broken on FreeBSD 10 where it is currently returning
+ // null paths most of the time for regular files. Directories work perfectly. I've
+ // logged a bug with test case at https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=197695.
+ if(!opened_as_dir())
+ {
+ struct stat s;
+ BOOST_AFIO_ERRHOS(fstat(fd, &s));
+ if(s.st_nlink)
+ {
+ lock_guard<pathlock_t> g(pathlock);
+ newpath=_path;
+ }
+#if 0 // We don't attempt refresh semantics as none are promised on FreeBSD for files
+ //if(s.st_nlink>1)
+ {
+ struct stat ls;
+ bool exists=(-1!=::lstat(newpath.c_str(), &ls)) && s.st_dev==ls.st_dev && s.st_ino==ls.st_ino;
+ if(!exists)
+ newpath.clear();
+ }
+#endif
+ }
+ else
+ {
+ size_t len;
+ int mib[4]={CTL_KERN, KERN_PROC, KERN_PROC_FILEDESC, getpid()};
+ BOOST_AFIO_ERRHOS(sysctl(mib, 4, NULL, &len, NULL, 0));
+ std::vector<char> buffer(len*2);
+ BOOST_AFIO_ERRHOS(sysctl(mib, 4, buffer.data(), &len, NULL, 0));
+#if 0 //ndef NDEBUG
+ for(char *p=buffer.data(); p<buffer.data()+len;)
+ {
+ struct kinfo_file *kif=(struct kinfo_file *) p;
+ std::cout << kif->kf_type << " " << kif->kf_fd << " " << kif->kf_path << std::endl;
+ p+=kif->kf_structsize;
+ }
+#endif
+ for(char *p=buffer.data(); p<buffer.data()+len;)
+ {
+ struct kinfo_file *kif=(struct kinfo_file *) p;
+ if(kif->kf_fd==fd)
+ {
+ //struct stat s;
+ //BOOST_AFIO_ERRHOS(fstat(fd, &s));
+ //if(s.st_nlink && kif->kf_path[0])
+ newpath=path::string_type(kif->kf_path);
+ //else if(!s.st_nlink)
+ // newpath.clear();
+ break;
+ }
+ p+=kif->kf_structsize;
+ }
+ }
+#else
+#error Unknown system
+#endif
+ bool changed=false;
+ BOOST_AFIO_V2_NAMESPACE::path oldpath;
+ {
+ lock_guard<pathlock_t> g(pathlock);
+ if((changed=(_path!=newpath)))
+ {
+ oldpath=std::move(_path);
+ _path=newpath;
+ }
+ }
+ if(changed)
+ update_path(oldpath, newpath);
+ }
+#endif
+ }
+ lock_guard<pathlock_t> g(pathlock);
+ return _path;
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path() const override final
+ {
+ lock_guard<pathlock_t> g(pathlock);
+ return _path;
+ }
+
+ // You can't use shared_from_this() nor virtual functions in a constructor so ...
+ void do_add_io_handle_to_parent()
+ {
+ if(fd!=-999)
+ {
+ // Canonicalise my path
+ auto newpath=path(true);
+ parent()->int_add_io_handle((void *) (size_t) fd, shared_from_this());
+ has_been_added=true;
+ // If I'm the right sort of directory, register myself with the dircache. path(true) on some platforms may have
+ // already done this, but it's not much harm to repeat myself.
+ if(available_to_directory_cache())
+ parent()->int_directory_cached_handle_path_changed(BOOST_AFIO_V2_NAMESPACE::path(), newpath, shared_from_this());
+ }
+ if(!!(flags() & file_flags::hold_parent_open) && !(flags() & file_flags::int_hold_parent_open_nested))
+ dirh=int_verifymyinode();
+ }
+ ~async_io_handle_posix()
+ {
+ async_io_handle_posix::close();
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC directory_entry direntry(metadata_flags wanted) override final
+ {
+ stat_t stat(nullptr);
+ BOOST_AFIO_POSIX_STAT_STRUCT s={0};
+ if(-999==fd) // Probably a symlink
+ {
+#if BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS
+ if(!(flags() & file_flags::no_race_protection))
+ {
+ auto dirh(int_verifymyinode());
+ if(!dirh)
+ {
+ errno=ENOENT;
+ BOOST_AFIO_ERRHOSFN(-1, [this]{return path();});
+ }
+ BOOST_AFIO_V2_NAMESPACE::path leaf(path().filename());
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_LSTATAT((int)(size_t)dirh->native_handle(), leaf.c_str(), &s), [this]{return path();});
+ }
+ else
+#endif
+ {
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_LSTATAT(at_fdcwd, path().c_str(), &s), [this]{return path();});
+ }
+ fill_stat_t(stat, s, wanted);
+ return directory_entry(path().filename().native(), stat, wanted);
+ }
+ else
+ {
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_FSTAT(fd, &s), [this]{return path();});
+ fill_stat_t(stat, s, wanted);
+ return directory_entry(const_cast<async_io_handle_posix *>(this)->path(true).filename().native(), stat, wanted);
+ }
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path target() override final
+ {
+#ifdef WIN32
+ return path();
+#else
+ if(!opened_as_symlink())
+ return path();
+ char buffer[PATH_MAX+1];
+ ssize_t len;
+#if BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS
+ // Some platforms allow the opening of links as a file descriptor
+ if(-999!=fd)
+ {
+ if((len = readlinkat(fd, "", buffer, sizeof(buffer)-1)) == -1)
+ BOOST_AFIO_ERRGOS(-1);
+ return path::string_type(buffer, len);
+ }
+ if(!(flags() & file_flags::no_race_protection))
+ {
+ auto dirh(int_verifymyinode());
+ if(!dirh)
+ {
+ errno=ENOENT;
+ BOOST_AFIO_ERRHOSFN(-1, [this]{return path();});
+ }
+ BOOST_AFIO_V2_NAMESPACE::path leaf(path().filename());
+ if((len = readlinkat((int)(size_t)dirh->native_handle(), leaf.c_str(), buffer, sizeof(buffer)-1)) == -1)
+ BOOST_AFIO_ERRGOS(-1);
+ }
+ else
+#endif
+ {
+ if((len = readlink(path().c_str(), buffer, sizeof(buffer)-1)) == -1)
+ BOOST_AFIO_ERRGOS(-1);
+ }
+ return path::string_type(buffer, len);
+#endif
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::unique_ptr<mapped_file> map_file(size_t length, off_t offset, bool read_only) override final
+ {
+#ifndef WIN32
+ BOOST_AFIO_POSIX_STAT_STRUCT s={0};
+ if(-1!=fstat(fd, &s))
+ {
+ if(length>(size_t) s.st_size)
+ length=(size_t) s.st_size;
+ if(offset+length>(size_t) s.st_size)
+ length=(size_t) s.st_size-offset;
+ void *mapaddr=nullptr;
+ int prot=PROT_READ;
+ if(!read_only && !!(flags() & file_flags::write))
+ prot|=PROT_WRITE;
+ if((mapaddr=BOOST_AFIO_POSIX_MMAP(nullptr, length, prot, MAP_SHARED, fd, offset)))
+ {
+ if(!!(flags() & file_flags::will_be_sequentially_accessed))
+ madvise(mapaddr, length, MADV_SEQUENTIAL);
+ else if(!!(flags() & file_flags::will_be_randomly_accessed))
+ madvise(mapaddr, length, MADV_RANDOM);
+ return detail::make_unique<mapped_file>(shared_from_this(), mapaddr, length, offset);
+ }
+ }
+#endif
+ return nullptr;
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void link(const path_req &_req) override final
+ {
+ path_req req(_req);
+#ifndef WIN32
+ if(-999!=fd)
+ {
+ auto newdirh=decode_relative_path<async_file_io_dispatcher_compat, async_io_handle_posix>(req);
+ if(!(flags() & file_flags::no_race_protection))
+ {
+#if BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS
+ // Some platforms may allow direct hard linking of file handles, in which case we are done
+#ifdef AT_EMPTY_PATH
+ if(fd>=0 && -1!=BOOST_AFIO_POSIX_LINKAT(fd, "", newdirh ? (int)(size_t)newdirh->native_handle() : at_fdcwd, req.path.c_str(), AT_EMPTY_PATH))
+ return;
+#endif
+ auto dirh(int_verifymyinode());
+ if(!dirh)
+ {
+ errno=ENOENT;
+ BOOST_AFIO_ERRHOSFN(-1, [this]{return path();});
+ }
+ BOOST_AFIO_V2_NAMESPACE::path leaf(path().filename());
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_LINKAT((int)(size_t)dirh->native_handle(), leaf.c_str(), newdirh ? (int)(size_t)newdirh->native_handle() : at_fdcwd, req.path.c_str(), 0), [this]{return path();});
+#else
+ // At least check if what I am about to delete matches myself
+ BOOST_AFIO_POSIX_STAT_STRUCT s={0};
+ BOOST_AFIO_V2_NAMESPACE::path p(path(true));
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_LSTATAT(at_fdcwd, p.c_str(), &s), [this]{return path();});
+ if(s.st_dev!=st_dev || s.st_ino!=st_ino)
+ {
+ errno=ENOENT;
+ BOOST_AFIO_ERRHOSFN(-1, [this]{return path();});
+ }
+ // Could of course still race between the lstat() and the unlink() so still unsafe ...
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_LINKAT(at_fdcwd, p.c_str(), at_fdcwd, req.path.c_str(), 0), [this]{return path();});
+#endif
+ return;
+ }
+ }
+ decode_relative_path<async_file_io_dispatcher_compat, async_io_handle_posix>(req, true);
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_LINKAT(at_fdcwd, path(true).c_str(), at_fdcwd, req.path.c_str(), 0), [this]{return path();});
+#endif
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlink() override final
+ {
+ if(-999!=fd)
+ {
+ if(!(flags() & file_flags::no_race_protection))
+ {
+#if BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS
+ // Some platforms may allow direct deletion of file handles, in which case we are done
+#ifdef AT_EMPTY_PATH
+ if(fd>=0 && -1!=BOOST_AFIO_POSIX_UNLINKAT(fd, "", opened_as_dir() ? AT_EMPTY_PATH|AT_REMOVEDIR : AT_EMPTY_PATH))
+ goto update_path;
+#endif
+ auto dirh(int_verifymyinode());
+ if(!dirh)
+ {
+ errno=ENOENT;
+ BOOST_AFIO_ERRHOSFN(-1, [this]{return path();});
+ }
+ BOOST_AFIO_V2_NAMESPACE::path leaf(path().filename());
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_UNLINKAT((int)(size_t)dirh->native_handle(), leaf.c_str(), opened_as_dir() ? AT_REMOVEDIR : 0), [this]{return path();});
+#else
+ // At least check if what I am about to delete matches myself
+ BOOST_AFIO_POSIX_STAT_STRUCT s={0};
+ BOOST_AFIO_V2_NAMESPACE::path p(path(true));
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_LSTATAT(at_fdcwd, p.c_str(), &s), [this]{return path();});
+ if(s.st_dev!=st_dev || s.st_ino!=st_ino)
+ {
+ errno=ENOENT;
+ BOOST_AFIO_ERRHOSFN(-1, [this]{return path();});
+ }
+ // Could of course still race between the lstat() and the unlink() so still unsafe ...
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_UNLINKAT(at_fdcwd, p.c_str(), opened_as_dir() ? AT_REMOVEDIR : 0), [this]{return path();});
+#endif
+ goto update_path;
+ }
+ }
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_UNLINKAT(at_fdcwd, path(true).c_str(), opened_as_dir() ? AT_REMOVEDIR : 0), [this]{return path();});
+update_path:
+ BOOST_AFIO_V2_NAMESPACE::path oldpath, newpath;
+ {
+ lock_guard<pathlock_t> g(pathlock);
+ oldpath=std::move(_path);
+ }
+ update_path(oldpath, newpath);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void atomic_relink(const path_req &_req) override final
+ {
+ path_req req(_req);
+ if(-999!=fd)
+ {
+ auto newdirh=decode_relative_path<async_file_io_dispatcher_compat, async_io_handle_posix>(req);
+ if(!(flags() & file_flags::no_race_protection))
+ {
+#if BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS
+ auto dirh(int_verifymyinode());
+ if(!dirh)
+ {
+ errno=ENOENT;
+ BOOST_AFIO_ERRHOSFN(-1, [this]{return path();});
+ }
+ BOOST_AFIO_V2_NAMESPACE::path leaf(path().filename());
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_RENAMEAT((int)(size_t)dirh->native_handle(), leaf.c_str(), newdirh ? (int)(size_t)newdirh->native_handle() : at_fdcwd, req.path.c_str()), [this]{return path();});
+#else
+ // At least check if what I am about to delete matches myself
+ BOOST_AFIO_POSIX_STAT_STRUCT s={0};
+ BOOST_AFIO_V2_NAMESPACE::path p(path(true));
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_LSTATAT(at_fdcwd, p.c_str(), &s), [this]{return path();});
+ if(s.st_dev!=st_dev || s.st_ino!=st_ino)
+ {
+ errno=ENOENT;
+ BOOST_AFIO_ERRHOSFN(-1, [this]{return path();});
+ }
+ // Could of course still race between the lstat() and the unlink() so still unsafe ...
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_RENAMEAT(at_fdcwd, p.c_str(), at_fdcwd, req.path.c_str()), [this]{return path();});
+#endif
+ if(newdirh)
+ req.path=newdirh->path(true)/req.path;
+ goto update_path;
+ }
+ }
+ decode_relative_path<async_file_io_dispatcher_compat, async_io_handle_posix>(req, true);
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_RENAMEAT(at_fdcwd, path(true).c_str(), at_fdcwd, req.path.c_str()), [this]{return path();});
+update_path:
+ BOOST_AFIO_V2_NAMESPACE::path oldpath;
+ {
+ lock_guard<pathlock_t> g(pathlock);
+ oldpath=std::move(_path);
+ _path=req.path;
+ }
+ update_path(oldpath, req.path);
+ }
+ };
+
+ struct async_file_io_dispatcher_op
+ {
+ OpType optype;
+ async_op_flags flags;
+ enqueued_task<handle_ptr()> enqueuement;
+ typedef std::pair<size_t, std::shared_ptr<detail::async_file_io_dispatcher_op>> completion_t;
+ std::vector<completion_t> completions;
+ const shared_future<handle_ptr> &h() const { return enqueuement.get_future(); }
+#ifdef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+ stack_type stack;
+ void fillStack() { collect_stack(stack); }
+#else
+ void fillStack() { }
+#endif
+ async_file_io_dispatcher_op(OpType _optype, async_op_flags _flags)
+ : optype(_optype), flags(_flags)
+ {
+ // Stop the stl_future from being auto-set on task return
+ enqueuement.disable_auto_set_future();
+ //completions.reserve(4); // stop needless storage doubling for small numbers
+ fillStack();
+ }
+ async_file_io_dispatcher_op(async_file_io_dispatcher_op &&o) noexcept : optype(o.optype), flags(std::move(o.flags)),
+ enqueuement(std::move(o.enqueuement)), completions(std::move(o.completions))
+#ifdef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+ , stack(std::move(o.stack))
+#endif
+ {
+ }
+ private:
+ async_file_io_dispatcher_op(const async_file_io_dispatcher_op &o) = delete;
+ };
+ struct dispatcher_p
+ {
+ std::shared_ptr<thread_source> pool;
+ unit_testing_flags testing_flags;
+ file_flags flagsforce, flagsmask;
+ std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_t>>> filters;
+ std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_readwrite_t>>> filters_buffers;
+
+#ifdef BOOST_AFIO_USE_CONCURRENT_UNORDERED_MAP
+ typedef null_lock fdslock_t;
+ typedef null_lock opslock_t;
+#else
+ typedef spinlock<size_t> fdslock_t;
+ typedef spinlock<size_t,
+ spins_to_loop<100>::policy,
+ spins_to_yield<500>::policy,
+ spins_to_sleep::policy
+ > opslock_t;
+#endif
+ typedef recursive_mutex dircachelock_t;
+ fdslock_t fdslock; engine_unordered_map_t<void *, std::weak_ptr<handle>> fds;
+ opslock_t opslock; atomic<size_t> monotoniccount; engine_unordered_map_t<size_t, std::shared_ptr<async_file_io_dispatcher_op>> ops;
+ dircachelock_t dircachelock; std::unordered_map<path, std::weak_ptr<handle>, path_hash> dirhcache;
+
+ dispatcher_p(std::shared_ptr<thread_source> _pool, file_flags _flagsforce, file_flags _flagsmask) : pool(_pool),
+ testing_flags(unit_testing_flags::none), flagsforce(_flagsforce), flagsmask(_flagsmask), monotoniccount(0)
+ {
+#ifdef BOOST_AFIO_USE_CONCURRENT_UNORDERED_MAP
+ // concurrent_unordered_map doesn't lock, so we actually don't need many buckets for max performance
+ fds.min_bucket_capacity(2);
+ fds.reserve(499);
+ ops.min_bucket_capacity(2);
+ ops.reserve(499);
+#else
+ // unordered_map needs to release the lock as quickly as possible
+ ops.reserve(10000);
+#endif
+ }
+ ~dispatcher_p()
+ {
+ }
+
+ // Returns a handle to a directory from the cache, or creates a new directory handle.
+ template<class F> handle_ptr get_handle_to_dir(F *parent, size_t id, path_req req, typename dispatcher::completion_returntype(F::*dofile)(size_t, future<>, path_req))
+ {
+ assert(!req.is_relative);
+ req.flags=file_flags::int_hold_parent_open_nested|file_flags::int_opening_dir|file_flags::read;
+ // First refresh all paths in the cache, eliminating any stale ptrs
+ // One problem is that h->path(true) will update itself in dirhcache if it has changed, thus invalidating all the iterators
+ // so we first copy dirhcache into torefresh
+ std::vector<std::pair<path, std::weak_ptr<handle>>> torefresh;
+ {
+ lock_guard<dircachelock_t> dircachelockh(dircachelock);
+ torefresh.reserve(dirhcache.size());
+ for(auto it=dirhcache.begin(); it!=dirhcache.end();)
+ {
+ if(it->second.expired())
+ {
+#ifndef NDEBUG
+ std::cout << "afio: directory cached handle pruned stale " << it->first << std::endl;
+#endif
+ it=dirhcache.erase(it);
+ continue;
+ }
+ torefresh.push_back(*it);
+ ++it;
+ }
+ }
+ handle_ptr dirh;
+ for(auto &i : torefresh)
+ {
+ if((dirh=i.second.lock()))
+ dirh->path(true);
+ }
+ dirh.reset();
+ lock_guard<dircachelock_t> dircachelockh(dircachelock);
+ do
+ {
+ std::unordered_map<path, std::weak_ptr<handle>, path_hash>::iterator it=dirhcache.find(req.path);
+ if(dirhcache.end()!=it)
+ dirh=it->second.lock();
+ if(!dirh)
+ {
+ dispatcher::completion_returntype result=(parent->*dofile)(id, future<>(), req); // should recurse in to insert itself
+ dirh=std::move(result.second);
+#ifndef NDEBUG
+ if(dirh)
+ std::cout << "afio: directory cached handle created for " << req.path << " (" << dirh.get() << ")" << std::endl;
+#endif
+ if(dirh)
+ {
+ // May have renamed itself, if so add this entry point to the cache too
+ if(dirh->path()!=req.path)
+ dirhcache.insert(std::make_pair(req.path, dirh));
+ return dirh;
+ }
+ else
+ abort();
+ }
+ } while(!dirh);
+ return dirh;
+ }
+ };
+ class async_file_io_dispatcher_compat;
+ class async_file_io_dispatcher_windows;
+ class async_file_io_dispatcher_linux;
+ class async_file_io_dispatcher_qnx;
+ struct immediate_async_ops
+ {
+ typedef handle_ptr rettype;
+ typedef rettype retfuncttype();
+ size_t reservation;
+ std::vector<enqueued_task<retfuncttype>> toexecute;
+
+ immediate_async_ops(size_t reserve) : reservation(reserve) { }
+ // Returns a promise which is fulfilled when this is destructed
+ void enqueue(enqueued_task<retfuncttype> task)
+ {
+ if(toexecute.empty())
+ toexecute.reserve(reservation);
+ toexecute.push_back(task);
+ }
+ ~immediate_async_ops()
+ {
+ for(auto &i: toexecute)
+ {
+ i();
+ }
+ }
+ private:
+ immediate_async_ops(const immediate_async_ops &);
+ immediate_async_ops &operator=(const immediate_async_ops &);
+ immediate_async_ops(immediate_async_ops &&);
+ immediate_async_ops &operator=(immediate_async_ops &&);
+ };
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC metadata_flags directory_entry::metadata_supported() noexcept
+{
+ metadata_flags ret;
+#ifdef WIN32
+ ret=metadata_flags::None
+ //| metadata_flags::dev
+ | metadata_flags::ino // FILE_INTERNAL_INFORMATION, enumerated
+ | metadata_flags::type // FILE_BASIC_INFORMATION, enumerated
+ //| metadata_flags::perms
+ | metadata_flags::nlink // FILE_STANDARD_INFORMATION
+ //| metadata_flags::uid
+ //| metadata_flags::gid
+ //| metadata_flags::rdev
+ | metadata_flags::atim // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::mtim // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::ctim // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::size // FILE_STANDARD_INFORMATION, enumerated
+ | metadata_flags::allocated // FILE_STANDARD_INFORMATION, enumerated
+ | metadata_flags::blocks
+ | metadata_flags::blksize // FILE_ALIGNMENT_INFORMATION
+ //| metadata_flags::flags
+ //| metadata_flags::gen
+ | metadata_flags::birthtim // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::sparse // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::compressed // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::reparse_point // FILE_BASIC_INFORMATION, enumerated
+ ;
+#elif defined(__linux__)
+ ret=metadata_flags::None
+ | metadata_flags::dev
+ | metadata_flags::ino
+ | metadata_flags::type
+ | metadata_flags::perms
+ | metadata_flags::nlink
+ | metadata_flags::uid
+ | metadata_flags::gid
+ | metadata_flags::rdev
+ | metadata_flags::atim
+ | metadata_flags::mtim
+ | metadata_flags::ctim
+ | metadata_flags::size
+ | metadata_flags::allocated
+ | metadata_flags::blocks
+ | metadata_flags::blksize
+ // Sadly these must wait until someone fixes the Linux stat() call e.g. the xstat() proposal.
+ //| metadata_flags::flags
+ //| metadata_flags::gen
+ //| metadata_flags::birthtim
+ // According to http://computer-forensics.sans.org/blog/2011/03/14/digital-forensics-understanding-ext4-part-2-timestamps
+ // ext4 keeps birth time at offset 144 to 151 in the inode. If we ever got round to it, birthtime could be hacked.
+ | metadata_flags::sparse
+ //| metadata_flags::compressed
+ //| metadata_flags::reparse_point
+ ;
+#else
+ // Kinda assumes FreeBSD or OS X really ...
+ ret=metadata_flags::None
+ | metadata_flags::dev
+ | metadata_flags::ino
+ | metadata_flags::type
+ | metadata_flags::perms
+ | metadata_flags::nlink
+ | metadata_flags::uid
+ | metadata_flags::gid
+ | metadata_flags::rdev
+ | metadata_flags::atim
+ | metadata_flags::mtim
+ | metadata_flags::ctim
+ | metadata_flags::size
+ | metadata_flags::allocated
+ | metadata_flags::blocks
+ | metadata_flags::blksize
+ | metadata_flags::flags
+ | metadata_flags::gen
+ | metadata_flags::birthtim
+ | metadata_flags::sparse
+ //| metadata_flags::compressed
+ //| metadata_flags::reparse_point
+ ;
+#endif
+ return ret;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC metadata_flags directory_entry::metadata_fastpath() noexcept
+{
+ metadata_flags ret;
+#ifdef WIN32
+ ret=metadata_flags::None
+ //| metadata_flags::dev
+ | metadata_flags::ino // FILE_INTERNAL_INFORMATION, enumerated
+ | metadata_flags::type // FILE_BASIC_INFORMATION, enumerated
+ //| metadata_flags::perms
+ //| metadata_flags::nlink // FILE_STANDARD_INFORMATION
+ //| metadata_flags::uid
+ //| metadata_flags::gid
+ //| metadata_flags::rdev
+ | metadata_flags::atim // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::mtim // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::ctim // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::size // FILE_STANDARD_INFORMATION, enumerated
+ | metadata_flags::allocated // FILE_STANDARD_INFORMATION, enumerated
+ //| metadata_flags::blocks
+ //| metadata_flags::blksize // FILE_ALIGNMENT_INFORMATION
+ //| metadata_flags::flags
+ //| metadata_flags::gen
+ | metadata_flags::birthtim // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::sparse // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::compressed // FILE_BASIC_INFORMATION, enumerated
+ | metadata_flags::reparse_point // FILE_BASIC_INFORMATION, enumerated
+ ;
+#elif defined(__linux__)
+ ret=metadata_flags::None
+ | metadata_flags::dev
+ | metadata_flags::ino
+ | metadata_flags::type
+ | metadata_flags::perms
+ | metadata_flags::nlink
+ | metadata_flags::uid
+ | metadata_flags::gid
+ | metadata_flags::rdev
+ | metadata_flags::atim
+ | metadata_flags::mtim
+ | metadata_flags::ctim
+ | metadata_flags::size
+ | metadata_flags::allocated
+ | metadata_flags::blocks
+ | metadata_flags::blksize
+ // Sadly these must wait until someone fixes the Linux stat() call e.g. the xstat() proposal.
+ //| metadata_flags::flags
+ //| metadata_flags::gen
+ //| metadata_flags::birthtim
+ // According to http://computer-forensics.sans.org/blog/2011/03/14/digital-forensics-understanding-ext4-part-2-timestamps
+ // ext4 keeps birth time at offset 144 to 151 in the inode. If we ever got round to it, birthtime could be hacked.
+ | metadata_flags::sparse
+ //| metadata_flags::compressed
+ //| metadata_flags::reparse_point
+ ;
+#else
+ // Kinda assumes FreeBSD or OS X really ...
+ ret=metadata_flags::None
+ | metadata_flags::dev
+ | metadata_flags::ino
+ | metadata_flags::type
+ | metadata_flags::perms
+ | metadata_flags::nlink
+ | metadata_flags::uid
+ | metadata_flags::gid
+ | metadata_flags::rdev
+ | metadata_flags::atim
+ | metadata_flags::mtim
+ | metadata_flags::ctim
+ | metadata_flags::size
+ | metadata_flags::allocated
+ | metadata_flags::blocks
+ | metadata_flags::blksize
+ | metadata_flags::flags
+ | metadata_flags::gen
+ | metadata_flags::birthtim
+ | metadata_flags::sparse
+ //| metadata_flags::compressed
+ //| metadata_flags::reparse_point
+ ;
+#endif
+ return ret;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t directory_entry::compatibility_maximum() noexcept
+{
+#ifdef WIN32
+ // Let's choose 100k entries. Why not!
+ return 100000;
+#else
+ return 100000;
+ // This is what glibc uses, a 32Kb buffer.
+ //return 32768/sizeof(dirent);
+#endif
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher::dispatcher(std::shared_ptr<thread_source> threadpool, file_flags flagsforce, file_flags flagsmask) : p(new detail::dispatcher_p(threadpool, flagsforce, flagsmask))
+{
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void dispatcher::testing_flags(detail::unit_testing_flags flags)
+{
+ p->testing_flags=flags;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher::~dispatcher()
+{
+#ifndef BOOST_AFIO_COMPILING_FOR_GCOV
+ engine_unordered_map_t<const detail::async_file_io_dispatcher_op *, std::pair<size_t, future_status>> reallyoutstanding;
+ for(;;)
+ {
+ std::vector<std::pair<const size_t, std::shared_ptr<detail::async_file_io_dispatcher_op>>> outstanding;
+ {
+ lock_guard<decltype(p->opslock)> g(p->opslock); // may have no effect under concurrent_unordered_map
+ if(!p->ops.empty())
+ {
+ outstanding.reserve(p->ops.size());
+ for(auto &op: p->ops)
+ {
+ if(op.second->h().valid())
+ {
+ auto it=reallyoutstanding.find(op.second.get());
+ if(reallyoutstanding.end()!=it)
+ {
+ if(it->second.first>=5)
+ {
+ static const char *statuses[]={ "ready", "timeout", "deferred", "unknown" };
+ int status=static_cast<int>(it->second.second);
+ BOOST_AFIO_LOG_FATAL_EXIT("WARNING: ~async_file_dispatcher_base() detects stuck future<> in total of " << p->ops.size() << " extant ops\n"
+ " id=" << op.first << " type=" << detail::optypes[static_cast<size_t>(op.second->optype)] << " flags=0x" << std::hex << static_cast<size_t>(op.second->flags) << std::dec << " status=" << statuses[(status>=0 && status<=2) ? status : 3] << " failcount=" << it->second.first << " Completions:");
+ for(auto &c: op.second->completions)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT(" id=" << c.first);
+ }
+ BOOST_AFIO_LOG_FATAL_EXIT(std::endl);
+#ifdef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+ BOOST_AFIO_LOG_FATAL_EXIT(" Allocation backtrace:" << std::endl);
+ std::stringstream stacktxt;
+ print_stack(stacktxt, op.second->stack);
+ BOOST_AFIO_LOG_FATAL_EXIT(stacktxt.str() << std::endl);
+#endif
+ }
+ }
+ outstanding.push_back(op);
+ }
+ }
+ }
+ }
+ if(outstanding.empty()) break;
+ size_t mincount=(size_t)-1;
+ for(auto &op: outstanding)
+ {
+ future_status status=op.second->h().wait_for(chrono::duration<int, ratio<1, 10>>(1));
+ switch(status)
+ {
+ case future_status::ready:
+ reallyoutstanding.erase(op.second.get());
+ break;
+ case future_status::deferred:
+ // Probably pending on others, but log
+ case future_status::timeout:
+ auto it=reallyoutstanding.find(op.second.get());
+ if(reallyoutstanding.end()==it)
+ it=reallyoutstanding.insert(std::make_pair(op.second.get(), std::make_pair(0, status))).first;
+ it->second.first++;
+ if(it->second.first<mincount) mincount=it->second.first;
+ break;
+ }
+ }
+ if(mincount>=10 && mincount!=(size_t)-1) // i.e. nothing is changing
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("WARNING: ~async_file_dispatcher_base() sees no change in " << reallyoutstanding.size() << " stuck async_io_ops, so exiting destructor wait" << std::endl);
+ break;
+ }
+ }
+ for(size_t n=0; !p->fds.empty(); n++)
+ {
+ this_thread::sleep_for(chrono::milliseconds(10));
+ if(n>300)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("WARNING: ~async_file_dispatcher_base() sees no change in " << p->fds.size() << " stuck handle_ptr's, so exiting destructor wait" << std::endl);
+ break;
+ }
+ }
+#endif
+ delete p;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void dispatcher::int_add_io_handle(void *key, handle_ptr h)
+{
+ {
+ lock_guard<decltype(p->fdslock)> g(p->fdslock);
+ p->fds.insert(std::make_pair(key, std::weak_ptr<handle>(h)));
+ }
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void dispatcher::int_del_io_handle(void *key)
+{
+ {
+ lock_guard<decltype(p->fdslock)> g(p->fdslock);
+ p->fds.erase(key);
+ }
+}
+
+// Returns a handle to a containing directory from the cache, or creates a new directory handle.
+template<class F> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC handle_ptr dispatcher::int_get_handle_to_containing_dir(F *parent, size_t id, path_req req, typename dispatcher::completion_returntype(F::*dofile)(size_t, future<>, path_req))
+{
+ req.path=req.path.parent_path();
+ return p->get_handle_to_dir(parent, id, req, dofile);
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::shared_ptr<thread_source> dispatcher::threadsource() const
+{
+ return p->pool;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC file_flags dispatcher::fileflags(file_flags flags) const
+{
+ file_flags ret=(flags&~p->flagsmask)|p->flagsforce;
+ return ret;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t dispatcher::wait_queue_depth() const
+{
+ size_t ret=0;
+ {
+ lock_guard<decltype(p->opslock)> g(p->opslock);
+ ret=p->ops.size();
+ }
+ return ret;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC size_t dispatcher::fd_count() const
+{
+ size_t ret=0;
+ {
+ lock_guard<decltype(p->fdslock)> g(p->fdslock);
+ ret=p->fds.size();
+ }
+ return ret;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void dispatcher::int_directory_cached_handle_path_changed(path oldpath, path newpath, handle_ptr h)
+{
+ lock_guard<decltype(p->dircachelock)> dircachelockh(p->dircachelock);
+ if(!oldpath.empty())
+ {
+ auto it=p->dirhcache.find(oldpath);
+ if(it!=p->dirhcache.end())
+ {
+ //assert(it->second.lock()==h);
+ p->dirhcache.erase(it);
+ }
+ }
+#ifndef NDEBUG
+ std::cout << "afio: directory cached handle we relocate from " << oldpath << " to " << newpath << " (" << h.get() << ")" << std::endl;
+#endif
+ if(!newpath.empty())
+ p->dirhcache.insert(std::make_pair(std::move(newpath), std::move(h)));
+}
+
+
+// Non op lock holding variant
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> dispatcher::int_op_from_scheduled_id(size_t id) const
+{
+#ifdef BOOST_AFIO_USE_CONCURRENT_UNORDERED_MAP
+ future<> ret;
+ typedef decltype(p->ops)::value_type op_value_type;
+ if(!p->ops.visit(id, [this, id, &ret](const op_value_type &op){
+ ret=future<>(const_cast<dispatcher *>(this), id, op.second->h());
+ }))
+ {
+ BOOST_AFIO_THROW(std::runtime_error("Failed to find this operation in list of currently executing operations"));
+ }
+ return ret;
+#else
+ engine_unordered_map_t<size_t, std::shared_ptr<detail::async_file_io_dispatcher_op>>::iterator it=p->ops.find(id);
+ if(p->ops.end()==it)
+ {
+ BOOST_AFIO_THROW(std::runtime_error("Failed to find this operation in list of currently executing operations"));
+ }
+ return future<>(const_cast<dispatcher *>(this), id, it->second->h());
+#endif
+}
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> dispatcher::op_from_scheduled_id(size_t id) const
+{
+ future<> ret;
+ {
+ lock_guard<decltype(p->opslock)> g(p->opslock);
+ ret=int_op_from_scheduled_id(id);
+ }
+ return ret;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void dispatcher::post_op_filter_clear()
+{
+ p->filters.clear();
+ p->filters_buffers.clear();
+}
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void dispatcher::post_op_filter(std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_t>>> filters)
+{
+ p->filters.reserve(p->filters.size()+filters.size());
+ p->filters.insert(p->filters.end(), std::make_move_iterator(filters.begin()), std::make_move_iterator(filters.end()));
+}
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void dispatcher::post_readwrite_filter(std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_readwrite_t>>> filters)
+{
+ p->filters_buffers.reserve(p->filters_buffers.size()+filters.size());
+ p->filters_buffers.insert(p->filters_buffers.end(), std::make_move_iterator(filters.begin()), std::make_move_iterator(filters.end()));
+}
+
+
+#if defined(BOOST_AFIO_ENABLE_BENCHMARKING_COMPLETION) || BOOST_AFIO_HEADERS_ONLY==0
+// Called in unknown thread
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher::completion_returntype dispatcher::invoke_user_completion_fast(size_t id, future<> op, dispatcher::completion_t *callback)
+{
+ return callback(id, op);
+}
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> dispatcher::completion(const std::vector<future<>> &ops, const std::vector<std::pair<async_op_flags, dispatcher::completion_t *>> &callbacks)
+{
+ if(!ops.empty() && ops.size()!=callbacks.size())
+ BOOST_AFIO_THROW(std::runtime_error("The sequence of preconditions must either be empty or exactly the same length as callbacks."));
+ std::vector<future<>> ret;
+ ret.reserve(callbacks.size());
+ std::vector<future<>>::const_iterator i;
+ std::vector<std::pair<async_op_flags, dispatcher::completion_t *>>::const_iterator c;
+ detail::immediate_async_ops immediates(callbacks.size());
+ if(ops.empty())
+ {
+ future<> empty;
+ for(auto & c: callbacks)
+ {
+ ret.push_back(chain_async_op(immediates, (int) detail::OpType::UserCompletion, empty, c.first, &dispatcher::invoke_user_completion_fast, c.second));
+ }
+ }
+ else for(i=ops.begin(), c=callbacks.begin(); i!=ops.end() && c!=callbacks.end(); ++i, ++c)
+ ret.push_back(chain_async_op(immediates, (int) detail::OpType::UserCompletion, *i, c->first, &dispatcher::invoke_user_completion_fast, c->second));
+ return ret;
+}
+#endif
+// Called in unknown thread
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher::completion_returntype dispatcher::invoke_user_completion_slow(size_t id, future<> op, std::function<dispatcher::completion_t> callback)
+{
+ return callback(id, std::move(op));
+}
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> dispatcher::completion(const std::vector<future<>> &ops, const std::vector<std::pair<async_op_flags, std::function<dispatcher::completion_t>>> &callbacks)
+{
+ if(!ops.empty() && ops.size()!=callbacks.size())
+ BOOST_AFIO_THROW(std::runtime_error("The sequence of preconditions must either be empty or exactly the same length as callbacks."));
+ std::vector<future<>> ret;
+ ret.reserve(callbacks.size());
+ std::vector<future<>>::const_iterator i;
+ std::vector<std::pair<async_op_flags, std::function<dispatcher::completion_t>>>::const_iterator c;
+ detail::immediate_async_ops immediates(callbacks.size());
+ if(ops.empty())
+ {
+ future<> empty;
+ for(auto & c: callbacks)
+ {
+ ret.push_back(chain_async_op(immediates, (int) detail::OpType::UserCompletion, empty, c.first, &dispatcher::invoke_user_completion_slow, c.second));
+ }
+ }
+ else for(i=ops.begin(), c=callbacks.begin(); i!=ops.end() && c!=callbacks.end(); ++i, ++c)
+ ret.push_back(chain_async_op(immediates, (int) detail::OpType::UserCompletion, *i, c->first, &dispatcher::invoke_user_completion_slow, c->second));
+ return ret;
+}
+
+// Called in unknown thread
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void dispatcher::complete_async_op(size_t id, handle_ptr h, exception_ptr e)
+{
+ detail::immediate_async_ops immediates(1);
+ std::shared_ptr<detail::async_file_io_dispatcher_op> thisop;
+ std::vector<detail::async_file_io_dispatcher_op::completion_t> completions;
+ {
+#ifdef BOOST_AFIO_USE_CONCURRENT_UNORDERED_MAP
+ // We can atomically remove without destruction
+ auto np=p->ops.extract(id);
+ if(!np)
+#else
+ lock_guard<decltype(p->opslock)> g(p->opslock);
+ // Find me in ops, remove my completions and delete me from extant ops
+ auto it=p->ops.find(id);
+ if(p->ops.end()==it)
+#endif
+ {
+#ifndef NDEBUG
+ std::vector<size_t> opsids;
+ for(auto &i: p->ops)
+ {
+ opsids.push_back(i.first);
+ }
+ std::sort(opsids.begin(), opsids.end());
+#endif
+ BOOST_AFIO_THROW_FATAL(std::runtime_error("Failed to find this operation in list of currently executing operations"));
+ }
+#ifdef BOOST_AFIO_USE_CONCURRENT_UNORDERED_MAP
+ thisop.swap(np->second);
+#else
+ thisop.swap(it->second); // thisop=it->second;
+ // Erase me from ops
+ p->ops.erase(it);
+#endif
+ // Ok so this op is now removed from the ops list.
+ // Because chain_async_op() holds the opslock during the finding of preconditions
+ // and adding ops to its completions, we can now safely detach our completions
+ // into stack storage and process them from there without holding any locks
+ completions=std::move(thisop->completions);
+ }
+ // Early set stl_future
+ if(e)
+ {
+#ifdef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+ auto unexception_stack=detail::Undoer([]{
+ delete afio_exception_stack();
+ afio_exception_stack()=nullptr;
+ });
+ if(afio_exception_stack() && !(p->testing_flags & detail::unit_testing_flags::no_symbol_lookup))
+ {
+ std::string originalmsg;
+ error_code ec;
+ bool is_runtime_error=false, is_system_error=false;
+ try { rethrow_exception(e); }
+ catch(const system_error &r) { ec=r.code(); originalmsg=r.what(); is_system_error=true; }
+ catch(const std::runtime_error &r) { originalmsg=r.what(); is_runtime_error=true; }
+ catch(...) { }
+ if(is_runtime_error || is_system_error)
+ {
+ // Append the stack to the runtime error message
+ std::ostringstream buffer;
+ buffer << originalmsg << ". Op was scheduled at:\n";
+ print_stack(buffer, thisop->stack);
+ buffer << "Exceptions were thrown within the engine at:\n";
+ for(auto &i : *afio_exception_stack())
+ {
+ //buffer << i.name << " Backtrace:\n";
+ print_stack(buffer, i.stack);
+ }
+ if(is_system_error)
+ e=BOOST_AFIO_V2_NAMESPACE::make_exception_ptr(system_error(ec, buffer.str()));
+ else
+ e=BOOST_AFIO_V2_NAMESPACE::make_exception_ptr(std::runtime_error(buffer.str()));
+ }
+ }
+#endif
+ thisop->enqueuement.set_future_exception(e);
+ /*if(!thisop->enqueuement.get_future().is_ready())
+ {
+ int a=1;
+ }
+ assert(thisop->enqueuement.get_future().is_ready());
+ assert(thisop->h().is_ready());
+ assert(thisop->enqueuement.get_future().get_exception_ptr()==e);
+ assert(thisop->h().get_exception_ptr()==e);*/
+ }
+ else
+ {
+ thisop->enqueuement.set_future_value(h);
+ /*if(!thisop->enqueuement.get_future().is_ready())
+ {
+ int a=1;
+ }
+ assert(thisop->enqueuement.get_future().is_ready());
+ assert(thisop->h().is_ready());
+ assert(thisop->enqueuement.get_future().get()==h);
+ assert(thisop->h().get()==h);*/
+ }
+ BOOST_AFIO_DEBUG_PRINT("X %u %p e=%d f=%p (uc=%u, c=%u)\n", (unsigned) id, h.get(), !!e, &thisop->h(), (unsigned) h.use_count(), (unsigned) thisop->completions.size());
+ // Any post op filters installed? If so, invoke those now.
+ if(!p->filters.empty())
+ {
+ future<> me(this, id, thisop->h(), false);
+ for(auto &i: p->filters)
+ {
+ if(i.first==detail::OpType::Unknown || i.first==thisop->optype)
+ {
+ try
+ {
+ i.second(thisop->optype, me);
+ }
+ catch(...)
+ {
+ // throw it away
+ }
+ }
+ }
+ }
+ if(!completions.empty())
+ {
+ for(auto &c: completions)
+ {
+ detail::async_file_io_dispatcher_op *c_op=c.second.get();
+ BOOST_AFIO_DEBUG_PRINT("X %u (f=%u) > %u\n", (unsigned) id, (unsigned) c_op->flags, (unsigned) c.first);
+ if(!!(c_op->flags & async_op_flags::immediate))
+ immediates.enqueue(c_op->enqueuement);
+ else
+ p->pool->enqueue(c_op->enqueuement);
+ }
+ }
+}
+
+
+// Called in unknown thread
+template<class F, class... Args> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC handle_ptr dispatcher::invoke_async_op_completions(size_t id, future<> op, completion_returntype(F::*f)(size_t, future<>, Args...), Args... args)
+{
+ try
+ {
+#ifndef NDEBUG
+ // Find our op
+ {
+ lock_guard<decltype(p->opslock)> g(p->opslock);
+ auto it(p->ops.find(id));
+ if(p->ops.end()==it)
+ {
+#ifndef NDEBUG
+ std::vector<size_t> opsids;
+ for(auto &i: p->ops)
+ {
+ opsids.push_back(i.first);
+ }
+ std::sort(opsids.begin(), opsids.end());
+#endif
+ BOOST_AFIO_THROW_FATAL(std::runtime_error("Failed to find this operation in list of currently executing operations"));
+ }
+ }
+#endif
+ completion_returntype ret((static_cast<F *>(this)->*f)(id, std::move(op), args...));
+ // If boolean is false, reschedule completion notification setting it to ret.second, otherwise complete now
+ if(ret.first)
+ {
+ complete_async_op(id, ret.second);
+ }
+ return ret.second;
+ }
+ catch(...)
+ {
+ exception_ptr e(current_exception());
+ assert(e);
+ BOOST_AFIO_DEBUG_PRINT("E %u begin\n", (unsigned) id);
+ complete_async_op(id, e);
+ BOOST_AFIO_DEBUG_PRINT("E %u end\n", (unsigned) id);
+ // complete_async_op() ought to have sent our exception state to our stl_future,
+ // so can silently drop the exception now
+ return handle_ptr();
+ }
+}
+
+
+// You MUST hold opslock before entry!
+template<class F, class... Args> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC future<> dispatcher::chain_async_op(detail::immediate_async_ops &immediates, int optype, const future<> &precondition, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, Args...), Args... args)
+{
+ size_t thisid=0;
+ while(!(thisid=++p->monotoniccount));
+#if 0 //ndef NDEBUG
+ if(!p->ops.empty())
+ {
+ std::vector<size_t> opsids;
+ for(auto &i: p->ops)
+ {
+ opsids.push_back(i.first);
+ }
+ std::sort(opsids.begin(), opsids.end());
+ assert(thisid==opsids.back()+1);
+ }
+#endif
+ // Wrap supplied implementation routine with a completion dispatcher
+ auto wrapperf=&dispatcher::invoke_async_op_completions<F, Args...>;
+ // Make a new future<> ready for returning
+ auto thisop=std::make_shared<detail::async_file_io_dispatcher_op>((detail::OpType) optype, flags);
+ // Bind supplied implementation routine to this, unique id, precondition and any args they passed
+ thisop->enqueuement.set_task(std::bind(wrapperf, this, thisid, precondition, f, args...));
+ // Set the output shared stl_future
+ future<> ret(this, thisid, thisop->h());
+ typename detail::async_file_io_dispatcher_op::completion_t item(std::make_pair(thisid, thisop));
+ bool done=false;
+ auto unopsit=detail::Undoer([this, thisid](){
+ std::string what;
+ try { throw; } catch(std::exception &e) { what=e.what(); } catch(...) { what="not a std exception"; }
+ BOOST_AFIO_DEBUG_PRINT("E X %u (%s)\n", (unsigned) thisid, what.c_str());
+ {
+#ifdef BOOST_AFIO_USE_CONCURRENT_UNORDERED_MAP
+ p->ops.erase(thisid);
+#else
+ lock_guard<decltype(p->opslock)> g(p->opslock);
+ auto opsit=p->ops.find(thisid);
+ if(p->ops.end()!=opsit) p->ops.erase(opsit);
+#endif
+ }
+ });
+#ifdef BOOST_AFIO_USE_CONCURRENT_UNORDERED_MAP
+ p->ops.insert(item);
+ if(precondition.id())
+ {
+ // If still in flight, chain item to be executed when precondition completes
+ typedef typename decltype(p->ops)::value_type op_value_type;
+ p->ops.visit(precondition.id, [&item, &done](const op_value_type &dep){
+ dep.second->completions.push_back(item);
+ done=true;
+ });
+ }
+#else
+ {
+ /* This is a weird bug which took me several days to track down ...
+ It turns out that libstdc++ 4.8.0 will *move* insert item into
+ p->ops because item's type is not *exactly* the value_type wanted
+ by unordered_map. That destroys item for use later, which is
+ obviously _insane_. The workaround is to feed insert() a copy. */
+ auto item2(item);
+ {
+ lock_guard<decltype(p->opslock)> g(p->opslock);
+ auto opsit=p->ops.insert(std::move(item2));
+ assert(opsit.second);
+ if(precondition.id())
+ {
+ // If still in flight, chain item to be executed when precondition completes
+ auto dep(p->ops.find(precondition.id()));
+ if(p->ops.end()!=dep)
+ {
+ dep->second->completions.push_back(item);
+ done=true;
+ }
+ }
+ }
+ }
+#endif
+ auto undep=detail::Undoer([done, this, &precondition, &item](){
+ if(done)
+ {
+#ifdef BOOST_AFIO_USE_CONCURRENT_UNORDERED_MAP
+ typedef typename decltype(p->ops)::value_type op_value_type;
+ p->ops.visit(precondition.id(), [&item](const op_value_type &dep){
+ // Items may have been added by other threads ...
+ for(auto it=--dep.second->completions.end(); true; --it)
+ {
+ if(it->first==item.first)
+ {
+ dep.second->completions.erase(it);
+ break;
+ }
+ if(dep.second->completions.begin()==it) break;
+ }
+ });
+#else
+ lock_guard<decltype(p->opslock)> g(p->opslock);
+ auto dep(p->ops.find(precondition.id()));
+ // Items may have been added by other threads ...
+ for(auto it=--dep->second->completions.end(); true; --it)
+ {
+ if(it->first==item.first)
+ {
+ dep->second->completions.erase(it);
+ break;
+ }
+ if(dep->second->completions.begin()==it) break;
+ }
+#endif
+ }
+ });
+ BOOST_AFIO_DEBUG_PRINT("I %u (d=%d) < %u (%s)\n", (unsigned) thisid, done, (unsigned) precondition.id(), detail::optypes[static_cast<int>(optype)]);
+ if(!done)
+ {
+ // Bind input handle now and queue immediately to next available thread worker
+#if 0
+ if(precondition.id() && !precondition.h.valid())
+ {
+ // It should never happen that precondition.id is valid but removed from extant ops
+ // which indicates it completed and yet h remains invalid
+ BOOST_AFIO_THROW_FATAL(std::runtime_error("Precondition was not in list of extant ops, yet its stl_future is invalid. This should never happen for any real op, so it's probably memory corruption."));
+ }
+#endif
+ if(!!(flags & async_op_flags::immediate))
+ immediates.enqueue(thisop->enqueuement);
+ else
+ p->pool->enqueue(thisop->enqueuement);
+ }
+ undep.dismiss();
+ unopsit.dismiss();
+ return ret;
+}
+
+// Generic op receiving specialisation i.e. precondition is also input op. Skips sanity checking.
+template<class F> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> dispatcher::chain_async_ops(int optype, const std::vector<future<>> &preconditions, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, future<>))
+{
+ std::vector<future<>> ret;
+ ret.reserve(preconditions.size());
+ detail::immediate_async_ops immediates(preconditions.size());
+ for (auto &i : preconditions)
+ {
+ ret.push_back(chain_async_op(immediates, optype, i, flags, f, i));
+ }
+ return ret;
+}
+// General non-specialised implementation taking some arbitrary parameter T with precondition
+template<class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> dispatcher::chain_async_ops(int optype, const std::vector<future<>> &preconditions, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T))
+{
+ std::vector<future<>> ret;
+ ret.reserve(container.size());
+ assert(preconditions.size() == container.size());
+ if (preconditions.size() != container.size())
+ BOOST_AFIO_THROW(std::runtime_error("preconditions size does not match size of ops data"));
+ detail::immediate_async_ops immediates(preconditions.size());
+ auto precondition_it = preconditions.cbegin();
+ auto container_it = container.cbegin();
+ for (; precondition_it != preconditions.cend() && container_it != container.cend(); ++precondition_it, ++container_it)
+ ret.push_back(chain_async_op(immediates, optype, *precondition_it, flags, f, *container_it));
+ return ret;
+}
+// General non-specialised implementation taking some arbitrary parameter T containing precondition returning custom type
+template<class R, class F> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<R>> dispatcher::chain_async_ops(int optype, const std::vector<future<>> &preconditions, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, std::shared_ptr<promise<R>>))
+{
+ std::vector<future<R>> ret;
+ ret.reserve(preconditions.size());
+ detail::immediate_async_ops immediates(preconditions.size());
+ for (auto &i : preconditions)
+ {
+ auto s(std::make_shared<promise<R>>());
+ ret.push_back(future<R>(chain_async_op(immediates, optype, i, flags, f, s), s->get_future()));
+ }
+ return ret;
+}
+// General non-specialised implementation taking some arbitrary parameter T with precondition returning custom type
+template<class R, class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<R>> dispatcher::chain_async_ops(int optype, const std::vector<future<>> &preconditions, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T, std::shared_ptr<promise<R>>))
+{
+ std::vector<future<R>> ret;
+ ret.reserve(container.size());
+ assert(preconditions.size() == container.size());
+ if (preconditions.size() != container.size())
+ BOOST_AFIO_THROW(std::runtime_error("preconditions size does not match size of ops data"));
+ detail::immediate_async_ops immediates(preconditions.size());
+ auto precondition_it = preconditions.cbegin();
+ auto container_it = container.cbegin();
+ for (; precondition_it != preconditions.cend() && container_it != container.cend(); ++precondition_it, ++container_it)
+ {
+ auto s(std::make_shared<promise<R>>());
+ ret.push_back(future<R>(chain_async_op(immediates, optype, *precondition_it, flags, f, *container_it, s), s->get_future()));
+ }
+ return ret;
+}
+// General non-specialised implementation taking some arbitrary parameter T containing precondition
+template<class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> dispatcher::chain_async_ops(int optype, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T))
+{
+ std::vector<future<>> ret;
+ ret.reserve(container.size());
+ detail::immediate_async_ops immediates(container.size());
+ for (auto &i : container)
+ {
+ ret.push_back(chain_async_op(immediates, optype, i.precondition, flags, f, i));
+ }
+ return ret;
+}
+// General non-specialised implementation taking some arbitrary parameter T containing precondition returning custom type
+template<class R, class F, class T> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<R>> dispatcher::chain_async_ops(int optype, const std::vector<T> &container, async_op_flags flags, completion_returntype(F::*f)(size_t, future<>, T, std::shared_ptr<promise<R>>))
+{
+ std::vector<future<R>> ret;
+ ret.reserve(container.size());
+ detail::immediate_async_ops immediates(container.size());
+ for (auto &i : container)
+ {
+ auto s(std::make_shared<promise<R>>());
+ ret.push_back(future<R>(chain_async_op(immediates, optype, i.precondition, flags, f, i, s), s->get_future()));
+ }
+ return ret;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> dispatcher::adopt(const std::vector<handle_ptr> &hs)
+{
+ std::vector<future<>> preconditions(hs.size());
+ return chain_async_ops((int) detail::OpType::adopt, preconditions, hs, async_op_flags::immediate, &dispatcher::doadopt);
+}
+
+namespace detail
+{
+ struct barrier_count_completed_state
+ {
+ atomic<size_t> togo;
+ std::vector<std::pair<size_t, handle_ptr>> out;
+ std::vector<shared_future<handle_ptr>> insharedstates;
+ barrier_count_completed_state(const std::vector<future<>> &ops) : togo(ops.size()), out(ops.size())
+ {
+ insharedstates.reserve(ops.size());
+ for(auto &i: ops)
+ {
+ insharedstates.push_back(i._h);
+ }
+ }
+ };
+}
+
+/* This is extremely naughty ... you really shouldn't be using templates to hide implementation
+types, but hey it works and is non-header so so what ...
+*/
+//template<class T> dispatcher::completion_returntype dispatcher::dobarrier(size_t id, future<> op, T state);
+template<> BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher::completion_returntype dispatcher::dobarrier<std::pair<std::shared_ptr<detail::barrier_count_completed_state>, size_t>>(size_t id, future<> op, std::pair<std::shared_ptr<detail::barrier_count_completed_state>, size_t> state)
+{
+ handle_ptr h(op.get_handle(true)); // Ignore any error state until later
+ size_t idx=state.second;
+ detail::barrier_count_completed_state &s=*state.first;
+ s.out[idx]=std::make_pair(id, h); // This might look thread unsafe, but each idx is unique
+ if(--state.first->togo)
+ return std::make_pair(false, h);
+#if 1
+ // On the basis that probably the preceding decrementing thread has yet to signal their stl_future,
+ // give up my timeslice
+ this_thread::yield();
+#endif
+#if BOOST_AFIO_USE_BOOST_THREAD
+ // Rather than potentially expend a syscall per wait on each input op to complete, compose a list of input futures and wait on them all
+ std::vector<shared_future<handle_ptr>> notready;
+ notready.reserve(s.insharedstates.size()-1);
+ for(idx=0; idx<s.insharedstates.size(); idx++)
+ {
+ shared_future<handle_ptr> &f=s.insharedstates[idx];
+ if(idx==state.second || is_ready(f)) continue;
+ notready.push_back(f);
+ }
+ if(!notready.empty())
+ boost::wait_for_all(notready.begin(), notready.end());
+#else
+ for(idx=0; idx<s.insharedstates.size(); idx++)
+ {
+ shared_future<handle_ptr> &f=s.insharedstates[idx];
+ if(idx==state.second || is_ready(f)) continue;
+ f.wait();
+ }
+#endif
+ // Last one just completed, so issue completions for everything in out including myself
+ for(idx=0; idx<s.out.size(); idx++)
+ {
+ shared_future<handle_ptr> &thisresult=s.insharedstates[idx];
+ exception_ptr e(get_exception_ptr(thisresult));
+ complete_async_op(s.out[idx].first, s.out[idx].second, e);
+ }
+ // As I just completed myself above, prevent any further processing
+ return std::make_pair(false, handle_ptr());
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC std::vector<future<>> dispatcher::barrier(const std::vector<future<>> &ops)
+{
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate(false))
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ // Create a shared state for the completions to be attached to all the items we are waiting upon
+ auto state(std::make_shared<detail::barrier_count_completed_state>(ops));
+ // Create each of the parameters to be sent to each dobarrier
+ std::vector<std::pair<std::shared_ptr<detail::barrier_count_completed_state>, size_t>> statev;
+ statev.reserve(ops.size());
+ size_t idx=0;
+ for(auto &op: ops)
+ {
+ statev.push_back(std::make_pair(state, idx++));
+ }
+ return chain_async_ops((int) detail::OpType::barrier, ops, statev, async_op_flags::immediate, &dispatcher::dobarrier<std::pair<std::shared_ptr<detail::barrier_count_completed_state>, size_t>>);
+}
+
+namespace detail {
+ class async_file_io_dispatcher_compat : public dispatcher
+ {
+ template<class Impl, class Handle> friend handle_ptr detail::decode_relative_path(path_req &req, bool force_absolute);
+ friend class dispatcher;
+ friend struct async_io_handle_posix;
+ handle_ptr decode_relative_path(path_req &req, bool force_absolute=false)
+ {
+ return detail::decode_relative_path<async_file_io_dispatcher_compat, async_io_handle_posix>(req, force_absolute);
+ }
+ // Called in unknown thread
+ completion_returntype dodir(size_t id, future<> op, path_req req)
+ {
+ req.flags=fileflags(req.flags)|file_flags::int_opening_dir|file_flags::read;
+ // TODO FIXME: Currently file_flags::create may duplicate handles in the dirhcache
+ if(!(req.flags & file_flags::unique_directory_handle) && !!(req.flags & file_flags::read) && !(req.flags & file_flags::write) && !(req.flags & (file_flags::create|file_flags::create_only_if_not_exist)))
+ {
+ path_req req2(req);
+ // Return a copy of the one in the dir cache if available as a fast path
+ decode_relative_path(req2, true);
+ try
+ {
+ return std::make_pair(true, p->get_handle_to_dir(this, id, req2, &async_file_io_dispatcher_compat::dofile));
+ }
+ catch(...)
+ {
+ // fall through
+ }
+ }
+ // This will add itself to the dir cache if it's eligible
+ return dofile(id, op, req);
+ }
+ // Called in unknown thread
+ completion_returntype dounlink(bool is_dir, size_t id, future<> op, path_req req)
+ {
+ req.flags=fileflags(req.flags);
+ // Deleting the input op?
+ if(req.path.empty())
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ p->unlink();
+ return std::make_pair(true, op.get_handle());
+ }
+ auto dirh=decode_relative_path(req);
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_UNLINKAT(dirh ? (int)(size_t)dirh->native_handle() : at_fdcwd, req.path.c_str(), is_dir ? AT_REMOVEDIR : 0), [&req]{return req.path;});
+ return std::make_pair(true, op.get_handle());
+ }
+ // Called in unknown thread
+ completion_returntype dormdir(size_t id, future<> op, path_req req)
+ {
+ return dounlink(true, id, std::move(op), std::move(req));
+ }
+ // Called in unknown thread
+ completion_returntype dofile(size_t id, future<> op, path_req req)
+ {
+ int flags=0, fd;
+ req.flags=fileflags(req.flags);
+ handle_ptr dirh=decode_relative_path(req);
+
+ // POSIX doesn't permit directories to be opened with write access
+ if(!!(req.flags & file_flags::int_opening_dir))
+ flags|=O_RDONLY;
+ else
+ {
+ if(!!(req.flags & file_flags::read) && !!(req.flags & file_flags::write)) flags|=O_RDWR;
+ else if(!!(req.flags & file_flags::read)) flags|=O_RDONLY;
+ else if(!!(req.flags & file_flags::write)) flags|=O_WRONLY;
+ }
+#ifdef O_SYNC
+ if(!!(req.flags & file_flags::always_sync)) flags|=O_SYNC;
+#endif
+ if(!!(req.flags & file_flags::int_opening_link))
+ {
+#ifdef WIN32
+ BOOST_AFIO_THROW(std::runtime_error("Creating symbolic links via MSVCRT is not supported on Windows."));
+#else
+ if(!!(req.flags & (file_flags::create|file_flags::create_only_if_not_exist)))
+ {
+ fd=BOOST_AFIO_POSIX_SYMLINKAT(op->path().c_str(), dirh ? (int)(size_t)dirh->native_handle() : at_fdcwd, req.path.c_str());
+ if(-1==fd && EEXIST==errno)
+ {
+ // Ignore already exists unless we were asked otherwise
+ if(!(req.flags & file_flags::create_only_if_not_exist))
+ fd=0;
+ }
+ BOOST_AFIO_ERRHOSFN(fd, [&req]{return req.path;});
+ }
+#endif
+#ifdef O_PATH
+ // Some platforms allow the direct opening of symbolic links as file descriptors. If so,
+ // symlinks become as unracy as files.
+ flags|=O_PATH|O_NOFOLLOW;
+ // fall through
+#else
+ // POSIX doesn't allow you to open symbolic links, so return a closed handle
+ if(dirh)
+ req.path=dirh->path()/req.path;
+ auto ret=std::make_shared<async_io_handle_posix>(this, req.path, req.flags, false, false, -999);
+ return std::make_pair(true, ret);
+#endif
+ }
+ else if(!!(req.flags & file_flags::int_opening_dir))
+ {
+#ifdef O_DIRECTORY
+ flags|=O_DIRECTORY;
+#endif
+#ifdef O_SEARCH
+ flags|=O_SEARCH;
+#endif
+ if(!!(req.flags & file_flags::create) || !!(req.flags & file_flags::create_only_if_not_exist))
+ {
+ fd=BOOST_AFIO_POSIX_MKDIRAT(dirh ? (int)(size_t)dirh->native_handle() : at_fdcwd, req.path.c_str(), 0x1f8/*770*/);
+ if(-1==fd && EEXIST==errno)
+ {
+ // Ignore already exists unless we were asked otherwise
+ if(!(req.flags & file_flags::create_only_if_not_exist))
+ fd=0;
+ }
+ BOOST_AFIO_ERRHOSFN(fd, [&req]{return req.path;});
+ }
+ }
+ else
+ {
+ if(!!(req.flags & file_flags::append)) flags|=O_APPEND;
+ if(!!(req.flags & file_flags::truncate)) flags|=O_TRUNC;
+ if(!!(req.flags & file_flags::create_only_if_not_exist)) flags|=O_EXCL|O_CREAT;
+ else if(!!(req.flags & file_flags::create)) flags|=O_CREAT;
+#ifdef O_DIRECT
+ if(!!(req.flags & file_flags::os_direct)) flags|=O_DIRECT;
+#endif
+ }
+ fd=BOOST_AFIO_POSIX_OPENAT(dirh ? (int)(size_t)dirh->native_handle() : at_fdcwd, req.path.c_str(), flags, 0x1b0/*660*/);
+ if(dirh)
+ req.path=dirh->path()/req.path;
+#ifdef O_PATH
+ // Some kernels don't really implement O_PATH
+ if(-1==fd && ELOOP==errno && !!(flags&O_PATH))
+ {
+ auto ret=std::make_shared<async_io_handle_posix>(this, req.path, req.flags, false, false, -999);
+ return std::make_pair(true, ret);
+ }
+#endif
+ // If writing and SyncOnClose and NOT synchronous, turn on SyncOnClose
+ auto ret=std::make_shared<async_io_handle_posix>(this, req.path, req.flags, (file_flags::create_only_if_not_exist|file_flags::delete_on_close)==(req.flags & (file_flags::create_only_if_not_exist|file_flags::delete_on_close)), (file_flags::sync_on_close|file_flags::write)==(req.flags & (file_flags::sync_on_close|file_flags::write|file_flags::always_sync)), fd);
+ static_cast<async_io_handle_posix *>(ret.get())->do_add_io_handle_to_parent();
+ if(!(req.flags & file_flags::int_opening_dir) && !(req.flags & file_flags::int_opening_link))
+ {
+ if(!!(req.flags & file_flags::will_be_sequentially_accessed) || !!(req.flags & file_flags::will_be_randomly_accessed))
+ {
+#ifndef WIN32
+#if !defined(__APPLE__)
+ int advice=!!(req.flags & file_flags::will_be_sequentially_accessed) ?
+ (POSIX_FADV_SEQUENTIAL|POSIX_FADV_WILLNEED) :
+ (POSIX_FADV_RANDOM);
+ BOOST_AFIO_ERRHOSFN(::posix_fadvise(fd, 0, 0, advice), [&req]{return req.path;});
+#endif
+#if defined(__FreeBSD__)
+ size_t readaheadsize=!!(req.flags & file_flags::will_be_sequentially_accessed) ? utils::file_buffer_default_size() : 0;
+ BOOST_AFIO_ERRHOSFN(::fcntl(fd, F_READAHEAD, readaheadsize), [&req]{return req.path;});
+#elif defined(__APPLE__)
+ size_t readahead=!!(req.flags & file_flags::will_be_sequentially_accessed) ? 1 : 0;
+ BOOST_AFIO_ERRHOSFN(::fcntl(fd, F_RDAHEAD, readahead), [&req]{return req.path;});
+#endif
+#endif
+ }
+ }
+ return std::make_pair(true, ret);
+ }
+ // Called in unknown thread
+ completion_returntype dormfile(size_t id, future<> op, path_req req)
+ {
+ return dounlink(false, id, std::move(op), std::move(req));
+ }
+ // Called in unknown thread
+ completion_returntype dosymlink(size_t id, future<> op, path_req req)
+ {
+ req.flags=fileflags(req.flags)|file_flags::int_opening_link;
+ return dofile(id, op, req);
+ }
+ // Called in unknown thread
+ completion_returntype dormsymlink(size_t id, future<> op, path_req req)
+ {
+ return dounlink(false, id, std::move(op), std::move(req));
+ }
+ // Called in unknown thread
+ completion_returntype dozero(size_t id, future<> op, std::vector<std::pair<off_t, off_t>> ranges)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ bool done=false;
+#if defined(__linux__)
+ done=true;
+ for(auto &i: ranges)
+ {
+ int ret;
+ while(-1==(ret=fallocate(p->fd, 0x02/*FALLOC_FL_PUNCH_HOLE*/|0x01/*FALLOC_FL_KEEP_SIZE*/, i.first, i.second)) && EINTR==errno);
+ if(-1==ret)
+ {
+ // The filing system may not support trim
+ if(EOPNOTSUPP==errno)
+ {
+ done=false;
+ break;
+ }
+ BOOST_AFIO_ERRHOSFN(-1, [p]{return p->path();});
+ }
+ }
+#endif
+ // Fall back onto a write of zeros
+ if(!done)
+ {
+ std::vector<char, utils::page_allocator<char>> buffer(utils::file_buffer_default_size());
+ for(auto &i: ranges)
+ {
+ ssize_t byteswritten=0;
+ std::vector<iovec> vecs(1+(size_t)(i.second/buffer.size()));
+ for(size_t n=0; n<vecs.size(); n++)
+ {
+ vecs[n].iov_base=buffer.data();
+ vecs[n].iov_len=(n<vecs.size()-1) ? buffer.size() : (size_t)(i.second-(off_t) n*buffer.size());
+ }
+ for(size_t n=0; n<vecs.size(); n+=IOV_MAX)
+ {
+ ssize_t _byteswritten;
+ size_t amount=std::min((int) (vecs.size()-n), IOV_MAX);
+ off_t offset=i.first+byteswritten;
+ while(-1==(_byteswritten=pwritev(p->fd, (&vecs.front())+n, (int) amount, offset)) && EINTR==errno)
+ /*empty*/;
+ BOOST_AFIO_ERRHOSFN((int) _byteswritten, [p]{return p->path();});
+ byteswritten+=_byteswritten;
+ }
+ }
+ }
+ return std::make_pair(true, h);
+ }
+ // Called in unknown thread
+ completion_returntype dosync(size_t id, future<> op, future<>)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ off_t bytestobesynced=p->write_count_since_fsync();
+ if(bytestobesynced)
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_FSYNC(p->fd), [p]{return p->path();});
+ p->has_ever_been_fsynced=true;
+ p->byteswrittenatlastfsync+=bytestobesynced;
+ return std::make_pair(true, h);
+ }
+ // Called in unknown thread
+ completion_returntype doclose(size_t id, future<> op, future<>)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ if(!!(p->flags() & file_flags::int_opening_dir) && p->available_to_directory_cache())
+ {
+ // As this is a directory which may be a fast directory enumerator, ignore close
+ }
+ else
+ {
+ p->close();
+ }
+ return std::make_pair(true, h);
+ }
+ // Called in unknown thread
+ completion_returntype doread(size_t id, future<> op, detail::io_req_impl<false> req)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ ssize_t bytesread=0, bytestoread=0;
+ iovec v;
+ BOOST_AFIO_DEBUG_PRINT("R %u %p (%c) @ %u, b=%u\n", (unsigned) id, h.get(), p->path().native().back(), (unsigned) req.where, (unsigned) req.buffers.size());
+#ifdef DEBUG_PRINTING
+ for(auto &b: req.buffers)
+ {
+ BOOST_AFIO_DEBUG_PRINT(" R %u: %p %u\n", (unsigned) id, asio::buffer_cast<const void *>(b), (unsigned) asio::buffer_size(b));
+ }
+#endif
+ std::vector<iovec> vecs;
+ vecs.reserve(req.buffers.size());
+ for(auto &b: req.buffers)
+ {
+ v.iov_base=asio::buffer_cast<void *>(b);
+ v.iov_len=asio::buffer_size(b);
+ bytestoread+=v.iov_len;
+ vecs.push_back(v);
+ }
+ for(size_t n=0; n<vecs.size(); n+=IOV_MAX)
+ {
+ ssize_t _bytesread;
+ size_t amount=std::min((int) (vecs.size()-n), IOV_MAX);
+ off_t offset=req.where+bytesread;
+ while(-1==(_bytesread=preadv(p->fd, (&vecs.front())+n, (int) amount, offset)) && EINTR==errno);
+ if(!this->p->filters_buffers.empty())
+ {
+ error_code ec(errno, generic_category());
+ for(auto &i: this->p->filters_buffers)
+ {
+ if(i.first==OpType::Unknown || i.first==OpType::read)
+ {
+ i.second(OpType::read, p, req, offset, n, amount, ec, (size_t)_bytesread);
+ }
+ }
+ }
+ BOOST_AFIO_ERRHOSFN((int) _bytesread, [p]{return p->path();});
+ p->bytesread+=_bytesread;
+ bytesread+=_bytesread;
+ }
+ if(bytesread!=bytestoread)
+ BOOST_AFIO_THROW_FATAL(std::runtime_error("Failed to read all buffers"));
+ return std::make_pair(true, h);
+ }
+ // Called in unknown thread
+ completion_returntype dowrite(size_t id, future<> op, detail::io_req_impl<true> req)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ ssize_t byteswritten=0, bytestowrite=0;
+ iovec v;
+ std::vector<iovec> vecs;
+ vecs.reserve(req.buffers.size());
+ BOOST_AFIO_DEBUG_PRINT("W %u %p (%c) @ %u, b=%u\n", (unsigned) id, h.get(), p->path().native().back(), (unsigned) req.where, (unsigned) req.buffers.size());
+#ifdef DEBUG_PRINTING
+ for(auto &b: req.buffers)
+ {
+ BOOST_AFIO_DEBUG_PRINT(" W %u: %p %u\n", (unsigned) id, asio::buffer_cast<const void *>(b), (unsigned) asio::buffer_size(b));
+ }
+#endif
+ for(auto &b: req.buffers)
+ {
+ v.iov_base=(void *) asio::buffer_cast<const void *>(b);
+ v.iov_len=asio::buffer_size(b);
+ bytestowrite+=v.iov_len;
+ vecs.push_back(v);
+ }
+ for(size_t n=0; n<vecs.size(); n+=IOV_MAX)
+ {
+ ssize_t _byteswritten;
+ size_t amount=std::min((int) (vecs.size()-n), IOV_MAX);
+ off_t offset=req.where+byteswritten;
+ // POSIX doesn't actually guarantee pwritev appends to O_APPEND files, and indeed OS X does not.
+ if(!!(p->flags() & file_flags::append))
+ {
+ while(-1==(_byteswritten=writev(p->fd, (&vecs.front())+n, (int) amount)) && EINTR==errno);
+ }
+ else
+ {
+ while(-1==(_byteswritten=pwritev(p->fd, (&vecs.front())+n, (int) amount, offset)) && EINTR==errno);
+ }
+ if(!this->p->filters_buffers.empty())
+ {
+ error_code ec(errno, generic_category());
+ for(auto &i: this->p->filters_buffers)
+ {
+ if(i.first==OpType::Unknown || i.first==OpType::write)
+ {
+ i.second(OpType::write, p, req, offset, n, amount, ec, (size_t) _byteswritten);
+ }
+ }
+ }
+ BOOST_AFIO_ERRHOSFN((int) _byteswritten, [p]{return p->path();});
+ p->byteswritten+=_byteswritten;
+ byteswritten+=_byteswritten;
+ }
+ if(byteswritten!=bytestowrite)
+ BOOST_AFIO_THROW_FATAL(std::runtime_error("Failed to write all buffers"));
+ return std::make_pair(true, h);
+ }
+ // Called in unknown thread
+ completion_returntype dotruncate(size_t id, future<> op, off_t newsize)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ BOOST_AFIO_DEBUG_PRINT("T %u %p (%c)\n", (unsigned) id, h.get(), p->path().native().back());
+ int ret;
+ while(-1==(ret=BOOST_AFIO_POSIX_FTRUNCATE(p->fd, newsize)) && EINTR==errno)
+ /*empty*/;
+ BOOST_AFIO_ERRHOSFN(ret, [p]{return p->path();});
+ return std::make_pair(true, h);
+ }
+#ifdef __linux__
+ static int int_getdents(int fd, char *buf, unsigned count) { return syscall(SYS_getdents64, fd, buf, count); }
+#endif
+#ifdef __APPLE__
+ static int int_getdents_emulation(int fd, char *buf, unsigned count) { off_t foo; return syscall(SYS_getdirentries64, fd, buf, count, &foo); }
+#endif
+ // Called in unknown thread
+ completion_returntype doenumerate(size_t id, future<> op, enumerate_req req, std::shared_ptr<promise<std::pair<std::vector<directory_entry>, bool>>> ret)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ try
+ {
+ auto globstr=req.glob.native();
+ // Is glob a single entry match? If so, skip enumerating.
+ if(!globstr.empty() && std::string::npos==globstr.find('*') && std::string::npos==globstr.find('?') && std::string::npos==globstr.find('['))
+ {
+ std::vector<directory_entry> _ret;
+ _ret.reserve(1);
+ BOOST_AFIO_POSIX_STAT_STRUCT s={0};
+#if BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS
+ if(-1!=BOOST_AFIO_POSIX_LSTATAT((int)(size_t)p->native_handle(), req.glob.c_str(), &s))
+#else
+ path path(p->path(true));
+ path/=req.glob;
+ if(-1!=BOOST_AFIO_POSIX_LSTATAT(at_fdcwd, path.c_str(), &s))
+#endif
+ {
+ stat_t stat(nullptr);
+ fill_stat_t(stat, s, req.metadata);
+ _ret.push_back(directory_entry(req.glob.native(), stat, req.metadata));
+ }
+ ret->set_value(std::make_pair(std::move(_ret), false));
+ return std::make_pair(true, h);
+ }
+#ifdef WIN32
+ BOOST_AFIO_THROW(std::runtime_error("Enumerating directories via MSVCRT is not supported."));
+#else
+#ifdef __linux__
+ // Unlike FreeBSD, Linux doesn't define a getdents() function, so we'll do that here.
+ typedef int (*getdents64_t)(int, char *, unsigned);
+ getdents64_t getdents=(getdents64_t)(&async_file_io_dispatcher_compat::int_getdents);
+ typedef dirent64 dirent;
+#endif
+#ifdef __APPLE__
+ // OS X defines a getdirentries64() kernel syscall which can emulate getdents
+ typedef int (*getdents_emulation_t)(int, char *, unsigned);
+ getdents_emulation_t getdents=(getdents_emulation_t)(&async_file_io_dispatcher_compat::int_getdents_emulation);
+#endif
+ auto buffer=std::unique_ptr<dirent[]>(new dirent[req.maxitems]);
+ if(req.restart)
+ {
+#ifdef __linux__
+ BOOST_AFIO_ERRHOS(lseek64(p->fd, 0, SEEK_SET));
+#else
+ BOOST_AFIO_ERRHOS(lseek(p->fd, 0, SEEK_SET));
+#endif
+ }
+ int bytes;
+ bool done;
+ do
+ {
+ bytes=getdents(p->fd, (char *) buffer.get(), sizeof(dirent)*req.maxitems);
+ if(-1==bytes && EINVAL==errno)
+ {
+ req.maxitems++;
+ buffer=std::unique_ptr<dirent[]>(new dirent[req.maxitems]);
+ done=false;
+ }
+ else done=true;
+ } while(!done);
+ BOOST_AFIO_ERRHOS(bytes);
+ if(!bytes)
+ {
+ ret->set_value(std::make_pair(std::vector<directory_entry>(), false));
+ return std::make_pair(true, h);
+ }
+ VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(buffer.get(), bytes);
+ bool thisbatchdone=(sizeof(dirent)*req.maxitems-bytes)>sizeof(dirent);
+ std::vector<directory_entry> _ret;
+ _ret.reserve(req.maxitems);
+ directory_entry item;
+ // This is what POSIX returns with getdents()
+ item.have_metadata=item.have_metadata|metadata_flags::ino|metadata_flags::type;
+ bool needmoremetadata=!!(req.metadata&~item.have_metadata);
+ done=false;
+ for(dirent *dent=buffer.get(); !done; dent=(dirent *)((size_t) dent + dent->d_reclen))
+ {
+ if((bytes-=dent->d_reclen)<=0) done=true;
+ if(!dent->d_ino)
+ continue;
+ size_t length=strchr(dent->d_name, 0)-dent->d_name;
+ if(length<=2 && '.'==dent->d_name[0])
+ if(1==length || '.'==dent->d_name[1]) continue;
+ if(!req.glob.empty() && fnmatch(globstr.c_str(), dent->d_name, 0)) continue;
+ path::string_type leafname(dent->d_name, length);
+ item.leafname=std::move(leafname);
+ item.stat.st_ino=dent->d_ino;
+ char d_type=dent->d_type;
+ if(DT_UNKNOWN==d_type)
+ item.have_metadata=item.have_metadata&~metadata_flags::type;
+ else
+ {
+ item.have_metadata=item.have_metadata|metadata_flags::type;
+ switch(d_type)
+ {
+ case DT_BLK:
+ item.stat.st_type=filesystem::file_type::block_file;
+ break;
+ case DT_CHR:
+ item.stat.st_type=filesystem::file_type::character_file;
+ break;
+ case DT_DIR:
+ item.stat.st_type=filesystem::file_type::directory_file;
+ break;
+ case DT_FIFO:
+ item.stat.st_type=filesystem::file_type::fifo_file;
+ break;
+ case DT_LNK:
+ item.stat.st_type=filesystem::file_type::symlink_file;
+ break;
+ case DT_REG:
+ item.stat.st_type=filesystem::file_type::regular_file;
+ break;
+ case DT_SOCK:
+ item.stat.st_type=filesystem::file_type::socket_file;
+ break;
+ default:
+ item.have_metadata=item.have_metadata&~metadata_flags::type;
+ item.stat.st_type=filesystem::file_type::type_unknown;
+ break;
+ }
+ }
+ _ret.push_back(std::move(item));
+ }
+ // NOTE: Potentially the OS didn't return type, and we're not checking
+ // for that here.
+ if(needmoremetadata)
+ {
+ for(auto &i: _ret)
+ {
+ i.fetch_metadata(h, req.metadata);
+ }
+ }
+ ret->set_value(std::make_pair(std::move(_ret), !thisbatchdone));
+#endif
+ return std::make_pair(true, h);
+ }
+ catch(...)
+ {
+ ret->set_exception(current_exception());
+ throw;
+ }
+ }
+ // Called in unknown thread
+ completion_returntype doextents(size_t id, future<> op, std::shared_ptr<promise<std::vector<std::pair<off_t, off_t>>>> ret)
+ {
+ try
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ std::vector<std::pair<off_t, off_t>> out;
+ off_t start=0, end=0;
+#ifndef WIN32
+ for(;;)
+ {
+#ifdef __linux__
+ start=lseek64(p->fd, end, SEEK_DATA);
+ if((off_t)-1==start) break;
+ end=lseek64(p->fd, start, SEEK_HOLE);
+ if((off_t)-1==end) break;
+#elif defined(__APPLE__)
+ // Can't find any support for extent enumeration in OS X
+ errno=EINVAL;
+ break;
+#elif defined(__FreeBSD__)
+ start=lseek(p->fd, end, SEEK_DATA);
+ if((off_t)-1==start) break;
+ end=lseek(p->fd, start, SEEK_HOLE);
+ if((off_t)-1==end) break;
+#else
+#error Unknown system
+#endif
+ // Data region may have been concurrently deleted
+ if(end>start)
+ out.push_back(std::make_pair<off_t, off_t>(std::move(start), end-start));
+ }
+ if(ENXIO!=errno)
+ {
+ if(EINVAL==errno)
+ {
+ // If it failed with no output, probably this filing system doesn't support extents
+ if(out.empty())
+ {
+ struct stat s;
+ BOOST_AFIO_ERRHOS(fstat(p->fd, &s));
+ out.push_back(std::make_pair<off_t, off_t>(0, s.st_size));
+ }
+ }
+ else
+ BOOST_AFIO_ERRHOS(-1);
+ }
+#endif
+ // A problem with SEEK_DATA and SEEK_HOLE is that they are racy under concurrent extents changing
+ // Coalesce sequences of contiguous data e.g. 0, 64; 64, 64; 128, 64 ...
+ std::vector<std::pair<off_t, off_t>> outfixed; outfixed.reserve(out.size());
+ outfixed.push_back(out.front());
+ for(size_t n=1; n<out.size(); n++)
+ {
+ if(outfixed.back().first+outfixed.back().second==out[n].first)
+ outfixed.back().second+=out[n].second;
+ else
+ outfixed.push_back(out[n]);
+ }
+ ret->set_value(std::move(outfixed));
+ return std::make_pair(true, h);
+ }
+ catch(...)
+ {
+ ret->set_exception(current_exception());
+ throw;
+ }
+ }
+ // Called in unknown thread
+ completion_returntype dostatfs(size_t id, future<> op, fs_metadata_flags req, std::shared_ptr<promise<statfs_t>> ret)
+ {
+ try
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ statfs_t out;
+#ifndef WIN32
+#ifdef __linux__
+ struct statfs64 s={0};
+ BOOST_AFIO_ERRHOS(fstatfs64(p->fd, &s));
+ if(!!(req&fs_metadata_flags::bsize)) out.f_bsize =s.f_bsize;
+ if(!!(req&fs_metadata_flags::iosize)) out.f_iosize =s.f_frsize;
+ if(!!(req&fs_metadata_flags::blocks)) out.f_blocks =s.f_blocks;
+ if(!!(req&fs_metadata_flags::bfree)) out.f_bfree =s.f_bfree;
+ if(!!(req&fs_metadata_flags::bavail)) out.f_bavail =s.f_bavail;
+ if(!!(req&fs_metadata_flags::files)) out.f_files =s.f_files;
+ if(!!(req&fs_metadata_flags::ffree)) out.f_ffree =s.f_ffree;
+ if(!!(req&fs_metadata_flags::namemax)) out.f_namemax =s.f_namelen;
+// if(!!(req&fs_metadata_flags::owner)) out.f_owner =s.f_owner;
+ if(!!(req&fs_metadata_flags::fsid)) { out.f_fsid[0]=(unsigned) s.f_fsid.__val[0]; out.f_fsid[1]=(unsigned) s.f_fsid.__val[1]; }
+ if(!!(req&fs_metadata_flags::flags) || !!(req&fs_metadata_flags::fstypename) || !!(req&fs_metadata_flags::mntfromname) || !!(req&fs_metadata_flags::mntonname))
+ {
+ struct mountentry
+ {
+ std::string mnt_fsname, mnt_dir, mnt_type, mnt_opts;
+ mountentry(const char *a, const char *b, const char *c, const char *d) : mnt_fsname(a), mnt_dir(b), mnt_type(c), mnt_opts(d) { }
+ };
+ std::vector<std::pair<mountentry, struct statfs64>> mountentries;
+ {
+ // Need to parse mount options on Linux
+ FILE *mtab=setmntent("/etc/mtab", "r");
+ if(!mtab) BOOST_AFIO_ERRHOS(-1);
+ auto unmtab=detail::Undoer([mtab]{endmntent(mtab);});
+ struct mntent m;
+ char buffer[32768];
+ while(getmntent_r(mtab, &m, buffer, sizeof(buffer)))
+ {
+ struct statfs64 temp={0};
+ if(0==statfs64(m.mnt_dir, &temp) && temp.f_type==s.f_type && !memcmp(&temp.f_fsid, &s.f_fsid, sizeof(s.f_fsid)))
+ mountentries.push_back(std::make_pair(mountentry(m.mnt_fsname, m.mnt_dir, m.mnt_type, m.mnt_opts), temp));
+ }
+ }
+#ifndef BOOST_AFIO_COMPILING_FOR_GCOV
+ if(mountentries.empty())
+ BOOST_AFIO_THROW("The filing system of this handle does not appear in /etc/mtab!");
+ // Choose the mount entry with the most closely matching statfs. You can't choose
+ // exclusively based on mount point because of bind mounts
+ if(mountentries.size()>1)
+ {
+ std::vector<std::pair<size_t, size_t>> scores(mountentries.size());
+ for(size_t n=0; n<mountentries.size(); n++)
+ {
+ const char *a=(const char *) &mountentries[n].second;
+ const char *b=(const char *) &s;
+ scores[n].first=0;
+ for(size_t x=0; x<sizeof(struct statfs64); x++)
+ scores[n].first+=abs(a[x]-b[x]);
+ scores[n].second=n;
+ }
+ std::sort(scores.begin(), scores.end());
+ auto temp(std::move(mountentries[scores.front().second]));
+ mountentries.clear();
+ mountentries.push_back(std::move(temp));
+ }
+#endif
+ if(!!(req&fs_metadata_flags::flags))
+ {
+ out.f_flags.rdonly =!!(s.f_flags & MS_RDONLY);
+ out.f_flags.noexec =!!(s.f_flags & MS_NOEXEC);
+ out.f_flags.nosuid =!!(s.f_flags & MS_NOSUID);
+ out.f_flags.acls =(std::string::npos!=mountentries.front().first.mnt_opts.find("acl") && std::string::npos==mountentries.front().first.mnt_opts.find("noacl"));
+ out.f_flags.xattr =(std::string::npos!=mountentries.front().first.mnt_opts.find("xattr") && std::string::npos==mountentries.front().first.mnt_opts.find("nouser_xattr"));
+// out.f_flags.compression=0;
+ // Those filing systems supporting FALLOC_FL_PUNCH_HOLE
+ out.f_flags.extents =(mountentries.front().first.mnt_type=="btrfs"
+ || mountentries.front().first.mnt_type=="ext4"
+ || mountentries.front().first.mnt_type=="xfs"
+ || mountentries.front().first.mnt_type=="tmpfs");
+ }
+ if(!!(req&fs_metadata_flags::fstypename)) out.f_fstypename=mountentries.front().first.mnt_type;
+ if(!!(req&fs_metadata_flags::mntfromname)) out.f_mntfromname=mountentries.front().first.mnt_fsname;
+ if(!!(req&fs_metadata_flags::mntonname)) out.f_mntonname=mountentries.front().first.mnt_dir;
+ }
+#else
+ struct statfs s;
+ BOOST_AFIO_ERRHOS(fstatfs(p->fd, &s));
+ if(!!(req&fs_metadata_flags::flags))
+ {
+ out.f_flags.rdonly =!!(s.f_flags & MNT_RDONLY);
+ out.f_flags.noexec =!!(s.f_flags & MNT_NOEXEC);
+ out.f_flags.nosuid =!!(s.f_flags & MNT_NOSUID);
+ out.f_flags.acls =0;
+#if defined(MNT_ACLS) && defined(MNT_NFS4ACLS)
+ out.f_flags.acls =!!(s.f_flags & (MNT_ACLS|MNT_NFS4ACLS));
+#endif
+ out.f_flags.xattr =1; // UFS and ZFS support xattr. TODO FIXME actually calculate this, zfs get xattr <f_mntfromname> would do it.
+ out.f_flags.compression=!strcmp(s.f_fstypename, "zfs");
+ out.f_flags.extents =!strcmp(s.f_fstypename, "ufs") || !strcmp(s.f_fstypename, "zfs");
+ }
+ if(!!(req&fs_metadata_flags::bsize)) out.f_bsize =s.f_bsize;
+ if(!!(req&fs_metadata_flags::iosize)) out.f_iosize =s.f_iosize;
+ if(!!(req&fs_metadata_flags::blocks)) out.f_blocks =s.f_blocks;
+ if(!!(req&fs_metadata_flags::bfree)) out.f_bfree =s.f_bfree;
+ if(!!(req&fs_metadata_flags::bavail)) out.f_bavail =s.f_bavail;
+ if(!!(req&fs_metadata_flags::files)) out.f_files =s.f_files;
+ if(!!(req&fs_metadata_flags::ffree)) out.f_ffree =s.f_ffree;
+#ifdef __APPLE__
+ if(!!(req&fs_metadata_flags::namemax)) out.f_namemax =255;
+#else
+ if(!!(req&fs_metadata_flags::namemax)) out.f_namemax =s.f_namemax;
+#endif
+ if(!!(req&fs_metadata_flags::owner)) out.f_owner =s.f_owner;
+ if(!!(req&fs_metadata_flags::fsid)) { out.f_fsid[0]=(unsigned) s.f_fsid.val[0]; out.f_fsid[1]=(unsigned) s.f_fsid.val[1]; }
+ if(!!(req&fs_metadata_flags::fstypename)) out.f_fstypename =s.f_fstypename;
+ if(!!(req&fs_metadata_flags::mntfromname)) out.f_mntfromname=s.f_mntfromname;
+ if(!!(req&fs_metadata_flags::mntonname)) out.f_mntonname =s.f_mntonname;
+#endif
+#endif
+ ret->set_value(std::move(out));
+ return std::make_pair(true, h);
+ }
+ catch(...)
+ {
+ ret->set_exception(current_exception());
+ throw;
+ }
+ }
+ // Called in unknown thread
+ completion_returntype dolock(size_t id, future<> op, lock_req req)
+ {
+#ifndef BOOST_AFIO_COMPILING_FOR_GCOV
+ handle_ptr h(op.get_handle());
+ async_io_handle_posix *p=static_cast<async_io_handle_posix *>(h.get());
+ if(!p->lockfile)
+ BOOST_AFIO_THROW(std::invalid_argument("This file handle was not opened with OSLockable."));
+ return p->lockfile->lock(id, std::move(op), std::move(req));
+#endif
+ }
+
+ public:
+ async_file_io_dispatcher_compat(std::shared_ptr<thread_source> threadpool, file_flags flagsforce, file_flags flagsmask) : dispatcher(threadpool, flagsforce, flagsmask)
+ {
+ }
+
+
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> dir(const std::vector<path_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::dir, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::dodir);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmdir(const std::vector<path_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::rmdir, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::dormdir);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> file(const std::vector<path_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::file, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::dofile);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmfile(const std::vector<path_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::rmfile, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::dormfile);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> symlink(const std::vector<path_req> &reqs, const std::vector<future<>> &targets) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ std::vector<future<>> ops(targets);
+ ops.resize(reqs.size());
+ for(size_t n=0; n<reqs.size(); n++)
+ {
+ if(!ops[n].valid())
+ ops[n]=reqs[n].precondition;
+ }
+ return chain_async_ops((int) detail::OpType::symlink, ops, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::dosymlink);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmsymlink(const std::vector<path_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::rmsymlink, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::dormsymlink);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> sync(const std::vector<future<>> &ops) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::sync, ops, async_op_flags::none, &async_file_io_dispatcher_compat::dosync);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> zero(const std::vector<future<>> &ops, const std::vector<std::vector<std::pair<off_t, off_t>>> &ranges) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::zero, ops, ranges, async_op_flags::none, &async_file_io_dispatcher_compat::dozero);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> close(const std::vector<future<>> &ops) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::close, ops, async_op_flags::none, &async_file_io_dispatcher_compat::doclose);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> read(const std::vector<detail::io_req_impl<false>> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::read, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::doread);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> write(const std::vector<detail::io_req_impl<true>> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::write, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::dowrite);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> truncate(const std::vector<future<>> &ops, const std::vector<off_t> &sizes) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::truncate, ops, sizes, async_op_flags::none, &async_file_io_dispatcher_compat::dotruncate);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<std::pair<std::vector<directory_entry>, bool>>> enumerate(const std::vector<enumerate_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::enumerate, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::doenumerate);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<std::vector<std::pair<off_t, off_t>>>> extents(const std::vector<future<>> &ops) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::extents, ops, async_op_flags::none, &async_file_io_dispatcher_compat::doextents);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<statfs_t>> statfs(const std::vector<future<>> &ops, const std::vector<fs_metadata_flags> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+ if(ops.size()!=reqs.size())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+#endif
+ return chain_async_ops((int) detail::OpType::statfs, ops, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::dostatfs);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> lock(const std::vector<lock_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::lock, reqs, async_op_flags::none, &async_file_io_dispatcher_compat::dolock);
+ }
+ };
+
+ inline handle_ptr async_io_handle_posix::int_verifymyinode()
+ {
+ BOOST_AFIO_POSIX_STAT_STRUCT s={0};
+ handle_ptr dirh(container());
+ for(size_t n=0; n<10; n++)
+ {
+ path_req req(path(true));
+ if(!dirh)
+ {
+ dirh=container();
+ if(!dirh)
+ {
+ try
+ {
+ dirh=parent()->int_get_handle_to_containing_dir(static_cast<async_file_io_dispatcher_compat *>(parent()), 0, req, &async_file_io_dispatcher_compat::dofile);
+ }
+ catch(...)
+ {
+ // Path to containing directory may have gone stale
+ this_thread::sleep_for(chrono::milliseconds(1));
+ continue;
+ }
+ }
+ }
+#if BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS
+ if(-1!=BOOST_AFIO_POSIX_LSTATAT((int)(size_t)dirh->native_handle(), req.path.filename().c_str(), &s))
+#else
+ if(-1!=BOOST_AFIO_POSIX_LSTATAT(at_fdcwd, req.path.c_str(), &s))
+#endif
+ {
+ if(s.st_dev==st_dev && s.st_ino==st_ino)
+ return dirh;
+ }
+ // Didn't find an entry with my name, so sleep and retry
+#ifndef NDEBUG
+ std::cout << "WARNING: Did not find inode " << st_ino << " in directory " << dirh->path() << ", sleeping." << std::endl;
+#endif
+ dirh.reset();
+ this_thread::sleep_for(chrono::milliseconds(1));
+ }
+ return dirh;
+ }
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#if defined(WIN32) && !defined(USE_POSIX_ON_WIN32)
+#include "afio_iocp.ipp"
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void directory_entry::_int_fetch(metadata_flags wanted, handle_ptr dirh)
+{
+#ifdef WIN32
+ detail::async_file_io_dispatcher_windows *dispatcher=dynamic_cast<detail::async_file_io_dispatcher_windows *>(dirh->parent());
+ if(dispatcher)
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ bool slowPath=(!!(wanted&metadata_flags::nlink) || !!(wanted&metadata_flags::blocks) || !!(wanted&metadata_flags::blksize));
+ if(!slowPath)
+ {
+ // Fast path skips opening a handle per file by enumerating the containing directory using a glob
+ // exactly matching the leafname. This is about 10x quicker, so it's very much worth it.
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[sizeof(FILE_ID_FULL_DIR_INFORMATION)/sizeof(path::value_type)+32769];
+ IO_STATUS_BLOCK isb={ 0 };
+ UNICODE_STRING _glob;
+ NTSTATUS ntstat;
+ _glob.Buffer=const_cast<path::value_type *>(leafname.c_str());
+ _glob.MaximumLength=(_glob.Length=(USHORT) (leafname.size()*sizeof(path::value_type)))+sizeof(path::value_type);
+ FILE_ID_FULL_DIR_INFORMATION *ffdi=(FILE_ID_FULL_DIR_INFORMATION *) buffer;
+ ntstat=NtQueryDirectoryFile(dirh->native_handle(), NULL, NULL, NULL, &isb, ffdi, sizeof(buffer),
+ FileIdFullDirectoryInformation, TRUE, &_glob, FALSE);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(dirh->native_handle(), FALSE, NULL);
+ BOOST_AFIO_ERRHNTFN(ntstat, [dirh]{return dirh->path();});
+ if(!!(wanted&metadata_flags::ino)) { stat.st_ino=ffdi->FileId.QuadPart; }
+ if(!!(wanted&metadata_flags::type)) { stat.st_type=windows_nt_kernel::to_st_type(ffdi->FileAttributes, ffdi->ReparsePointTag); }
+ if(!!(wanted&metadata_flags::atim)) { stat.st_atim=to_timepoint(ffdi->LastAccessTime); }
+ if(!!(wanted&metadata_flags::mtim)) { stat.st_mtim=to_timepoint(ffdi->LastWriteTime); }
+ if(!!(wanted&metadata_flags::ctim)) { stat.st_ctim=to_timepoint(ffdi->ChangeTime); }
+ if(!!(wanted&metadata_flags::size)) { stat.st_size=ffdi->EndOfFile.QuadPart; }
+ if(!!(wanted&metadata_flags::allocated)) { stat.st_allocated=ffdi->AllocationSize.QuadPart; }
+ if(!!(wanted&metadata_flags::birthtim)) { stat.st_birthtim=to_timepoint(ffdi->CreationTime); }
+ if(!!(wanted&metadata_flags::sparse)) { stat.st_sparse=!!(ffdi->FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE); }
+ if(!!(wanted&metadata_flags::compressed)) { stat.st_compressed=!!(ffdi->FileAttributes & FILE_ATTRIBUTE_COMPRESSED); }
+ if(!!(wanted&metadata_flags::reparse_point)) { stat.st_reparse_point=!!(ffdi->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); }
+ }
+ else
+ {
+ // No choice here, open a handle and stat it. Make sure you open with no flags, else
+ // files with delete pending will refuse to open
+ path_req::relative req(dirh, name(), file_flags::none);
+ auto fileh=dispatcher->dofile(0, future<>(), req).second;
+ auto direntry=fileh->direntry(wanted);
+ wanted=wanted & direntry.metadata_ready(); // direntry() can fail to fill some entries on Win XP
+ //if(!!(wanted&metadata_flags::dev)) { stat.st_dev=direntry.stat.st_dev; }
+ if(!!(wanted&metadata_flags::ino)) { stat.st_ino=direntry.stat.st_ino; }
+ if(!!(wanted&metadata_flags::type)) { stat.st_type=direntry.stat.st_type; }
+ //if(!!(wanted&metadata_flags::perms)) { stat.st_mode=direntry.stat.st_perms; }
+ if(!!(wanted&metadata_flags::nlink)) { stat.st_nlink=direntry.stat.st_nlink; }
+ //if(!!(wanted&metadata_flags::uid)) { stat.st_uid=direntry.stat.st_uid; }
+ //if(!!(wanted&metadata_flags::gid)) { stat.st_gid=direntry.stat.st_gid; }
+ //if(!!(wanted&metadata_flags::rdev)) { stat.st_rdev=direntry.stat.st_rdev; }
+ if(!!(wanted&metadata_flags::atim)) { stat.st_atim=direntry.stat.st_atim; }
+ if(!!(wanted&metadata_flags::mtim)) { stat.st_mtim=direntry.stat.st_mtim; }
+ if(!!(wanted&metadata_flags::ctim)) { stat.st_ctim=direntry.stat.st_ctim; }
+ if(!!(wanted&metadata_flags::size)) { stat.st_size=direntry.stat.st_size; }
+ if(!!(wanted&metadata_flags::allocated)) { stat.st_allocated=direntry.stat.st_allocated; }
+ if(!!(wanted&metadata_flags::blocks)) { stat.st_blocks=direntry.stat.st_blocks; }
+ if(!!(wanted&metadata_flags::blksize)) { stat.st_blksize=direntry.stat.st_blksize; }
+#ifdef HAVE_STAT_FLAGS
+ if(!!(wanted&metadata_flags::flags)) { stat.st_flags=direntry.stat.st_flags; }
+#endif
+#ifdef HAVE_STAT_GEN
+ if(!!(wanted&metadata_flags::gen)) { stat.st_gen=direntry.stat.st_gen; }
+#endif
+#ifdef HAVE_BIRTHTIMESPEC
+ if(!!(wanted&metadata_flags::birthtim)) { stat.st_birthtim=direntry.stat.st_birthtim; }
+#endif
+ if(!!(wanted&metadata_flags::sparse)) { stat.st_sparse=direntry.stat.st_sparse; }
+ if(!!(wanted&metadata_flags::compressed)) { stat.st_compressed=direntry.stat.st_compressed; }
+ if(!!(wanted&metadata_flags::reparse_point)) { stat.st_reparse_point=direntry.stat.st_reparse_point; }
+ }
+ }
+ else
+#endif
+ {
+ BOOST_AFIO_POSIX_STAT_STRUCT s={0};
+#if BOOST_AFIO_POSIX_PROVIDES_AT_PATH_FUNCTIONS
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_LSTATAT((int)(size_t)dirh->native_handle(), leafname.c_str(), &s), [&]{return dirh->path()/leafname;});
+#else
+ BOOST_AFIO_V2_NAMESPACE::path path(dirh->path(true));
+ path/=leafname;
+ BOOST_AFIO_ERRHOSFN(BOOST_AFIO_POSIX_LSTATAT(at_fdcwd, path.c_str(), &s), [&path]{return path;});
+#endif
+ fill_stat_t(stat, s, wanted);
+ }
+ have_metadata=have_metadata | wanted;
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC handle::mapped_file::~mapped_file()
+{
+#ifdef WIN32
+ UnmapViewOfFile(addr);
+#else
+ ::munmap(addr, length);
+#endif
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC outcome<dispatcher_ptr> make_dispatcher(std::string uri, file_flags flagsforce, file_flags flagsmask, std::shared_ptr<thread_source> threadpool) noexcept
+{
+ try
+ {
+ if(uri!="file:///")
+ return error_code(ENXIO, generic_category());
+#if defined(WIN32) && !defined(USE_POSIX_ON_WIN32)
+ return dispatcher_ptr(std::make_shared<detail::async_file_io_dispatcher_windows>(threadpool, flagsforce, flagsmask));
+#else
+ return dispatcher_ptr(std::make_shared<detail::async_file_io_dispatcher_compat>(threadpool, flagsforce, flagsmask));
+#endif
+ }
+ catch(...)
+ {
+ return current_exception();
+ }
+}
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC dispatcher_ptr current_dispatcher(option<dispatcher_ptr> new_dispatcher)
+{
+#if defined(__FreeBSD__) || defined(__APPLE__) || defined(BOOST_AFIO_USE_CXA_THREAD_ATEXIT_WORKAROUND)
+ // FreeBSD and OS X haven't implemented __cxa_thread_atexit in their libc yet, so fire and forget a workaround
+ static __thread dispatcher_ptr *current;
+ if(!current)
+ {
+ current=new dispatcher_ptr();
+ fprintf(stderr, "WARNING: Until libc++ fixes the lack of __cxa_thread_atexit, Boost.AFIO leaks %u bytes for this thread!\n", (unsigned) sizeof(dispatcher_ptr));
+ }
+ dispatcher_ptr ret(*current);
+ if(new_dispatcher)
+ *current=new_dispatcher.get();
+ return ret;
+#else
+ static thread_local dispatcher_ptr current;
+ dispatcher_ptr ret(current);
+ if(new_dispatcher)
+ current=new_dispatcher.get();
+ return ret;
+#endif
+}
+
+namespace utils
+{
+ // Stupid MSVC ...
+ namespace detail { using namespace BOOST_AFIO_V2_NAMESPACE::detail; }
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 6387) // MSVC sanitiser warns that GetModuleHandleA() might fail (hah!)
+#endif
+ std::vector<size_t> page_sizes(bool only_actually_available) noexcept
+ {
+ static spinlock<bool> lock;
+ static std::vector<size_t> pagesizes, pagesizes_available;
+ lock_guard<decltype(lock)> g(lock);
+ if(pagesizes.empty())
+ {
+#ifdef WIN32
+ typedef size_t (WINAPI *GetLargePageMinimum_t)(void);
+ SYSTEM_INFO si={ 0 };
+ GetSystemInfo(&si);
+ pagesizes.push_back(si.dwPageSize);
+ pagesizes_available.push_back(si.dwPageSize);
+ GetLargePageMinimum_t GetLargePageMinimum_ = (GetLargePageMinimum_t) GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "GetLargePageMinimum");
+ if(GetLargePageMinimum_)
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ pagesizes.push_back(GetLargePageMinimum_());
+ /* Attempt to enable SeLockMemoryPrivilege */
+ HANDLE token;
+ if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &token))
+ {
+ auto untoken=detail::Undoer([&token]{CloseHandle(token);});
+ TOKEN_PRIVILEGES privs={1};
+ if(LookupPrivilegeValue(NULL, SE_LOCK_MEMORY_NAME, &privs.Privileges[0].Luid))
+ {
+ privs.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
+ if(AdjustTokenPrivileges(token, FALSE, &privs, 0, NULL, NULL) && GetLastError()==S_OK)
+ pagesizes_available.push_back(GetLargePageMinimum_());
+ }
+ }
+ }
+#elif defined(__FreeBSD__)
+ pagesizes.resize(32);
+ int out;
+ if(-1==(out=getpagesizes(pagesizes.data(), 32)))
+ {
+ pagesizes.clear();
+ pagesizes.push_back(getpagesize());
+ pagesizes_available.push_back(getpagesize());
+ }
+ else
+ {
+ pagesizes.resize(out);
+ pagesizes_available=pagesizes;
+ }
+#elif defined(__APPLE__)
+ // I can't find how to determine what the super page size is on OS X programmatically
+ // It appears to be hard coded into mach/vm_statistics.h which says that it's always 2Mb
+ // Therefore, we hard code 2Mb
+ pagesizes.push_back(getpagesize());
+ pagesizes_available.push_back(getpagesize());
+ pagesizes.push_back(2*1024*1024);
+ pagesizes_available.push_back(2*1024*1024);
+#elif defined(__linux__)
+ pagesizes.push_back(getpagesize());
+ pagesizes_available.push_back(getpagesize());
+ int ih=::open("/proc/meminfo", O_RDONLY);
+ if(-1!=ih)
+ {
+ char buffer[4096], *hugepagesize, *hugepages;
+ buffer[::read(ih, buffer, sizeof(buffer)-1)]=0;
+ ::close(ih);
+ hugepagesize=strstr(buffer, "Hugepagesize:");
+ hugepages=strstr(buffer, "HugePages_Total:");
+ if(hugepagesize && hugepages)
+ {
+ unsigned _hugepages=0, _hugepagesize=0;
+ while(*++hugepagesize!=' ');
+ while(*++hugepages!=' ');
+ while(*++hugepagesize==' ');
+ while(*++hugepages==' ');
+ sscanf(hugepagesize, "%u", &_hugepagesize);
+ sscanf(hugepages, "%u", &_hugepages);
+ if(_hugepagesize)
+ {
+ pagesizes.push_back(((size_t)_hugepagesize)*1024);
+ if(_hugepages)
+ pagesizes_available.push_back(((size_t)_hugepagesize)*1024);
+ }
+ }
+ }
+#else
+ pagesizes.push_back(getpagesize());
+ pagesizes_available.push_back(getpagesize());
+#endif
+ }
+ return only_actually_available ? pagesizes_available : pagesizes;
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ void random_fill(char *buffer, size_t bytes)
+ {
+#ifdef WIN32
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ BOOST_AFIO_ERRHWIN(RtlGenRandom(buffer, (ULONG) bytes));
+#else
+ static spinlock<bool> lock;
+ static int randomfd=-1;
+ if(-1==randomfd)
+ {
+ lock_guard<decltype(lock)> g(lock);
+ BOOST_AFIO_ERRHOS((randomfd=::open("/dev/urandom", O_RDONLY)));
+ }
+ BOOST_AFIO_ERRHOS(::read(randomfd, buffer, bytes));
+#endif
+ }
+
+
+ namespace detail
+ {
+ large_page_allocation allocate_large_pages(size_t bytes)
+ {
+ large_page_allocation ret(calculate_large_page_allocation(bytes));
+#ifdef WIN32
+ DWORD type=MEM_COMMIT|MEM_RESERVE;
+ if(ret.page_size_used>65536)
+ type|=MEM_LARGE_PAGES;
+ if(!(ret.p=VirtualAlloc(nullptr, ret.actual_size, type, PAGE_READWRITE)))
+ {
+ if(ERROR_NOT_ENOUGH_MEMORY==GetLastError())
+ if((ret.p=VirtualAlloc(nullptr, ret.actual_size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE)))
+ return ret;
+ BOOST_AFIO_ERRHWIN(0);
+ }
+#ifndef NDEBUG
+ else if(ret.page_size_used>65536)
+ std::cout << "afio: Large page allocation successful" << std::endl;
+#endif
+#else
+ int flags=MAP_SHARED|MAP_ANON;
+ if(ret.page_size_used>65536)
+ {
+#ifdef MAP_HUGETLB
+ flags|=MAP_HUGETLB;
+#endif
+#ifdef MAP_ALIGNED_SUPER
+ flags|=MAP_ALIGNED_SUPER;
+#endif
+#ifdef VM_FLAGS_SUPERPAGE_SIZE_ANY
+ flags|=VM_FLAGS_SUPERPAGE_SIZE_ANY;
+#endif
+ }
+ if(!(ret.p=mmap(nullptr, ret.actual_size, PROT_WRITE, flags, -1, 0)))
+ {
+ if(ENOMEM==errno)
+ if((ret.p=mmap(nullptr, ret.actual_size, PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0)))
+ return ret;
+ BOOST_AFIO_ERRHOS(-1);
+ }
+#ifndef NDEBUG
+ else if(ret.page_size_used>65536)
+ std::cout << "afio: Large page allocation successful" << std::endl;
+#endif
+#endif
+ return ret;
+ }
+ void deallocate_large_pages(void *p, size_t bytes)
+ {
+#ifdef WIN32
+ BOOST_AFIO_ERRHWIN(VirtualFree(p, 0, MEM_RELEASE));
+#else
+ BOOST_AFIO_ERRHOS(munmap(p, bytes));
+#endif
+ }
+ }
+
+}
+
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
diff --git a/attic/include/boost/afio/v2/detail/impl/afio_iocp.ipp b/attic/include/boost/afio/v2/detail/impl/afio_iocp.ipp
new file mode 100644
index 00000000..012d4aaa
--- /dev/null
+++ b/attic/include/boost/afio/v2/detail/impl/afio_iocp.ipp
@@ -0,0 +1,1957 @@
+/* async_file_io
+Provides a threadpool and asynchronous file i/o infrastructure based on Boost.ASIO, Boost.Iostreams and filesystem
+(C) 2013-2014 Niall Douglas http://www.nedprod.com/
+File Created: Mar 2013
+*/
+
+#ifdef WIN32
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 6262) // Excessive stack usage
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+namespace detail {
+ // Helper for opening files. Lightweight means open with no access, it can be faster.
+ static inline std::pair<NTSTATUS, HANDLE> ntcreatefile(handle_ptr dirh, BOOST_AFIO_V2_NAMESPACE::path path, file_flags flags, bool overlapped=true) noexcept
+ {
+ DWORD access=FILE_READ_ATTRIBUTES|SYNCHRONIZE, attribs=FILE_ATTRIBUTE_NORMAL;
+ DWORD fileshare=FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE;
+ DWORD creatdisp=0, ntflags=0x4000/*FILE_OPEN_FOR_BACKUP_INTENT*/;
+ if(!overlapped)
+ ntflags|=0x20/*FILE_SYNCHRONOUS_IO_NONALERT*/;
+ if(flags==file_flags::none)
+
+ ntflags|=0x00200000/*FILE_OPEN_REPARSE_POINT*/;
+
+ else
+ {
+ if(!!(flags & file_flags::int_opening_link))
+ ntflags|=0x00200000/*FILE_OPEN_REPARSE_POINT*/;
+ if(!!(flags & file_flags::int_opening_dir))
+ {
+ /* NOTE TO SELF: Never, ever, ever again request FILE_DELETE_CHILD perms. It subtly breaks renaming, enumeration
+ and a ton of other stuff in a really weird way. */
+ ntflags|=0x01/*FILE_DIRECTORY_FILE*/;
+ access|=FILE_LIST_DIRECTORY|FILE_TRAVERSE; // FILE_READ_DATA|FILE_EXECUTE
+ // Match POSIX where read perms are sufficient to use this handle as a relative base for creating new entries
+ //access|=FILE_ADD_FILE|FILE_ADD_SUBDIRECTORY; // FILE_WRITE_DATA|FILE_APPEND_DATA // Fails where user is not administrator
+ if(!!(flags & file_flags::read)) access|=GENERIC_READ;
+ // Write access probably means he wants to delete or rename self
+ if(!!(flags & file_flags::write)) access|=GENERIC_WRITE|DELETE;
+ // Windows doesn't like opening directories without buffering.
+ if(!!(flags & file_flags::os_direct)) flags=flags & ~file_flags::os_direct;
+ }
+ else
+ {
+ ntflags|=0x040/*FILE_NON_DIRECTORY_FILE*/;
+ if(!!(flags & file_flags::append)) access|=FILE_APPEND_DATA|DELETE;
+ else
+ {
+ if(!!(flags & file_flags::read)) access|=GENERIC_READ;
+ if(!!(flags & file_flags::write)) access|=GENERIC_WRITE|DELETE;
+ }
+ if(!!(flags & file_flags::will_be_sequentially_accessed))
+ ntflags|=0x00000004/*FILE_SEQUENTIAL_ONLY*/;
+ else if(!!(flags & file_flags::will_be_randomly_accessed))
+ ntflags|=0x00000800/*FILE_RANDOM_ACCESS*/;
+ if(!!(flags & file_flags::temporary_file))
+ attribs|=FILE_ATTRIBUTE_TEMPORARY;
+ }
+ if(!!(flags & file_flags::delete_on_close) && (!!(flags & file_flags::create_only_if_not_exist) || !!(flags & file_flags::int_file_share_delete)))
+ {
+ ntflags|=0x00001000/*FILE_DELETE_ON_CLOSE*/;
+ access|=DELETE;
+ }
+ if(!!(flags & file_flags::int_file_share_delete))
+ access|=DELETE;
+ }
+ if(!!(flags & file_flags::create_only_if_not_exist))
+ {
+ creatdisp|=0x00000002/*FILE_CREATE*/;
+ }
+ else if(!!(flags & file_flags::create))
+ {
+ creatdisp|=0x00000003/*FILE_OPEN_IF*/;
+ }
+ else if(!!(flags & file_flags::truncate)) creatdisp|=0x00000005/*FILE_OVERWRITE_IF*/;
+ else creatdisp|=0x00000001/*FILE_OPEN*/;
+ if(!!(flags & file_flags::os_direct)) ntflags|=0x00000008/*FILE_NO_INTERMEDIATE_BUFFERING*/;
+ if(!!(flags & file_flags::always_sync)) ntflags|=0x00000002/*FILE_WRITE_THROUGH*/;
+
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ HANDLE h=nullptr;
+ IO_STATUS_BLOCK isb={ 0 };
+ OBJECT_ATTRIBUTES oa={sizeof(OBJECT_ATTRIBUTES)};
+ oa.RootDirectory=dirh ? dirh->native_handle() : nullptr;
+ UNICODE_STRING _path;
+ // If relative path, or symlinked DOS path, use case insensitive
+ bool isSymlinkedDosPath=(path.native().size()>3 && path.native()[0]=='\\' && path.native()[1]=='?' && path.native()[2]=='?' && path.native()[3]=='\\');
+ oa.Attributes=(path.native()[0]!='\\' || isSymlinkedDosPath) ? 0x40/*OBJ_CASE_INSENSITIVE*/ : 0;
+ //if(!!(flags & file_flags::int_opening_link))
+ // oa.Attributes|=0x100/*OBJ_OPENLINK*/;
+ _path.Buffer=const_cast<path::value_type *>(path.c_str());
+ _path.MaximumLength=(_path.Length=(USHORT) (path.native().size()*sizeof(path::value_type)))+sizeof(path::value_type);
+ oa.ObjectName=&_path;
+ LARGE_INTEGER AllocationSize={0};
+ return std::make_pair(NtCreateFile(&h, access, &oa, &isb, &AllocationSize, attribs, fileshare,
+ creatdisp, ntflags, NULL, 0), h);
+ }
+
+ struct win_actual_lock_file;
+ struct win_lock_file : public lock_file<win_actual_lock_file>
+ {
+ asio::windows::object_handle ev;
+ win_lock_file(handle *_h=nullptr) : lock_file<win_actual_lock_file>(_h), ev(_h->parent()->threadsource()->io_service())
+ {
+ HANDLE evh;
+ BOOST_AFIO_ERRHWIN(INVALID_HANDLE_VALUE!=(evh=CreateEvent(nullptr, true, false, nullptr)));
+ ev.assign(evh);
+ }
+ };
+
+ // Two modes of calling, either a handle or a leafname + dir handle
+ static inline bool isDeletedFile(path::string_type leafname)
+ {
+ if(leafname.size()==64+6 && !leafname.compare(0, 6, L".afiod"))
+ {
+ // Could be one of our "deleted" files, is he ".afiod" + all hex?
+ for(size_t n=6; n<leafname.size(); n++)
+ {
+ auto c=leafname[n];
+ if(!((c>='0' && c<='9') || (c>='a' && c<='f')))
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+ static inline bool isDeletedFile(handle_ptr h)
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ IO_STATUS_BLOCK isb={ 0 };
+ BOOST_AFIO_TYPEALIGNMENT(8) FILE_ALL_INFORMATION fai;
+ NTSTATUS ntstat=NtQueryInformationFile(h->native_handle(), &isb, &fai.StandardInformation, sizeof(fai.StandardInformation), FileStandardInformation);
+ // If the query succeeded and delete is pending, filter out this entry
+ if(!ntstat && fai.StandardInformation.DeletePending)
+ return true;
+ else
+ return false;
+ }
+
+ static inline path MountPointFromHandle(HANDLE h)
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769];
+ IO_STATUS_BLOCK isb={ 0 };
+ NTSTATUS ntstat;
+ UNICODE_STRING *objectpath=(UNICODE_STRING *) buffer;
+ ULONG length;
+ ntstat=NtQueryObject(h, ObjectNameInformation, objectpath, sizeof(buffer), &length);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h, FALSE, NULL);
+ BOOST_AFIO_ERRHNT(ntstat);
+ // Now get the subpath of our handle within its mount
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer2[sizeof(FILE_NAME_INFORMATION)/sizeof(path::value_type)+32769];
+ FILE_NAME_INFORMATION *fni=(FILE_NAME_INFORMATION *) buffer2;
+ ntstat=NtQueryInformationFile(h, &isb, fni, sizeof(buffer2), FileNameInformation);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h, FALSE, NULL);
+ BOOST_AFIO_ERRHNT(ntstat);
+ // The mount path will be the remainder of the objectpath after removing the
+ // common ending
+ size_t remainder=(objectpath->Length-fni->FileNameLength)/sizeof(path::value_type);
+ return path::string_type(objectpath->Buffer, remainder);
+ }
+
+ static inline path::string_type VolumeNameFromMountPoint(path mountpoint)
+ {
+ HANDLE vh;
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769];
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type volumename[64];
+ // Enumerate every volume in the system and the device it maps until we find ours
+ BOOST_AFIO_ERRHWIN(INVALID_HANDLE_VALUE!=(vh=FindFirstVolume(volumename, sizeof(volumename)/sizeof(volumename[0]))));
+ auto unvh=Undoer([&vh]{FindVolumeClose(vh);});
+ size_t volumenamelen;
+ do
+ {
+ DWORD len;
+ volumenamelen=wcslen(volumename);
+ volumename[volumenamelen-1]=0; // remove the trailing backslash
+ BOOST_AFIO_ERRHWIN((len=QueryDosDevice(volumename+4, buffer, sizeof(buffer)/sizeof(buffer[0]))));
+ if(!mountpoint.native().compare(0, len, buffer))
+ return path::string_type(volumename, volumenamelen-1);
+ } while(FindNextVolume(vh, volumename, sizeof(volumename)/sizeof(volumename[0])));
+ return path::string_type();
+ }
+}
+
+BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC filesystem::path normalise_path(path p, path_normalise type)
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ bool isSymlinkedDosPath=(p.native().size()>3 && p.native()[0]=='\\' && p.native()[1]=='?' && p.native()[2]=='?' && p.native()[3]=='\\');
+ bool needToOpen=!isSymlinkedDosPath || path_normalise::guid_all==type;
+ // Path is probably \Device\Harddisk...
+ NTSTATUS ntstat;
+ HANDLE h=nullptr;
+ if(needToOpen)
+ {
+ // Open with no rights and no access
+ std::tie(ntstat, h)=detail::ntcreatefile(handle_ptr(), p, file_flags::none, false);
+ BOOST_AFIO_ERRHNTFN(ntstat, [&p]{return p;});
+ }
+ auto unh=detail::Undoer([&h]{if(h) CloseHandle(h);});
+ path mountpoint;
+ path::string_type volumename;
+ if(isSymlinkedDosPath)
+ {
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[64];
+ mountpoint=path(p.native().substr(0, p.native().find('\\', 4)+1), path::direct());
+ BOOST_AFIO_ERRHWIN(GetVolumeNameForVolumeMountPoint(mountpoint.c_str()+4, buffer, 64));
+ volumename=buffer;
+ // Trim the mount point's ending slash
+ filesystem::path::string_type &me=const_cast<filesystem::path::string_type &>(mountpoint.native());
+ me.resize(me.size()-1);
+ }
+ else
+ {
+ mountpoint=detail::MountPointFromHandle(h);
+ volumename=detail::VolumeNameFromMountPoint(mountpoint);
+ }
+ if(path_normalise::guid_volume==type)
+ return filesystem::path(volumename)/p.native().substr(mountpoint.native().size());
+ else if(path_normalise::guid_all==type)
+ {
+ FILE_OBJECTID_BUFFER fob;
+ DWORD out;
+ BOOST_AFIO_ERRHWIN(DeviceIoControl(h, FSCTL_CREATE_OR_GET_OBJECT_ID, nullptr, 0, &fob, sizeof(fob), &out, nullptr));
+ GUID *guid=(GUID *) &fob.ObjectId;
+ path::value_type buffer[64];
+ swprintf_s(buffer, 64, L"{%08x-%04hx-%04hx-%02x%02x-%02x%02x%02x%02x%02x%02x}", guid->Data1, guid->Data2, guid->Data3, guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3], guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
+ return filesystem::path(volumename)/filesystem::path::string_type(buffer, 38);
+ }
+ else
+ {
+ bool needsExtendedPrefix=false;
+ // Are there any illegal Win32 characters in here?
+ static BOOST_CONSTEXPR_OR_CONST char reserved_chars[]="\"*/:<>?|";
+ for(size_t n=isSymlinkedDosPath ? p.native().find('\\', 5) : 0; !needsExtendedPrefix && n<p.native().size(); n++)
+ {
+ if(p.native()[n]>=1 && p.native()[n]<=31)
+ needsExtendedPrefix=true;
+ for(size_t x=0; x<sizeof(reserved_chars); x++)
+ if(p.native()[n]==reserved_chars[x])
+ {
+ needsExtendedPrefix=true;
+ break;
+ }
+ }
+ if(!needsExtendedPrefix)
+ {
+ // Are any segments of the filename a reserved name?
+ static constexpr const wchar_t *reserved_names[]={L"CON", L"PRN", L"AUX", L"NUL", L"COM1", L"COM2", L"COM3", L"COM4", L"COM5", L"COM6", L"COM7", L"COM8", L"COM9", L"LPT1", L"LPT2", L"LPT3", L"LPT4", L"LPT5", L"LPT6", L"LPT7", L"LPT8", L"LPT9"};
+ for(auto &i : p)
+ {
+ auto s=i.stem().native();
+ if(s[0]==' ' || s[s.size()-1]=='.')
+ {
+ needsExtendedPrefix=true;
+ break;
+ }
+ for(size_t n=0; n<sizeof(reserved_names)/sizeof(reserved_names[0]); n++)
+ {
+ if(s==reserved_names[n])
+ {
+ needsExtendedPrefix=true;
+ break;
+ }
+ }
+ if(needsExtendedPrefix)
+ break;
+ }
+ }
+
+ DWORD len;
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769];
+ if(volumename.back()!='\\') volumename.push_back('\\');
+ BOOST_AFIO_ERRHWIN(GetVolumePathNamesForVolumeName(volumename.c_str(), buffer, sizeof(buffer)/sizeof(buffer[0]), &len));
+ // Read the returned DOS mount points into a vector and sort them by size
+ std::vector<path::string_type> dosmountpoints;
+ for(path::value_type *x=buffer; *x; x+=wcslen(x)+1)
+ dosmountpoints.push_back(path::string_type(x));
+ if(dosmountpoints.empty())
+ BOOST_AFIO_THROW(std::runtime_error("No Win32 mount points returned for volume path"));
+ std::sort(dosmountpoints.begin(), dosmountpoints.end(), [](const path::string_type &a, const path::string_type &b){return a.size()<b.size();});
+ filesystem::path ret(dosmountpoints.front());
+ ret/=p.native().substr(mountpoint.native().size()+1);
+ if(ret.native().size()>=260)
+ needsExtendedPrefix=true;
+ if(needsExtendedPrefix)
+ return filesystem::path("\\\\?")/ret;
+ else
+ return ret;
+ }
+}
+
+namespace detail
+{
+ struct async_io_handle_windows : public handle
+ {
+ std::unique_ptr<asio::windows::random_access_handle> h;
+ void *myid;
+ bool has_been_added, SyncOnClose;
+ HANDLE sectionh;
+ typedef spinlock<bool> pathlock_t;
+ mutable pathlock_t pathlock; BOOST_AFIO_V2_NAMESPACE::path _path;
+ std::unique_ptr<win_lock_file> lockfile;
+
+ static HANDLE int_checkHandle(HANDLE h, const BOOST_AFIO_V2_NAMESPACE::path &path)
+ {
+ BOOST_AFIO_ERRHWINFN(INVALID_HANDLE_VALUE!=h, [&path]{return path;});
+ return h;
+ }
+ async_io_handle_windows(dispatcher *_parent, const BOOST_AFIO_V2_NAMESPACE::path &path, file_flags flags) : handle(_parent, flags),
+ myid(nullptr), has_been_added(false), SyncOnClose(false), sectionh(nullptr), _path(path) { }
+ inline async_io_handle_windows(dispatcher *_parent, const BOOST_AFIO_V2_NAMESPACE::path &path, file_flags flags, bool _SyncOnClose, HANDLE _h);
+ inline handle_ptr int_verifymyinode();
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void close() override final
+ {
+ BOOST_AFIO_DEBUG_PRINT("D %p\n", this);
+ if(sectionh)
+ {
+ BOOST_AFIO_ERRHWINFN(CloseHandle(sectionh), [this]{return path();});
+ sectionh=nullptr;
+ }
+ if(h)
+ {
+ // Windows doesn't provide an async fsync so do it synchronously
+ if(SyncOnClose && write_count_since_fsync())
+ BOOST_AFIO_ERRHWINFN(FlushFileBuffers(myid), [this]{return path();});
+ h->close();
+ h.reset();
+ }
+ // Deregister AFTER close of file handle
+ if(has_been_added)
+ {
+ parent()->int_del_io_handle(myid);
+ has_been_added=false;
+ }
+ myid=nullptr;
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC open_states is_open() const override final
+ {
+ if(!myid)
+ return open_states::closed;
+ return available_to_directory_cache() ? open_states::opendir : open_states::open;
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void *native_handle() const override final { return myid; }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path(bool refresh=false) override final
+ {
+ if(refresh)
+ {
+ if(!myid)
+ {
+#if 0
+ auto mycontainer=container();
+ if(mycontainer)
+ mycontainer->path(true);
+#endif
+ }
+ else
+ {
+ BOOST_AFIO_V2_NAMESPACE::path newpath;
+ if(!isDeletedFile(shared_from_this()))
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ HANDLE h=myid;
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769];
+ UNICODE_STRING *volumepath=(UNICODE_STRING *) buffer;
+ ULONG bufferlength;
+ NTSTATUS ntstat=NtQueryObject(h, ObjectNameInformation, volumepath, sizeof(buffer), &bufferlength);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h, FALSE, NULL);
+ BOOST_AFIO_ERRHNT(ntstat);
+ newpath=path::string_type(volumepath->Buffer, volumepath->Length/sizeof(path::value_type));
+ }
+ bool changed=false;
+ BOOST_AFIO_V2_NAMESPACE::path oldpath;
+ {
+ lock_guard<pathlock_t> g(pathlock);
+ if((changed=(newpath!=_path)))
+ {
+ oldpath=std::move(_path);
+ _path=newpath;
+ }
+ }
+ if(changed)
+ {
+ // Currently always zap the container if path changes
+ if(this->dirh)
+ this->dirh.reset();
+ // Need to update the directory cache
+ if(available_to_directory_cache())
+ parent()->int_directory_cached_handle_path_changed(oldpath, newpath, shared_from_this());
+#if 0
+ // Perhaps also need to update my container
+ auto containerh(container());
+ if(containerh)
+ containerh->path(true);
+#endif
+ }
+ }
+ }
+ lock_guard<pathlock_t> g(pathlock);
+ return _path;
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path path() const override final
+ {
+ lock_guard<pathlock_t> g(pathlock);
+ return _path;
+ }
+
+ // You can't use shared_from_this() in a constructor so ...
+ void do_add_io_handle_to_parent()
+ {
+ if(myid)
+ {
+ // Canonicalise my path
+ auto newpath=path(true);
+ parent()->int_add_io_handle(myid, shared_from_this());
+ has_been_added=true;
+ // If I'm the right sort of directory, register myself with the dircache. path(true) may have
+ // already done this, but it's not much harm to repeat myself.
+ if(available_to_directory_cache())
+ parent()->int_directory_cached_handle_path_changed(BOOST_AFIO_V2_NAMESPACE::path(), newpath, shared_from_this());
+ }
+ if(!!(flags() & file_flags::hold_parent_open) && !(flags() & file_flags::int_hold_parent_open_nested))
+ dirh=int_verifymyinode();
+ }
+ ~async_io_handle_windows()
+ {
+ async_io_handle_windows::close();
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC directory_entry direntry(metadata_flags wanted) override final
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ stat_t stat(nullptr);
+ IO_STATUS_BLOCK isb={ 0 };
+ NTSTATUS ntstat;
+
+ HANDLE h=myid;
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[sizeof(FILE_ALL_INFORMATION)/sizeof(path::value_type)+32769];
+ buffer[0]=0;
+ FILE_ALL_INFORMATION &fai=*(FILE_ALL_INFORMATION *)buffer;
+ FILE_FS_SECTOR_SIZE_INFORMATION ffssi={0};
+ bool needInternal=!!(wanted&metadata_flags::ino);
+ bool needBasic=(!!(wanted&metadata_flags::type) || !!(wanted&metadata_flags::atim) || !!(wanted&metadata_flags::mtim) || !!(wanted&metadata_flags::ctim) || !!(wanted&metadata_flags::birthtim) || !!(wanted&metadata_flags::sparse) || !!(wanted&metadata_flags::compressed) || !!(wanted&metadata_flags::reparse_point));
+ bool needStandard=(!!(wanted&metadata_flags::nlink) || !!(wanted&metadata_flags::size) || !!(wanted&metadata_flags::allocated) || !!(wanted&metadata_flags::blocks));
+ // It's not widely known that the NT kernel supplies a stat() equivalent i.e. get me everything in a single syscall
+ // However fetching FileAlignmentInformation which comes with FILE_ALL_INFORMATION is slow as it touches the device driver,
+ // so only use if we need more than one item
+ if(((int) needInternal+(int) needBasic+(int) needStandard)>=2)
+ {
+ ntstat=NtQueryInformationFile(h, &isb, &fai, sizeof(buffer), FileAllInformation);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h, FALSE, NULL);
+ BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();});
+ }
+ else
+ {
+ if(needInternal)
+ {
+ ntstat=NtQueryInformationFile(h, &isb, &fai.InternalInformation, sizeof(fai.InternalInformation), FileInternalInformation);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h, FALSE, NULL);
+ BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();});
+ }
+ if(needBasic)
+ {
+ ntstat=NtQueryInformationFile(h, &isb, &fai.BasicInformation, sizeof(fai.BasicInformation), FileBasicInformation);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h, FALSE, NULL);
+ BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();});
+ }
+ if(needStandard)
+ {
+ ntstat=NtQueryInformationFile(h, &isb, &fai.StandardInformation, sizeof(fai.StandardInformation), FileStandardInformation);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h, FALSE, NULL);
+ BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();});
+ }
+ }
+ if(!!(wanted&metadata_flags::blocks) || !!(wanted&metadata_flags::blksize))
+ {
+ ntstat=NtQueryVolumeInformationFile(h, &isb, &ffssi, sizeof(ffssi), FileFsSectorSizeInformation);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h, FALSE, NULL);
+ //BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();});
+ if(0/*STATUS_SUCCESS*/!=ntstat)
+ {
+ // Windows XP and Vista don't support the FILE_FS_SECTOR_SIZE_INFORMATION
+ // API, so we'll just hardcode 512 bytes
+ ffssi.PhysicalBytesPerSectorForPerformance=512;
+ }
+ }
+ if(!!(wanted&metadata_flags::ino)) { stat.st_ino=fai.InternalInformation.IndexNumber.QuadPart; }
+ if(!!(wanted&metadata_flags::type))
+ {
+ ULONG ReparsePointTag=fai.EaInformation.ReparsePointTag;
+ // We need to get its reparse tag to see if it's a symlink
+ if(fai.BasicInformation.FileAttributes&FILE_ATTRIBUTE_REPARSE_POINT && !ReparsePointTag)
+ {
+ BOOST_AFIO_TYPEALIGNMENT(8) char buffer[sizeof(REPARSE_DATA_BUFFER)+32769];
+ DWORD written=0;
+ REPARSE_DATA_BUFFER *rpd=(REPARSE_DATA_BUFFER *) buffer;
+ memset(rpd, 0, sizeof(*rpd));
+ BOOST_AFIO_ERRHWINFN(DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, rpd, sizeof(buffer), &written, NULL), [this]{return path();});
+ ReparsePointTag=rpd->ReparseTag;
+ }
+ stat.st_type=windows_nt_kernel::to_st_type(fai.BasicInformation.FileAttributes, ReparsePointTag);
+ }
+ if(!!(wanted&metadata_flags::nlink)) { stat.st_nlink=(int16_t) fai.StandardInformation.NumberOfLinks; }
+ if(!!(wanted&metadata_flags::atim)) { stat.st_atim=to_timepoint(fai.BasicInformation.LastAccessTime); }
+ if(!!(wanted&metadata_flags::mtim)) { stat.st_mtim=to_timepoint(fai.BasicInformation.LastWriteTime); }
+ if(!!(wanted&metadata_flags::ctim)) { stat.st_ctim=to_timepoint(fai.BasicInformation.ChangeTime); }
+ if(!!(wanted&metadata_flags::size)) { stat.st_size=fai.StandardInformation.EndOfFile.QuadPart; }
+ if(!!(wanted&metadata_flags::allocated)) { stat.st_allocated=fai.StandardInformation.AllocationSize.QuadPart; }
+ if(!!(wanted&metadata_flags::blocks)) { stat.st_blocks=fai.StandardInformation.AllocationSize.QuadPart/ffssi.PhysicalBytesPerSectorForPerformance; }
+ if(!!(wanted&metadata_flags::blksize)) { stat.st_blksize=(uint16_t) ffssi.PhysicalBytesPerSectorForPerformance; }
+ if(!!(wanted&metadata_flags::birthtim)) { stat.st_birthtim=to_timepoint(fai.BasicInformation.CreationTime); }
+ if(!!(wanted&metadata_flags::sparse)) { stat.st_sparse=!!(fai.BasicInformation.FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE); }
+ if(!!(wanted&metadata_flags::compressed)) { stat.st_compressed=!!(fai.BasicInformation.FileAttributes & FILE_ATTRIBUTE_COMPRESSED); }
+ if(!!(wanted&metadata_flags::reparse_point)) { stat.st_reparse_point=!!(fai.BasicInformation.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT); }
+ return directory_entry(const_cast<async_io_handle_windows *>(this)->path(true).filename().native(), stat, wanted);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC BOOST_AFIO_V2_NAMESPACE::path target() override final
+ {
+ if(!opened_as_symlink())
+ return path();
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ using windows_nt_kernel::REPARSE_DATA_BUFFER;
+ BOOST_AFIO_TYPEALIGNMENT(8) char buffer[sizeof(REPARSE_DATA_BUFFER)+32769];
+ DWORD written=0;
+ REPARSE_DATA_BUFFER *rpd=(REPARSE_DATA_BUFFER *) buffer;
+ memset(rpd, 0, sizeof(*rpd));
+ BOOST_AFIO_ERRHWIN(DeviceIoControl(myid, FSCTL_GET_REPARSE_POINT, NULL, 0, rpd, sizeof(buffer), &written, NULL));
+ switch(rpd->ReparseTag)
+ {
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ return BOOST_AFIO_V2_NAMESPACE::path(path::string_type(rpd->MountPointReparseBuffer.PathBuffer+rpd->MountPointReparseBuffer.SubstituteNameOffset/sizeof(BOOST_AFIO_V2_NAMESPACE::path::value_type), rpd->MountPointReparseBuffer.SubstituteNameLength/sizeof(BOOST_AFIO_V2_NAMESPACE::path::value_type)), BOOST_AFIO_V2_NAMESPACE::path::direct());
+ case IO_REPARSE_TAG_SYMLINK:
+ return BOOST_AFIO_V2_NAMESPACE::path(path::string_type(rpd->SymbolicLinkReparseBuffer.PathBuffer+rpd->SymbolicLinkReparseBuffer.SubstituteNameOffset/sizeof(BOOST_AFIO_V2_NAMESPACE::path::value_type), rpd->SymbolicLinkReparseBuffer.SubstituteNameLength/sizeof(BOOST_AFIO_V2_NAMESPACE::path::value_type)), BOOST_AFIO_V2_NAMESPACE::path::direct());
+ }
+ BOOST_AFIO_THROW(system_error(EINVAL, generic_category()));
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::unique_ptr<mapped_file> map_file(size_t length, off_t offset, bool read_only) override final
+ {
+ if(length==(size_t)-1)
+ length=0;
+ if(!sectionh)
+ {
+ lock_guard<pathlock_t> g(pathlock);
+ if(!sectionh)
+ {
+ HANDLE h;
+ DWORD prot=!!(flags() & file_flags::write) ? PAGE_READWRITE : PAGE_READONLY;
+ if(INVALID_HANDLE_VALUE!=(h=CreateFileMapping(myid, NULL, prot, 0, 0, nullptr)))
+ sectionh=h;
+ }
+ }
+ if(sectionh)
+ {
+ DWORD prot=FILE_MAP_READ;
+ if(!read_only && !!(flags() & file_flags::write))
+ prot=FILE_MAP_WRITE;
+ void *mapaddr=nullptr;
+ if((mapaddr=MapViewOfFile(sectionh, prot, (DWORD)(offset>>32), (DWORD)(offset&0xffffffff), length)))
+ {
+ return detail::make_unique<mapped_file>(shared_from_this(), mapaddr, length, offset);
+ }
+ }
+ return nullptr;
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void link(const path_req &_req) override final
+ {
+ if(!myid)
+ BOOST_AFIO_THROW(std::invalid_argument("Currently cannot hard link a closed file by handle."));
+ path_req req(_req);
+ auto newdirh=decode_relative_path<async_file_io_dispatcher_windows, async_io_handle_windows>(req);
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ IO_STATUS_BLOCK isb={ 0 };
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769];
+ FILE_LINK_INFORMATION *fni=(FILE_LINK_INFORMATION *) buffer;
+ fni->ReplaceIfExists=false;
+ fni->RootDirectory=newdirh ? newdirh->native_handle() : nullptr;
+ fni->FileNameLength=(ULONG)(req.path.native().size()*sizeof(path::value_type));
+ memcpy(fni->FileName, req.path.c_str(), fni->FileNameLength);
+ BOOST_AFIO_ERRHNTFN(NtSetInformationFile(myid, &isb, fni, sizeof(FILE_LINK_INFORMATION)+fni->FileNameLength, FileLinkInformation), [this]{return path();});
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void unlink() override final
+ {
+ if(!myid)
+ BOOST_AFIO_THROW(std::invalid_argument("Currently cannot unlink a closed file by handle."));
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+#if 0
+ // Despite what is claimed on the internet, NtDeleteFile does NOT delete the file immediately.
+ // Moreover, it cannot delete directories nor junction points, so we'll go the more proper path.
+ OBJECT_ATTRIBUTES oa={sizeof(OBJECT_ATTRIBUTES)};
+ oa.RootDirectory=myid;
+ UNICODE_STRING _path;
+ path::value_type c=0;
+ _path.Buffer=&c;
+ _path.MaximumLength=(_path.Length=0)+sizeof(path::value_type);
+ oa.ObjectName=&_path;
+ //if(opened_as_symlink())
+ // oa.Attributes=0x100/*OBJ_OPENLINK*/; // Seems to dislike deleting junctions points without this
+ NTSTATUS ntstat=NtDeleteFile(&oa);
+ // Returns this error if we are deleting a junction point
+ if(ntstat!=0xC0000278/*STATUS_IO_REPARSE_DATA_INVALID*/)
+ BOOST_AFIO_ERRHNTFN(ntstat, [this]{return path();});
+#else
+ IO_STATUS_BLOCK isb={ 0 };
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769+sizeof(FILE_NAME_INFORMATION)/sizeof(path::value_type)];
+ // This is done in two steps to stop annoying temporary failures
+ // Firstly, get where I am within my volume, NtQueryObject returns too much so simply fetch my current name
+ // Then try to rename myself to the closest to the root as possible with a .afiodXXXXX crypto random name
+ // If I fail to rename myself there, try the next directory up, usually at some point I'll find some directory
+ // I'm allowed write to
+ FILE_NAME_INFORMATION *fni=(FILE_NAME_INFORMATION *) buffer;
+ BOOST_AFIO_V2_NAMESPACE::path mypath;
+ bool success=false;
+ do
+ {
+ auto _randomname(".afiod"+utils::random_string(32 /* 128 bits */));
+ filesystem::path::string_type randomname(_randomname.begin(), _randomname.end());
+ mypath=path(true);
+ BOOST_AFIO_ERRHNTFN(NtQueryInformationFile(myid, &isb, fni, sizeof(buffer), FileNameInformation), [this]{return path(); });
+ filesystem::path myloc(filesystem::path::string_type(fni->FileName, fni->FileNameLength/sizeof(path::value_type))), prefix(mypath.native());
+ const_cast<filesystem::path::string_type &>(prefix.native()).resize(prefix.native().size()-myloc.native().size());
+ auto try_rename=[&]{
+ FILE_RENAME_INFORMATION *fri=(FILE_RENAME_INFORMATION *) buffer;
+ fri->ReplaceIfExists=false;
+ fri->RootDirectory=nullptr; // rename to the same directory
+ size_t n=prefix.make_preferred().native().size();
+ memcpy(fri->FileName, prefix.c_str(), prefix.native().size()*sizeof(path::value_type));
+ fri->FileName[n++]='\\';
+ memcpy(fri->FileName+n, randomname.c_str(), randomname.size()*sizeof(path::value_type));
+ n+=randomname.size();
+ fri->FileName[n]=0;
+ fri->FileNameLength=(ULONG) (n*sizeof(path::value_type));
+ NTSTATUS ntstat=NtSetInformationFile(myid, &isb, fri, sizeof(FILE_RENAME_INFORMATION)+fri->FileNameLength, FileRenameInformation);
+ // Access denied may come back if we can't rename to the location or we are renaming a directory
+ // and that directory contains open files. We can't tell the difference, so keep going
+ if(!ntstat)
+ success=true;
+#if 0
+ else
+ std::wcout << "unlink() failed to rename to " << fri->FileName << " due to " << ntstat << std::endl;
+#endif
+ return ntstat;
+ };
+ if(try_rename())
+ {
+ myloc=myloc.parent_path();
+ for(auto &fragment : myloc)
+ {
+ prefix/=fragment;
+ if(fragment.native().size()>1 && !try_rename())
+ break;
+ }
+ }
+ } while(!success && mypath!=path(true));
+ // By this point maybe the file/directory was renamed, maybe it was not.
+ FILE_BASIC_INFORMATION fbi={ 0 };
+ fbi.FileAttributes=FILE_ATTRIBUTE_HIDDEN;
+ NtSetInformationFile(myid, &isb, &fbi, sizeof(fbi), FileBasicInformation);
+ FILE_DISPOSITION_INFORMATION fdi={ true };
+ BOOST_AFIO_ERRHNTFN(NtSetInformationFile(myid, &isb, &fdi, sizeof(fdi), FileDispositionInformation), [this]{return path();});
+#endif
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void atomic_relink(const path_req &_req) override final
+ {
+ if(!myid)
+ BOOST_AFIO_THROW(std::invalid_argument("Currently cannot relink a closed file by handle."));
+ path_req req(_req);
+ auto newdirh=decode_relative_path<async_file_io_dispatcher_windows, async_io_handle_windows>(req);
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ IO_STATUS_BLOCK isb={ 0 };
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769];
+ FILE_RENAME_INFORMATION *fni=(FILE_RENAME_INFORMATION *) buffer;
+ fni->ReplaceIfExists=true;
+ fni->RootDirectory=newdirh ? newdirh->native_handle() : nullptr;
+ fni->FileNameLength=(ULONG)(req.path.native().size()*sizeof(path::value_type));
+ memcpy(fni->FileName, req.path.c_str(), fni->FileNameLength);
+ BOOST_AFIO_ERRHNTFN(NtSetInformationFile(myid, &isb, fni, sizeof(FILE_RENAME_INFORMATION)+fni->FileNameLength, FileRenameInformation), [this]{return path();});
+ }
+ };
+ inline async_io_handle_windows::async_io_handle_windows(dispatcher *_parent,
+ const BOOST_AFIO_V2_NAMESPACE::path &path, file_flags flags, bool _SyncOnClose, HANDLE _h)
+ : handle(_parent, flags),
+ h(new asio::windows::random_access_handle(_parent->p->pool->io_service(), int_checkHandle(_h, path))), myid(_h),
+ has_been_added(false), SyncOnClose(_SyncOnClose), sectionh(nullptr), _path(path)
+ {
+ if(!!(flags & file_flags::os_lockable))
+ lockfile=process_lockfile_registry::open<win_lock_file>(this);
+ }
+
+ class async_file_io_dispatcher_windows : public dispatcher
+ {
+ template<class Impl, class Handle> friend handle_ptr detail::decode_relative_path(path_req &req, bool force_absolute);
+ friend class dispatcher;
+ friend class directory_entry;
+ friend void directory_entry::_int_fetch(metadata_flags wanted, handle_ptr dirh);
+ friend struct async_io_handle_windows;
+ size_t pagesize;
+ handle_ptr decode_relative_path(path_req &req, bool force_absolute=false)
+ {
+ return detail::decode_relative_path<async_file_io_dispatcher_windows, async_io_handle_windows>(req, force_absolute);
+ }
+
+ // Called in unknown thread
+ completion_returntype dodir(size_t id, future<> op, path_req req)
+ {
+ req.flags=fileflags(req.flags)|file_flags::int_opening_dir|file_flags::read;
+ // TODO FIXME: Currently file_flags::create may duplicate handles in the dirhcache
+ if(!(req.flags & file_flags::unique_directory_handle) && !!(req.flags & file_flags::read) && !(req.flags & file_flags::write) && !(req.flags & (file_flags::create|file_flags::create_only_if_not_exist)))
+ {
+ path_req req2(req);
+ // Return a copy of the one in the dir cache if available as a fast path
+ decode_relative_path(req2, true);
+ try
+ {
+ return std::make_pair(true, p->get_handle_to_dir(this, id, req2, &async_file_io_dispatcher_windows::dofile));
+ }
+ catch(...)
+ {
+ // fall through
+ }
+ }
+ // This will add itself to the dir cache if it's eligible
+ return dofile(id, op, req);
+ }
+ // Called in unknown thread
+ completion_returntype dounlink(bool is_dir, size_t id, future<> op, path_req req)
+ {
+ req.flags=fileflags(req.flags);
+ // Deleting the input op?
+ if(req.path.empty())
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ if(p->is_open()!=handle::open_states::closed)
+ {
+ p->unlink();
+ return std::make_pair(true, op.get_handle());
+ }
+ }
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ auto dirh=decode_relative_path(req);
+#if 0
+ OBJECT_ATTRIBUTES oa={sizeof(OBJECT_ATTRIBUTES)};
+ oa.RootDirectory=dirh ? dirh->native_handle() : nullptr;
+ UNICODE_STRING _path;
+ // If relative path, or symlinked DOS path, use case insensitive
+ bool isSymlinkedDosPath=(req.path.native().size()>3 && req.path.native()[0]=='\\' && req.path.native()[1]=='?' && req.path.native()[2]=='?' && req.path.native()[3]=='\\');
+ oa.Attributes=(req.path.native()[0]!='\\' || isSymlinkedDosPath) ? 0x40/*OBJ_CASE_INSENSITIVE*/ : 0;
+ _path.Buffer=const_cast<path::value_type *>(req.path.c_str());
+ _path.MaximumLength=(_path.Length=(USHORT) (req.path.native().size()*sizeof(path::value_type)))+sizeof(path::value_type);
+ oa.ObjectName=&_path;
+ BOOST_AFIO_ERRHNTFN(NtDeleteFile(&oa), [&req]{return req.path;});
+#else
+ // Open the file with DeleteOnClose, renaming it before closing the handle.
+ NTSTATUS ntstat;
+ HANDLE temph;
+ req.flags=req.flags|file_flags::delete_on_close|file_flags::int_file_share_delete;
+ std::tie(ntstat, temph)=ntcreatefile(dirh, req.path, req.flags, false);
+ BOOST_AFIO_ERRHNTFN(ntstat, [&req]{return req.path;});
+ auto untemph=detail::Undoer([&temph]{if(temph) CloseHandle(temph);});
+ IO_STATUS_BLOCK isb={ 0 };
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769];
+ FILE_RENAME_INFORMATION *fni=(FILE_RENAME_INFORMATION *) buffer;
+ fni->ReplaceIfExists=false;
+ fni->RootDirectory=nullptr; // same directory
+ auto randompath(".afiod"+utils::random_string(32 /* 128 bits */));
+ fni->FileNameLength=(ULONG)(randompath.size()*sizeof(path::value_type));
+ for(size_t n=0; n<randompath.size(); n++)
+ fni->FileName[n]=randompath[n];
+ fni->FileName[randompath.size()]=0;
+ ntstat=NtSetInformationFile(temph, &isb, fni, sizeof(FILE_RENAME_INFORMATION)+fni->FileNameLength, FileRenameInformation);
+ // Access denied may come back if the directory contains any files with open handles
+ // If that happens, ignore and mark to delete anyway
+ //if(ntstat==0xC0000022/*STATUS_ACCESS_DENIED*/)
+ FILE_BASIC_INFORMATION fbi={ 0 };
+ fbi.FileAttributes=FILE_ATTRIBUTE_HIDDEN;
+ NtSetInformationFile(temph, &isb, &fbi, sizeof(fbi), FileBasicInformation);
+ FILE_DISPOSITION_INFORMATION fdi={ true };
+ BOOST_AFIO_ERRHNTFN(NtSetInformationFile(temph, &isb, &fdi, sizeof(fdi), FileDispositionInformation), [this]{return path(); });
+#endif
+ return std::make_pair(true, op.get_handle());
+ }
+ // Called in unknown thread
+ completion_returntype dormdir(size_t id, future<> op, path_req req)
+ {
+ req.flags=fileflags(req.flags)|file_flags::int_opening_dir;
+ return dounlink(true, id, std::move(op), std::move(req));
+ }
+ public:
+ private:
+ // Called in unknown thread
+ completion_returntype dofile(size_t id, future<> op, path_req req)
+ {
+ NTSTATUS status=0;
+ HANDLE h=nullptr;
+ req.flags=fileflags(req.flags);
+ handle_ptr dirh=decode_relative_path(req);
+ std::tie(status, h)=ntcreatefile(dirh, req.path, req.flags);
+ if(dirh)
+ req.path=dirh->path()/req.path;
+ BOOST_AFIO_ERRHNTFN(status, [&req]{return req.path;});
+ // If writing and SyncOnClose and NOT synchronous, turn on SyncOnClose
+ auto ret=std::make_shared<async_io_handle_windows>(this, req.path, req.flags,
+ (file_flags::sync_on_close|file_flags::write)==(req.flags & (file_flags::sync_on_close|file_flags::write|file_flags::always_sync)),
+ h);
+ static_cast<async_io_handle_windows *>(ret.get())->do_add_io_handle_to_parent();
+ // If creating a file or directory or link and he wants compression, try that now
+ if(!!(req.flags & file_flags::create_compressed) && (!!(req.flags & file_flags::create_only_if_not_exist) || !!(req.flags & file_flags::create)))
+ {
+ DWORD bytesout=0;
+ USHORT val=COMPRESSION_FORMAT_DEFAULT;
+ BOOST_AFIO_ERRHWINFN(DeviceIoControl(ret->native_handle(), FSCTL_SET_COMPRESSION, &val, sizeof(val), nullptr, 0, &bytesout, nullptr), [&req]{return req.path;});
+ }
+ if(!(req.flags & file_flags::int_opening_dir) && !(req.flags & file_flags::int_opening_link))
+ {
+ // If opening existing file for write, try to convert to sparse, ignoring any failures
+ if(!(req.flags & file_flags::no_sparse) && !!(req.flags & file_flags::write))
+ {
+#if defined(__MINGW32__) && !defined(__MINGW64__) && !defined(__MINGW64_VERSION_MAJOR)
+ // Mingw32 currently lacks the FILE_SET_SPARSE_BUFFER structure
+ typedef struct _FILE_SET_SPARSE_BUFFER {
+ BOOLEAN SetSparse;
+ } FILE_SET_SPARSE_BUFFER, *PFILE_SET_SPARSE_BUFFER;
+#endif
+ DWORD bytesout=0;
+ FILE_SET_SPARSE_BUFFER fssb={true};
+ DeviceIoControl(ret->native_handle(), FSCTL_SET_SPARSE, &fssb, sizeof(fssb), nullptr, 0, &bytesout, nullptr);
+ }
+ }
+ return std::make_pair(true, ret);
+ }
+ // Called in unknown thread
+ completion_returntype dormfile(size_t id, future<> op, path_req req)
+ {
+ return dounlink(true, id, std::move(op), std::move(req));
+ }
+ // Called in unknown thread
+ void boost_asio_symlink_completion_handler(size_t id, handle_ptr h, std::shared_ptr<std::unique_ptr<path::value_type[]>> buffer, const error_code &ec, size_t bytes_transferred)
+ {
+ if(ec)
+ {
+ exception_ptr e;
+ // boost::system::system_error makes no attempt to ask windows for what the error code means :(
+ try
+ {
+ BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();});
+ }
+ catch(...)
+ {
+ e=current_exception();
+ }
+ complete_async_op(id, h, e);
+ }
+ else
+ {
+ complete_async_op(id, h);
+ }
+ }
+ // Called in unknown thread
+ completion_returntype dosymlink(size_t id, future<> op, path_req req)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ req.flags=fileflags(req.flags);
+ req.flags=req.flags|file_flags::int_opening_link;
+ // For safety, best not create unless doesn't exist
+ if(!!(req.flags&file_flags::create))
+ {
+ req.flags=req.flags&~file_flags::create;
+ req.flags=req.flags|file_flags::create_only_if_not_exist;
+ }
+ // If not creating, simply open
+ if(!(req.flags&file_flags::create_only_if_not_exist))
+ return dofile(id, op, req);
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ using windows_nt_kernel::REPARSE_DATA_BUFFER;
+ // Our new object needs write access and if we're linking a directory, create a directory
+ req.flags=req.flags|file_flags::write;
+ if(!!(h->flags()&file_flags::int_opening_dir))
+ req.flags=req.flags|file_flags::int_opening_dir;
+ completion_returntype ret=dofile(id, op, req);
+ assert(ret.first);
+ path destpath(h->path(true));
+ size_t destpathbytes=destpath.native().size()*sizeof(path::value_type);
+ size_t buffersize=sizeof(REPARSE_DATA_BUFFER)+destpathbytes*2+256;
+ auto buffer=std::make_shared<std::unique_ptr<path::value_type[]>>(new path::value_type[buffersize]);
+ REPARSE_DATA_BUFFER *rpd=(REPARSE_DATA_BUFFER *) buffer->get();
+ memset(rpd, 0, sizeof(*rpd));
+ // Create a directory junction for directories and symbolic links for files
+ if(!!(h->flags()&file_flags::int_opening_dir))
+ rpd->ReparseTag=IO_REPARSE_TAG_MOUNT_POINT;
+ else
+ rpd->ReparseTag=IO_REPARSE_TAG_SYMLINK;
+ if(destpath.native().size()>5 && isalpha(destpath.native()[4]) && destpath.native()[5]==':')
+ {
+ memcpy(rpd->MountPointReparseBuffer.PathBuffer, destpath.c_str(), destpathbytes+sizeof(path::value_type));
+ rpd->MountPointReparseBuffer.SubstituteNameOffset=0;
+ rpd->MountPointReparseBuffer.SubstituteNameLength=(USHORT)destpathbytes;
+ rpd->MountPointReparseBuffer.PrintNameOffset=(USHORT)(destpathbytes+sizeof(path::value_type));
+ rpd->MountPointReparseBuffer.PrintNameLength=(USHORT)(destpathbytes-4);
+ memcpy(rpd->MountPointReparseBuffer.PathBuffer+rpd->MountPointReparseBuffer.PrintNameOffset/sizeof(path::value_type), destpath.c_str()+4, rpd->MountPointReparseBuffer.PrintNameLength+sizeof(path::value_type));
+ }
+ else
+ {
+ memcpy(rpd->MountPointReparseBuffer.PathBuffer, destpath.c_str(), destpathbytes+sizeof(path::value_type));
+ rpd->MountPointReparseBuffer.SubstituteNameOffset=0;
+ rpd->MountPointReparseBuffer.SubstituteNameLength=(USHORT)destpathbytes;
+ rpd->MountPointReparseBuffer.PrintNameOffset=(USHORT)(destpathbytes+sizeof(path::value_type));
+ rpd->MountPointReparseBuffer.PrintNameLength=(USHORT)destpathbytes;
+ memcpy(rpd->MountPointReparseBuffer.PathBuffer+rpd->MountPointReparseBuffer.PrintNameOffset/sizeof(path::value_type), h->path().c_str(), rpd->MountPointReparseBuffer.PrintNameLength+sizeof(path::value_type));
+ }
+ size_t headerlen=offsetof(REPARSE_DATA_BUFFER, MountPointReparseBuffer);
+ size_t reparsebufferheaderlen=offsetof(REPARSE_DATA_BUFFER, MountPointReparseBuffer.PathBuffer)-headerlen;
+ rpd->ReparseDataLength=(USHORT)(rpd->MountPointReparseBuffer.SubstituteNameLength+rpd->MountPointReparseBuffer.PrintNameLength+2*sizeof(path::value_type)+reparsebufferheaderlen);
+ auto dirop(ret.second);
+ asio::windows::overlapped_ptr ol(threadsource()->io_service(), [this, id,
+ BOOST_AFIO_LAMBDA_MOVE_CAPTURE(dirop),
+ BOOST_AFIO_LAMBDA_MOVE_CAPTURE(buffer)](const error_code &ec, size_t bytes){ boost_asio_symlink_completion_handler(id, std::move(dirop), std::move(buffer), ec, bytes);});
+ DWORD bytesout=0;
+ BOOL ok=DeviceIoControl(ret.second->native_handle(), FSCTL_SET_REPARSE_POINT, rpd, (DWORD)(rpd->ReparseDataLength+headerlen), NULL, 0, &bytesout, ol.get());
+ DWORD errcode=GetLastError();
+ if(!ok && ERROR_IO_PENDING!=errcode)
+ {
+ //std::cerr << "ERROR " << errcode << std::endl;
+ error_code ec(errcode, asio::error::get_system_category());
+ ol.complete(ec, ol.get()->InternalHigh);
+ }
+ else
+ ol.release();
+ // Indicate we're not finished yet
+ return std::make_pair(false, h);
+ }
+ // Called in unknown thread
+ completion_returntype dormsymlink(size_t id, future<> op, path_req req)
+ {
+ req.flags=fileflags(req.flags)|file_flags::int_opening_link;
+ return dounlink(true, id, std::move(op), std::move(req));
+ }
+ // Called in unknown thread
+ completion_returntype dozero(size_t id, future<> op, std::vector<std::pair<off_t, off_t>> ranges)
+ {
+#if defined(__MINGW32__) && !defined(__MINGW64__) && !defined(__MINGW64_VERSION_MAJOR)
+ // Mingw32 currently lacks the FILE_ZERO_DATA_INFORMATION structure and FSCTL_SET_ZERO_DATA
+ typedef struct _FILE_ZERO_DATA_INFORMATION {
+ LARGE_INTEGER FileOffset;
+ LARGE_INTEGER BeyondFinalZero;
+ } FILE_ZERO_DATA_INFORMATION, *PFILE_ZERO_DATA_INFORMATION;
+#define FSCTL_SET_ZERO_DATA CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 50, METHOD_BUFFERED, FILE_WRITE_DATA)
+#endif
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ assert(p);
+ auto buffers=std::make_shared<std::vector<FILE_ZERO_DATA_INFORMATION>>();
+ buffers->reserve(ranges.size());
+ off_t bytes=0;
+ for(auto &i : ranges)
+ {
+ FILE_ZERO_DATA_INFORMATION fzdi;
+ fzdi.FileOffset.QuadPart=i.first;
+ fzdi.BeyondFinalZero.QuadPart=i.first+i.second;
+ buffers->push_back(std::move(fzdi));
+ bytes+=i.second;
+ }
+ auto bytes_to_transfer=std::make_shared<std::pair<atomic<bool>, atomic<off_t>>>();
+ bytes_to_transfer->first=false;
+ bytes_to_transfer->second=bytes;
+ auto completion_handler=[this, id, h, bytes_to_transfer, buffers](const error_code &ec, size_t bytes, off_t thisbytes)
+ {
+ if(ec)
+ {
+ exception_ptr e;
+ // boost::system::system_error makes no attempt to ask windows for what the error code means :(
+ try
+ {
+ BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();});
+ }
+ catch(...)
+ {
+ e=current_exception();
+ bool exp=false;
+ // If someone else has already returned an error, simply exit
+ if(!bytes_to_transfer->first.compare_exchange_strong(exp, true))
+ return;
+ }
+ complete_async_op(id, h, e);
+ }
+ else if(!(bytes_to_transfer->second-=thisbytes))
+ {
+ complete_async_op(id, h);
+ }
+ };
+ for(auto &i : *buffers)
+ {
+ asio::windows::overlapped_ptr ol(threadsource()->io_service(), std::bind(completion_handler, std::placeholders::_1, std::placeholders::_2, i.BeyondFinalZero.QuadPart-i.FileOffset.QuadPart));
+ DWORD bytesout=0;
+ BOOL ok=DeviceIoControl(p->native_handle(), FSCTL_SET_ZERO_DATA, &i, sizeof(i), nullptr, 0, &bytesout, ol.get());
+ DWORD errcode=GetLastError();
+ if(!ok && ERROR_IO_PENDING!=errcode)
+ {
+ //std::cerr << "ERROR " << errcode << std::endl;
+ error_code ec(errcode, asio::error::get_system_category());
+ ol.complete(ec, ol.get()->InternalHigh);
+ }
+ else
+ ol.release();
+ }
+ // Indicate we're not finished yet
+ return std::make_pair(false, h);
+ }
+ // Called in unknown thread
+ completion_returntype dosync(size_t id, future<> op, future<>)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ assert(p);
+ off_t bytestobesynced=p->write_count_since_fsync();
+ if(bytestobesynced)
+ BOOST_AFIO_ERRHWINFN(FlushFileBuffers(p->native_handle()), [p]{return p->path();});
+ p->byteswrittenatlastfsync+=bytestobesynced;
+ return std::make_pair(true, h);
+ }
+ // Called in unknown thread
+ completion_returntype doclose(size_t id, future<> op, future<>)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ assert(p);
+ if(!!(p->flags() & file_flags::int_opening_dir) && !(p->flags() & file_flags::unique_directory_handle) && !!(p->flags() & file_flags::read) && !(p->flags() & file_flags::write))
+ {
+ // As this is a directory which may be a fast directory enumerator, ignore close
+ }
+ else
+ {
+ p->close();
+ }
+ return std::make_pair(true, h);
+ }
+ // Called in unknown thread
+ void boost_asio_readwrite_completion_handler(bool is_write, size_t id, handle_ptr h, std::shared_ptr<std::tuple<atomic<bool>, atomic<size_t>, detail::io_req_impl<true>>> bytes_to_transfer, std::tuple<off_t, size_t, size_t, size_t> pars, const error_code &ec, size_t bytes_transferred)
+ {
+ if(!this->p->filters_buffers.empty())
+ {
+ for(auto &i: this->p->filters_buffers)
+ {
+ if(i.first==OpType::Unknown || (!is_write && i.first==OpType::read) || (is_write && i.first==OpType::write))
+ {
+ i.second(is_write ? OpType::write : OpType::read, h.get(), std::get<2>(*bytes_to_transfer), std::get<0>(pars), std::get<1>(pars), std::get<2>(pars), ec, bytes_transferred);
+ }
+ }
+ }
+ if(ec)
+ {
+ exception_ptr e;
+ // boost::system::system_error makes no attempt to ask windows for what the error code means :(
+ try
+ {
+ BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();});
+ }
+ catch(...)
+ {
+ e=current_exception();
+ bool exp=false;
+ // If someone else has already returned an error, simply exit
+ if(!std::get<0>(*bytes_to_transfer).compare_exchange_strong(exp, true))
+ return;
+ }
+ complete_async_op(id, h, e);
+ }
+ else
+ {
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ if(is_write)
+ p->byteswritten+=bytes_transferred;
+ else
+ p->bytesread+=bytes_transferred;
+ size_t togo=(std::get<1>(*bytes_to_transfer)-=std::get<3>(pars));
+ if(!togo) // bytes_this_chunk may not equal bytes_transferred if final 4Kb chunk of direct file
+ complete_async_op(id, h);
+ if(togo>((size_t)1<<(8*sizeof(size_t)-1)))
+ BOOST_AFIO_THROW_FATAL(std::runtime_error("IOCP returned more bytes than we asked for. This is probably memory corruption."));
+ BOOST_AFIO_DEBUG_PRINT("H %u e=%u togo=%u bt=%u bc=%u\n", (unsigned) id, (unsigned) ec.value(), (unsigned) togo, (unsigned) bytes_transferred, (unsigned) std::get<3>(pars));
+ }
+ //std::cout << "id=" << id << " total=" << bytes_to_transfer->second << " this=" << bytes_transferred << std::endl;
+ }
+ template<bool iswrite> void doreadwrite(size_t id, handle_ptr h, detail::io_req_impl<iswrite> req, async_io_handle_windows *p)
+ {
+ // asio::async_read_at() seems to have a bug and only transfers 64Kb per buffer
+ // asio::windows::random_access_handle::async_read_some_at() clearly bothers
+ // with the first buffer only. Same goes for both async write functions.
+ //
+ // So we implement by hand and skip ASIO altogether.
+ size_t amount=0;
+ for(auto &b: req.buffers)
+ {
+ amount+=asio::buffer_size(b);
+ }
+ auto bytes_to_transfer=std::make_shared<std::tuple<atomic<bool>, atomic<size_t>, detail::io_req_impl<true>>>();
+ //mingw choked on atomic<T>::operator=, thought amount was atomic&, so changed to store to avoid issue
+ std::get<1>(*bytes_to_transfer).store(amount);
+ std::get<2>(*bytes_to_transfer)=req;
+ // Are we using direct i/o, because then we get the magic scatter/gather special functions?
+ if(!!(p->flags() & file_flags::os_direct))
+ {
+ // Yay we can use the direct i/o scatter gather functions which are far more efficient
+ size_t pages=amount/pagesize, thisbufferoffset;
+ std::vector<FILE_SEGMENT_ELEMENT> elems(1+pages);
+ auto bufferit=req.buffers.begin();
+ thisbufferoffset=0;
+ for(size_t n=0; n<pages; n++)
+ {
+ // Be careful of 32 bit void * sign extension here ...
+ elems[n].Alignment=((size_t) asio::buffer_cast<const void *>(*bufferit))+thisbufferoffset;
+ thisbufferoffset+=pagesize;
+ if(thisbufferoffset>=asio::buffer_size(*bufferit))
+ {
+ ++bufferit;
+ thisbufferoffset=0;
+ }
+ }
+ elems[pages].Alignment=0;
+ auto t(std::make_tuple(req.where, 0, req.buffers.size(), amount));
+ asio::windows::overlapped_ptr ol(threadsource()->io_service(), [this, id, h, bytes_to_transfer,
+ BOOST_AFIO_LAMBDA_MOVE_CAPTURE(t)](const error_code &ec, size_t bytes) {
+ boost_asio_readwrite_completion_handler(iswrite, id, std::move(h),
+ bytes_to_transfer, std::move(t), ec, bytes); });
+ ol.get()->Offset=(DWORD) (req.where & 0xffffffff);
+ ol.get()->OffsetHigh=(DWORD) ((req.where>>32) & 0xffffffff);
+ BOOL ok=iswrite ? WriteFileGather
+ (p->native_handle(), &elems.front(), (DWORD) amount, NULL, ol.get())
+ : ReadFileScatter
+ (p->native_handle(), &elems.front(), (DWORD) amount, NULL, ol.get());
+ DWORD errcode=GetLastError();
+ if(!ok && ERROR_IO_PENDING!=errcode)
+ {
+ //std::cerr << "ERROR " << errcode << std::endl;
+ error_code ec(errcode, asio::error::get_system_category());
+ ol.complete(ec, ol.get()->InternalHigh);
+ }
+ else
+ ol.release();
+ }
+ else
+ {
+ size_t offset=0, n=0;
+ for(auto &b: req.buffers)
+ {
+ auto t(std::make_tuple(req.where+offset, n, 1, asio::buffer_size(b)));
+ asio::windows::overlapped_ptr ol(threadsource()->io_service(), [this, id, h, bytes_to_transfer,
+ BOOST_AFIO_LAMBDA_MOVE_CAPTURE(t)](const error_code &ec, size_t bytes) {
+ boost_asio_readwrite_completion_handler(iswrite, id, std::move(h),
+ bytes_to_transfer, std::move(t), ec, bytes); });
+ ol.get()->Offset=(DWORD) ((req.where+offset) & 0xffffffff);
+ ol.get()->OffsetHigh=(DWORD) (((req.where+offset)>>32) & 0xffffffff);
+ DWORD bytesmoved=0;
+ BOOL ok=iswrite ? WriteFile
+ (p->native_handle(), asio::buffer_cast<const void *>(b), (DWORD) asio::buffer_size(b), &bytesmoved, ol.get())
+ : ReadFile
+ (p->native_handle(), (LPVOID) asio::buffer_cast<const void *>(b), (DWORD) asio::buffer_size(b), &bytesmoved, ol.get());
+ DWORD errcode=GetLastError();
+ if(!ok && ERROR_IO_PENDING!=errcode)
+ {
+ //std::cerr << "ERROR " << errcode << std::endl;
+ error_code ec(errcode, asio::error::get_system_category());
+ ol.complete(ec, ol.get()->InternalHigh);
+ }
+ else
+ ol.release();
+ offset+=asio::buffer_size(b);
+ n++;
+ }
+ }
+ }
+ // Called in unknown thread
+ completion_returntype doread(size_t id, future<> op, detail::io_req_impl<false> req)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ assert(p);
+ BOOST_AFIO_DEBUG_PRINT("R %u %p (%c) @ %u, b=%u\n", (unsigned) id, h.get(), p->path().native().back(), (unsigned) req.where, (unsigned) req.buffers.size());
+#ifdef DEBUG_PRINTING
+ for(auto &b: req.buffers)
+ { BOOST_AFIO_DEBUG_PRINT(" R %u: %p %u\n", (unsigned) id, asio::buffer_cast<const void *>(b), (unsigned) asio::buffer_size(b)); }
+#endif
+ doreadwrite(id, h, req, p);
+ // Indicate we're not finished yet
+ return std::make_pair(false, h);
+ }
+ // Called in unknown thread
+ completion_returntype dowrite(size_t id, future<> op, detail::io_req_impl<true> req)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ assert(p);
+ BOOST_AFIO_DEBUG_PRINT("W %u %p (%c) @ %u, b=%u\n", (unsigned) id, h.get(), p->path().native().back(), (unsigned) req.where, (unsigned) req.buffers.size());
+#ifdef DEBUG_PRINTING
+ for(auto &b: req.buffers)
+ { BOOST_AFIO_DEBUG_PRINT(" W %u: %p %u\n", (unsigned) id, asio::buffer_cast<const void *>(b), (unsigned) asio::buffer_size(b)); }
+#endif
+ doreadwrite(id, h, req, p);
+ // Indicate we're not finished yet
+ return std::make_pair(false, h);
+ }
+ // Called in unknown thread
+ completion_returntype dotruncate(size_t id, future<> op, off_t _newsize)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ assert(p);
+ BOOST_AFIO_DEBUG_PRINT("T %u %p (%c)\n", (unsigned) id, h.get(), p->path().native().back());
+#if 1
+ BOOST_AFIO_ERRHWINFN(wintruncate(p->native_handle(), _newsize), [p]{return p->path();});
+#else
+ // This is a bit tricky ... overlapped files ignore their file position except in this one
+ // case, but clearly here we have a race condition. No choice but to rinse and repeat I guess.
+ LARGE_INTEGER size={0}, newsize={0};
+ newsize.QuadPart=_newsize;
+ while(size.QuadPart!=newsize.QuadPart)
+ {
+ BOOST_AFIO_ERRHWINFN(SetFilePointerEx(p->native_handle(), newsize, NULL, FILE_BEGIN), [p]{return p->path();});
+ BOOST_AFIO_ERRHWINFN(SetEndOfFile(p->native_handle()), [p]{return p->path();});
+ BOOST_AFIO_ERRHWINFN(GetFileSizeEx(p->native_handle(), &size), [p]{return p->path();});
+ }
+#endif
+ return std::make_pair(true, h);
+ }
+ // Called in unknown thread
+ struct enumerate_state : std::enable_shared_from_this<enumerate_state>
+ {
+ size_t id;
+ future<> op;
+ std::shared_ptr<promise<std::pair<std::vector<directory_entry>, bool>>> ret;
+ std::unique_ptr<windows_nt_kernel::FILE_ID_FULL_DIR_INFORMATION[]> buffer;
+ enumerate_req req;
+ enumerate_state(size_t _id, future<> _op, std::shared_ptr<promise<std::pair<std::vector<directory_entry>, bool>>> _ret,
+ enumerate_req _req) : id(_id), op(std::move(_op)), ret(std::move(_ret)), req(std::move(_req)) { reallocate_buffer(); }
+ void reallocate_buffer()
+ {
+ using windows_nt_kernel::FILE_ID_FULL_DIR_INFORMATION;
+ buffer=std::unique_ptr<FILE_ID_FULL_DIR_INFORMATION[]>(new FILE_ID_FULL_DIR_INFORMATION[req.maxitems]);
+ }
+ };
+ typedef std::shared_ptr<enumerate_state> enumerate_state_t;
+ void boost_asio_enumerate_completion_handler(enumerate_state_t state, const error_code &ec, size_t bytes_transferred)
+ {
+ using namespace windows_nt_kernel;
+ size_t id=state->id;
+ handle_ptr h(state->op.get_handle());
+ std::shared_ptr<promise<std::pair<std::vector<directory_entry>, bool>>> &ret=state->ret;
+ std::unique_ptr<FILE_ID_FULL_DIR_INFORMATION[]> &buffer=state->buffer;
+ enumerate_req &req=state->req;
+ if(ec && ERROR_MORE_DATA==ec.value() && bytes_transferred<(sizeof(FILE_ID_FULL_DIR_INFORMATION)+buffer.get()->FileNameLength))
+ {
+ // Bump maxitems by one and reschedule.
+ req.maxitems++;
+ state->reallocate_buffer();
+ doenumerate(state);
+ return;
+ }
+ if(ec && ERROR_MORE_DATA!=ec.value())
+ {
+ if(ERROR_NO_MORE_FILES==ec.value())
+ {
+ ret->set_value(std::make_pair(std::vector<directory_entry>(), false));
+ complete_async_op(id, h);
+ return;
+ }
+ exception_ptr e;
+ // boost::system::system_error makes no attempt to ask windows for what the error code means :(
+ try
+ {
+ BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();});
+ }
+ catch(...)
+ {
+ e=current_exception();
+ }
+ ret->set_exception(e);
+ complete_async_op(id, h, e);
+ }
+ else
+ {
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ using windows_nt_kernel::to_st_type;
+ using windows_nt_kernel::to_timepoint;
+ std::vector<directory_entry> _ret;
+ _ret.reserve(req.maxitems);
+ bool thisbatchdone=(sizeof(FILE_ID_FULL_DIR_INFORMATION)*req.maxitems-bytes_transferred)>sizeof(FILE_ID_FULL_DIR_INFORMATION);
+ directory_entry item;
+ // This is what windows returns with each enumeration
+ item.have_metadata=directory_entry::metadata_fastpath();
+ bool needmoremetadata=!!(req.metadata&~item.have_metadata);
+ bool done=false;
+ for(FILE_ID_FULL_DIR_INFORMATION *ffdi=buffer.get(); !done; ffdi=(FILE_ID_FULL_DIR_INFORMATION *)((size_t) ffdi + ffdi->NextEntryOffset))
+ {
+ if(!ffdi->NextEntryOffset) done=true;
+ size_t length=ffdi->FileNameLength/sizeof(path::value_type);
+ if(length<=2 && '.'==ffdi->FileName[0])
+ if(1==length || '.'==ffdi->FileName[1])
+ continue;
+ path::string_type leafname(ffdi->FileName, length);
+ if(req.filtering==enumerate_req::filter::fastdeleted && isDeletedFile(leafname))
+ continue;
+ item.leafname=std::move(leafname);
+ item.stat.st_ino=ffdi->FileId.QuadPart;
+ item.stat.st_type=to_st_type(ffdi->FileAttributes, ffdi->ReparsePointTag);
+ item.stat.st_atim=to_timepoint(ffdi->LastAccessTime);
+ item.stat.st_mtim=to_timepoint(ffdi->LastWriteTime);
+ item.stat.st_ctim=to_timepoint(ffdi->ChangeTime);
+ item.stat.st_size=ffdi->EndOfFile.QuadPart;
+ item.stat.st_allocated=ffdi->AllocationSize.QuadPart;
+ item.stat.st_birthtim=to_timepoint(ffdi->CreationTime);
+ item.stat.st_sparse=!!(ffdi->FileAttributes & FILE_ATTRIBUTE_SPARSE_FILE);
+ item.stat.st_compressed=!!(ffdi->FileAttributes & FILE_ATTRIBUTE_COMPRESSED);
+ item.stat.st_reparse_point=!!(ffdi->FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT);
+ _ret.push_back(std::move(item));
+ }
+ if(needmoremetadata)
+ {
+ for(auto &i: _ret)
+ {
+ try
+ {
+ i.fetch_metadata(h, req.metadata);
+ }
+ catch(...) { } // File may have vanished between enumerate and now
+ }
+ }
+ ret->set_value(std::make_pair(std::move(_ret), !thisbatchdone));
+ complete_async_op(id, h);
+ }
+ }
+ // Called in unknown thread
+ void doenumerate(enumerate_state_t state)
+ {
+ size_t id=state->id;
+ handle_ptr h(state->op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ using namespace windows_nt_kernel;
+ std::unique_ptr<FILE_ID_FULL_DIR_INFORMATION[]> &buffer=state->buffer;
+ enumerate_req &req=state->req;
+ NTSTATUS ntstat;
+ UNICODE_STRING _glob={ 0 };
+ if(!req.glob.empty())
+ {
+ _glob.Buffer=const_cast<path::value_type *>(req.glob.c_str());
+ _glob.MaximumLength=(_glob.Length=(USHORT) (req.glob.native().size()*sizeof(path::value_type)))+sizeof(path::value_type);
+ }
+ asio::windows::overlapped_ptr ol(threadsource()->io_service(), [this, state](const error_code &ec, size_t bytes)
+ {
+ if(!state)
+ abort();
+ boost_asio_enumerate_completion_handler(state, ec, bytes);
+ });
+ bool done;
+ do
+ {
+ // 2015-01-03 ned: I've been battling this stupid memory corruption bug for a while now, and it's really a bug in NT because they're probably not
+ // testing asynchronous direction enumeration so hence this evil workaround. Basically 32 bit kernels will corrupt memory if
+ // ApcContext is the i/o status block, and they demand ApcContext to be null or else! Unfortunately 64 bit kernels, including
+ // when a 32 bit process is running under WoW64, pass ApcContext not IoStatusBlock as the completion port overlapped output,
+ // so on 64 bit kernels we have no choice and must always set ApcContext to IoStatusBlock!
+ //
+ // So, if I'm a 32 bit build, check IsWow64Process() to see if I'm really 32 bit and don't set ApcContext, else set ApcContext.
+ void *ApcContext=ol.get();
+#ifndef _WIN64
+ BOOL isWow64=false;
+ if(IsWow64Process(GetCurrentProcess(), &isWow64), !isWow64)
+ ApcContext=nullptr;
+#endif
+ ntstat=NtQueryDirectoryFile(p->native_handle(), ol.get()->hEvent, NULL, ApcContext, (PIO_STATUS_BLOCK) ol.get(),
+ buffer.get(), (ULONG)(sizeof(FILE_ID_FULL_DIR_INFORMATION)*req.maxitems),
+ FileIdFullDirectoryInformation, FALSE, req.glob.empty() ? NULL : &_glob, req.restart);
+ if(STATUS_BUFFER_OVERFLOW==ntstat)
+ {
+ req.maxitems++;
+ state->reallocate_buffer();
+ done=false;
+ }
+ else done=true;
+ } while(!done);
+ if(STATUS_PENDING!=ntstat)
+ {
+ //std::cerr << "ERROR " << errcode << std::endl;
+ SetWin32LastErrorFromNtStatus(ntstat);
+ error_code ec(GetLastError(), asio::error::get_system_category());
+ ol.complete(ec, ol.get()->InternalHigh);
+ }
+ else
+ ol.release();
+ }
+ // Called in unknown thread
+ completion_returntype doenumerate(size_t id, future<> op, enumerate_req req, std::shared_ptr<promise<std::pair<std::vector<directory_entry>, bool>>> ret)
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+
+ // A bit messy this, but necessary
+ enumerate_state_t state=std::make_shared<enumerate_state>(
+ id,
+ std::move(op),
+ std::move(ret),
+ std::move(req)
+ );
+ doenumerate(std::move(state));
+
+ // Indicate we're not finished yet
+ return std::make_pair(false, handle_ptr());
+ }
+ // Called in unknown thread
+ completion_returntype doextents(size_t id, future<> op, std::shared_ptr<promise<std::vector<std::pair<off_t, off_t>>>> ret, size_t entries)
+ {
+#if defined(__MINGW32__) && !defined(__MINGW64__) && !defined(__MINGW64_VERSION_MAJOR)
+ // Mingw32 currently lacks the FILE_ALLOCATED_RANGE_BUFFER structure and FSCTL_QUERY_ALLOCATED_RANGES
+ typedef struct _FILE_ALLOCATED_RANGE_BUFFER {
+ LARGE_INTEGER FileOffset;
+ LARGE_INTEGER Length;
+ } FILE_ALLOCATED_RANGE_BUFFER, *PFILE_ALLOCATED_RANGE_BUFFER;
+#define FSCTL_QUERY_ALLOCATED_RANGES CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 51, METHOD_NEITHER, FILE_READ_DATA)
+#endif
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ assert(p);
+ auto buffers=std::make_shared<std::vector<FILE_ALLOCATED_RANGE_BUFFER>>(entries);
+ auto completion_handler=[this, id, op, ret, entries, buffers](const error_code &ec, size_t bytes)
+ {
+ handle_ptr h(op.get_handle());
+ if(ec)
+ {
+ if(ERROR_MORE_DATA==ec.value())
+ {
+ doextents(id, std::move(op), std::move(ret), entries*2);
+ return;
+ }
+ exception_ptr e;
+ // boost::system::system_error makes no attempt to ask windows for what the error code means :(
+ try
+ {
+ BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();});
+ }
+ catch(...)
+ {
+ e=current_exception();
+ }
+ ret->set_exception(e);
+ complete_async_op(id, h, e);
+ }
+ else
+ {
+ std::vector<std::pair<off_t, off_t>> out(bytes/sizeof(FILE_ALLOCATED_RANGE_BUFFER));
+ for(size_t n=0; n<out.size(); n++)
+ {
+ out[n].first=buffers->data()[n].FileOffset.QuadPart;
+ out[n].second=buffers->data()[n].Length.QuadPart;
+ }
+ ret->set_value(std::move(out));
+ complete_async_op(id, h);
+ }
+ };
+ asio::windows::overlapped_ptr ol(threadsource()->io_service(), completion_handler);
+ do
+ {
+ DWORD bytesout=0;
+ // Search entire file
+ buffers->front().FileOffset.QuadPart=0;
+ buffers->front().Length.QuadPart=((off_t)1<<63)-1; // Microsoft claims this is 1<<64-1024 for NTFS, but I get bad parameter error with anything higher than 1<<63-1.
+ BOOL ok=DeviceIoControl(p->native_handle(), FSCTL_QUERY_ALLOCATED_RANGES, buffers->data(), sizeof(FILE_ALLOCATED_RANGE_BUFFER), buffers->data(), (DWORD)(buffers->size()*sizeof(FILE_ALLOCATED_RANGE_BUFFER)), &bytesout, ol.get());
+ DWORD errcode=GetLastError();
+ if(!ok && ERROR_IO_PENDING!=errcode)
+ {
+ if(ERROR_INSUFFICIENT_BUFFER==errcode || ERROR_MORE_DATA==errcode)
+ {
+ buffers->resize(buffers->size()*2);
+ continue;
+ }
+ //std::cerr << "ERROR " << errcode << std::endl;
+ error_code ec(errcode, asio::error::get_system_category());
+ ol.complete(ec, ol.get()->InternalHigh);
+ }
+ else
+ ol.release();
+ break;
+ } while(true);
+
+ // Indicate we're not finished yet
+ return std::make_pair(false, h);
+ }
+ // Called in unknown thread
+ completion_returntype doextents(size_t id, future<> op, std::shared_ptr<promise<std::vector<std::pair<off_t, off_t>>>> ret)
+ {
+ return doextents(id, op, std::move(ret), 16);
+ }
+ // Called in unknown thread
+ completion_returntype dostatfs(size_t id, future<> op, fs_metadata_flags req, std::shared_ptr<promise<statfs_t>> out)
+ {
+ try
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ assert(p);
+ statfs_t ret;
+
+ // Probably not worth doing these asynchronously, so execute synchronously
+ BOOST_AFIO_TYPEALIGNMENT(8) path::value_type buffer[32769];
+ IO_STATUS_BLOCK isb={ 0 };
+ NTSTATUS ntstat;
+ if(!!(req&fs_metadata_flags::flags) || !!(req&fs_metadata_flags::namemax) || !!(req&fs_metadata_flags::fstypename))
+ {
+ FILE_FS_ATTRIBUTE_INFORMATION *ffai=(FILE_FS_ATTRIBUTE_INFORMATION *) buffer;
+ ntstat=NtQueryVolumeInformationFile(h->native_handle(), &isb, ffai, sizeof(buffer), FileFsAttributeInformation);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h->native_handle(), FALSE, NULL);
+ BOOST_AFIO_ERRHNTFN(ntstat, [h]{return h->path();});
+ if(!!(req&fs_metadata_flags::flags))
+ {
+ ret.f_flags.rdonly=!!(ffai->FileSystemAttributes & FILE_READ_ONLY_VOLUME);
+ ret.f_flags.acls=!!(ffai->FileSystemAttributes & FILE_PERSISTENT_ACLS);
+ ret.f_flags.xattr=!!(ffai->FileSystemAttributes & FILE_NAMED_STREAMS);
+ ret.f_flags.compression=!!(ffai->FileSystemAttributes & FILE_VOLUME_IS_COMPRESSED);
+ ret.f_flags.extents=!!(ffai->FileSystemAttributes & FILE_SUPPORTS_SPARSE_FILES);
+ ret.f_flags.filecompression=!!(ffai->FileSystemAttributes & FILE_FILE_COMPRESSION);
+ }
+ if(!!(req&fs_metadata_flags::namemax)) ret.f_namemax=ffai->MaximumComponentNameLength;
+ if(!!(req&fs_metadata_flags::fstypename))
+ {
+ ret.f_fstypename.resize(ffai->FileSystemNameLength/sizeof(path::value_type));
+ for(size_t n=0; n<ffai->FileSystemNameLength/sizeof(path::value_type); n++)
+ ret.f_fstypename[n]=(char) ffai->FileSystemName[n];
+ }
+ }
+ if(!!(req&fs_metadata_flags::bsize) || !!(req&fs_metadata_flags::blocks) || !!(req&fs_metadata_flags::bfree) || !!(req&fs_metadata_flags::bavail))
+ {
+ FILE_FS_FULL_SIZE_INFORMATION *fffsi=(FILE_FS_FULL_SIZE_INFORMATION *) buffer;
+ ntstat=NtQueryVolumeInformationFile(h->native_handle(), &isb, fffsi, sizeof(buffer), FileFsFullSizeInformation);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h->native_handle(), FALSE, NULL);
+ BOOST_AFIO_ERRHNTFN(ntstat, [h]{return h->path();});
+ if(!!(req&fs_metadata_flags::bsize)) ret.f_bsize=fffsi->BytesPerSector*fffsi->SectorsPerAllocationUnit;
+ if(!!(req&fs_metadata_flags::blocks)) ret.f_blocks=fffsi->TotalAllocationUnits.QuadPart;
+ if(!!(req&fs_metadata_flags::bfree)) ret.f_bfree=fffsi->ActualAvailableAllocationUnits.QuadPart;
+ if(!!(req&fs_metadata_flags::bavail)) ret.f_bavail=fffsi->CallerAvailableAllocationUnits.QuadPart;
+ }
+ if(!!(req&fs_metadata_flags::fsid))
+ {
+ FILE_FS_OBJECTID_INFORMATION *ffoi=(FILE_FS_OBJECTID_INFORMATION *) buffer;
+ ntstat=NtQueryVolumeInformationFile(h->native_handle(), &isb, ffoi, sizeof(buffer), FileFsObjectIdInformation);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h->native_handle(), FALSE, NULL);
+ if(0/*STATUS_SUCCESS*/==ntstat)
+ {
+ // FAT32 doesn't support filing system id, so sink error
+ memcpy(&ret.f_fsid, ffoi->ObjectId, sizeof(ret.f_fsid));
+ }
+ }
+ if(!!(req&fs_metadata_flags::iosize))
+ {
+ FILE_FS_SECTOR_SIZE_INFORMATION *ffssi=(FILE_FS_SECTOR_SIZE_INFORMATION *) buffer;
+ ntstat=NtQueryVolumeInformationFile(h->native_handle(), &isb, ffssi, sizeof(buffer), FileFsSectorSizeInformation);
+ if(STATUS_PENDING==ntstat)
+ ntstat=NtWaitForSingleObject(h->native_handle(), FALSE, NULL);
+ if(0/*STATUS_SUCCESS*/!=ntstat)
+ {
+ // Windows XP and Vista don't support the FILE_FS_SECTOR_SIZE_INFORMATION
+ // API, so we'll just hardcode 512 bytes
+ ffssi->PhysicalBytesPerSectorForPerformance=512;
+ }
+ ret.f_iosize=ffssi->PhysicalBytesPerSectorForPerformance;
+ }
+ if(!!(req&fs_metadata_flags::mntfromname) || !!(req&fs_metadata_flags::mntonname))
+ {
+ // Irrespective we need the mount path before figuring out the mounted device
+ ret.f_mntonname=MountPointFromHandle(h->native_handle());
+ if(!!(req&fs_metadata_flags::mntfromname))
+ {
+ path::string_type volumename=VolumeNameFromMountPoint(ret.f_mntonname);
+ ret.f_mntfromname.reserve(volumename.size());
+ for(size_t n=0; n<volumename.size(); n++)
+ ret.f_mntfromname.push_back((char) volumename[n]);
+ }
+ }
+ out->set_value(std::move(ret));
+ return std::make_pair(true, h);
+ }
+ catch(...)
+ {
+ out->set_exception(current_exception());
+ throw;
+ }
+ }
+ // Called in unknown thread
+ completion_returntype dolock(size_t id, future<> op, lock_req req)
+ {
+ handle_ptr h(op.get_handle());
+ async_io_handle_windows *p=static_cast<async_io_handle_windows *>(h.get());
+ assert(p);
+ if(!p->lockfile)
+ BOOST_AFIO_THROW(std::invalid_argument("This file handle was not opened with OSLockable."));
+ return p->lockfile->lock(id, std::move(op), std::move(req));
+ }
+
+ public:
+ async_file_io_dispatcher_windows(std::shared_ptr<thread_source> threadpool, file_flags flagsforce, file_flags flagsmask) : dispatcher(threadpool, flagsforce, flagsmask), pagesize(utils::page_sizes().front())
+ {
+ }
+
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> dir(const std::vector<path_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::dir, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dodir);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmdir(const std::vector<path_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::rmdir, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dormdir);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> file(const std::vector<path_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::file, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dofile);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmfile(const std::vector<path_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::rmfile, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dormfile);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> symlink(const std::vector<path_req> &reqs, const std::vector<future<>> &targets) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ std::vector<future<>> ops(targets);
+ ops.resize(reqs.size());
+ for(size_t n=0; n<reqs.size(); n++)
+ {
+ if(!ops[n].valid())
+ ops[n]=reqs[n].precondition;
+ }
+ return chain_async_ops((int) detail::OpType::symlink, ops, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dosymlink);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> rmsymlink(const std::vector<path_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::rmsymlink, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dormsymlink);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> sync(const std::vector<future<>> &ops) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::sync, ops, async_op_flags::none, &async_file_io_dispatcher_windows::dosync);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> zero(const std::vector<future<>> &ops, const std::vector<std::vector<std::pair<off_t, off_t>>> &ranges) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::zero, ops, ranges, async_op_flags::none, &async_file_io_dispatcher_windows::dozero);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> close(const std::vector<future<>> &ops) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::close, ops, async_op_flags::none, &async_file_io_dispatcher_windows::doclose);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> read(const std::vector<detail::io_req_impl<false>> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::read, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::doread);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> write(const std::vector<detail::io_req_impl<true>> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::write, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dowrite);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> truncate(const std::vector<future<>> &ops, const std::vector<off_t> &sizes) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+ if(ops.size()!=sizes.size())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+#endif
+ return chain_async_ops((int) detail::OpType::truncate, ops, sizes, async_op_flags::none, &async_file_io_dispatcher_windows::dotruncate);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<std::pair<std::vector<directory_entry>, bool>>> enumerate(const std::vector<enumerate_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::enumerate, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::doenumerate);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<std::vector<std::pair<off_t, off_t>>>> extents(const std::vector<future<>> &ops) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::extents, ops, async_op_flags::none, &async_file_io_dispatcher_windows::doextents);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<statfs_t>> statfs(const std::vector<future<>> &ops, const std::vector<fs_metadata_flags> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: ops)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+ }
+ if(ops.size()!=reqs.size())
+ BOOST_AFIO_THROW(std::runtime_error("Inputs are invalid."));
+#endif
+ return chain_async_ops((int) detail::OpType::statfs, ops, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dostatfs);
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC std::vector<future<>> lock(const std::vector<lock_req> &reqs) override final
+ {
+#if BOOST_AFIO_VALIDATE_INPUTS
+ for(auto &i: reqs)
+ {
+ if(!i.validate())
+ BOOST_AFIO_THROW(std::invalid_argument("Inputs are invalid."));
+ }
+#endif
+ return chain_async_ops((int) detail::OpType::lock, reqs, async_op_flags::none, &async_file_io_dispatcher_windows::dolock);
+ }
+ };
+
+ inline handle_ptr async_io_handle_windows::int_verifymyinode()
+ {
+ handle_ptr dirh(container());
+ if(!dirh)
+ {
+ path_req req(path(true));
+ dirh=parent()->int_get_handle_to_containing_dir(static_cast<async_file_io_dispatcher_windows *>(parent()), 0, req, &async_file_io_dispatcher_windows::dofile);
+ }
+ return dirh;
+ }
+
+ struct win_actual_lock_file : public actual_lock_file
+ {
+ HANDLE h;
+ OVERLAPPED ol;
+ static BOOST_CONSTEXPR_OR_CONST off_t magicbyte=(1ULL<<62)-1;
+ static bool win32_lockmagicbyte(HANDLE h, DWORD flags)
+ {
+ OVERLAPPED ol={0};
+ ol.Offset=(DWORD) (magicbyte & 0xffffffff);
+ ol.OffsetHigh=(DWORD) ((magicbyte>>32) & 0xffffffff);
+ DWORD len_low=1, len_high=0;
+ return LockFileEx(h, LOCKFILE_FAIL_IMMEDIATELY|flags, 0, len_low, len_high, &ol)!=0;
+ }
+ static bool win32_unlockmagicbyte(HANDLE h)
+ {
+ OVERLAPPED ol={0};
+ ol.Offset=(DWORD) (magicbyte & 0xffffffff);
+ ol.OffsetHigh=(DWORD) ((magicbyte>>32) & 0xffffffff);
+ DWORD len_low=1, len_high=0;
+ return UnlockFileEx(h, 0, len_low, len_high, &ol)!=0;
+ }
+ win_actual_lock_file(BOOST_AFIO_V2_NAMESPACE::path p) : actual_lock_file(std::move(p)), h(nullptr)
+ {
+ memset(&ol, 0, sizeof(ol));
+ bool done=false;
+ do
+ {
+ NTSTATUS status=0;
+ for(size_t n=0; n<10; n++)
+ {
+ // TODO FIXME: Lock file needs to copy exact security descriptor from its original
+ std::tie(status, h)=ntcreatefile(handle_ptr(), lockfilepath, file_flags::create|file_flags::read_write|file_flags::temporary_file|file_flags::int_file_share_delete);
+ // This may fail with STATUS_DELETE_PENDING, if so sleep and loop
+ if(!status)
+ break;
+ else if(((NTSTATUS) 0xC0000056)/*STATUS_DELETE_PENDING*/==status)
+ this_thread::sleep_for(chrono::milliseconds(100));
+ else
+ BOOST_AFIO_ERRHNT(status);
+ }
+ BOOST_AFIO_ERRHNT(status);
+ // We can't use DeleteOnClose for Samba shares because he'll delete on close even if
+ // other processes have a handle open. We hence read lock the same final byte as POSIX considers
+ // it (i.e. 1<<62-1). If it fails then the other has a write lock and is about to delete.
+ if(!(done=win32_lockmagicbyte(h, 0)))
+ CloseHandle(h);
+ } while(!done);
+ }
+ ~win_actual_lock_file()
+ {
+ // Lock POSIX magic byte for write access. Win32 doesn't permit conversion of shared to exclusive locks
+ win32_unlockmagicbyte(h);
+ if(win32_lockmagicbyte(h, LOCKFILE_EXCLUSIVE_LOCK))
+ {
+ // If this lock succeeded then I am potentially the last with this file open
+ // so unlink myself, which will work with my open file handle as I was opened with FILE_SHARE_DELETE.
+ // All further opens will now fail with STATUS_DELETE_PENDING
+ path::string_type escapedpath(lockfilepath.filesystem_path().native());
+ BOOST_AFIO_ERRHWIN(DeleteFile(escapedpath.c_str()));
+ }
+ BOOST_AFIO_ERRHWIN(CloseHandle(h));
+ }
+ dispatcher::completion_returntype lock(size_t id, future<> op, lock_req req, void *_thislockfile) override final
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ win_lock_file *thislockfile=(win_lock_file *) _thislockfile;
+ auto completion_handler=[this, id, op, req](const error_code &ec)
+ {
+ handle_ptr h(op.get_handle());
+ if(ec)
+ {
+ exception_ptr e;
+ // boost::system::system_error makes no attempt to ask windows for what the error code means :(
+ try
+ {
+ BOOST_AFIO_ERRGWINFN(ec.value(), [h]{return h->path();});
+ }
+ catch(...)
+ {
+ e=current_exception();
+ }
+ op.parent()->complete_async_op(id, h, e);
+ }
+ else
+ {
+ op.parent()->complete_async_op(id, h);
+ }
+ };
+ // (1<<62)-1 byte used by POSIX for last use detection
+ static BOOST_CONSTEXPR_OR_CONST off_t magicbyte=(1ULL<<62)-1;
+ if(req.offset==magicbyte)
+ BOOST_AFIO_THROW(std::invalid_argument("offset cannot be (1<<62)-1"));
+ // If we straddle the magic byte then clamp to just under it
+ if(req.offset<magicbyte && req.offset+req.length>magicbyte)
+ req.length=std::min(req.offset+req.length, magicbyte)-req.offset;
+
+ NTSTATUS ntstat=0;
+ LARGE_INTEGER offset; offset.QuadPart=req.offset;
+ LARGE_INTEGER length; length.QuadPart=req.length;
+ if(req.type==lock_req::Type::unlock)
+ {
+ ntstat=NtUnlockFile(h, (PIO_STATUS_BLOCK) &ol, &offset, &length, 0);
+ //printf("UL %u ntstat=%u\n", id, ntstat);
+ }
+ else
+ {
+ ntstat=NtLockFile(h, thislockfile->ev.native_handle(), nullptr, nullptr, (PIO_STATUS_BLOCK) &ol, &offset, &length, 0,
+ false, req.type==lock_req::Type::write_lock);
+ //printf("L %u ntstat=%u\n", id, ntstat);
+ }
+ if(STATUS_PENDING!=ntstat)
+ {
+ SetWin32LastErrorFromNtStatus(ntstat);
+ error_code ec(GetLastError(), asio::error::get_system_category());
+ completion_handler(ec);
+ }
+ else
+ thislockfile->ev.async_wait(completion_handler);
+ // Indicate we're not finished yet
+ return std::make_pair(false, handle_ptr());
+ }
+ };
+} // namespace
+BOOST_AFIO_V2_NAMESPACE_END
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+#endif
diff --git a/attic/include/boost/afio/v2/detail/impl/nt_kernel_stuff.hpp b/attic/include/boost/afio/v2/detail/impl/nt_kernel_stuff.hpp
new file mode 100644
index 00000000..1c976a8a
--- /dev/null
+++ b/attic/include/boost/afio/v2/detail/impl/nt_kernel_stuff.hpp
@@ -0,0 +1,726 @@
+/* async_file_io
+Provides a threadpool and asynchronous file i/o infrastructure based on Boost.ASIO, Boost.Iostreams and filesystem
+(C) 2013 Niall Douglas http://www.nedprod.com/
+File Created: Mar 2013
+*/
+
+#ifdef WIN32
+#include <Windows.h>
+#include <winioctl.h>
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+namespace windows_nt_kernel
+{
+ // From http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/File/FILE_INFORMATION_CLASS.html
+ typedef enum _FILE_INFORMATION_CLASS {
+ FileDirectoryInformation = 1,
+ FileFullDirectoryInformation,
+ FileBothDirectoryInformation,
+ FileBasicInformation,
+ FileStandardInformation,
+ FileInternalInformation,
+ FileEaInformation,
+ FileAccessInformation,
+ FileNameInformation,
+ FileRenameInformation,
+ FileLinkInformation,
+ FileNamesInformation,
+ FileDispositionInformation,
+ FilePositionInformation,
+ FileFullEaInformation,
+ FileModeInformation,
+ FileAlignmentInformation,
+ FileAllInformation,
+ FileAllocationInformation,
+ FileEndOfFileInformation,
+ FileAlternateNameInformation,
+ FileStreamInformation,
+ FilePipeInformation,
+ FilePipeLocalInformation,
+ FilePipeRemoteInformation,
+ FileMailslotQueryInformation,
+ FileMailslotSetInformation,
+ FileCompressionInformation,
+ FileObjectIdInformation,
+ FileCompletionInformation,
+ FileMoveClusterInformation,
+ FileQuotaInformation,
+ FileReparsePointInformation,
+ FileNetworkOpenInformation,
+ FileAttributeTagInformation,
+ FileTrackingInformation,
+ FileIdBothDirectoryInformation,
+ FileIdFullDirectoryInformation,
+ FileValidDataLengthInformation,
+ FileShortNameInformation,
+ FileIoCompletionNotificationInformation,
+ FileIoStatusBlockRangeInformation,
+ FileIoPriorityHintInformation,
+ FileSfioReserveInformation,
+ FileSfioVolumeInformation,
+ FileHardLinkInformation,
+ FileProcessIdsUsingFileInformation,
+ FileNormalizedNameInformation,
+ FileNetworkPhysicalNameInformation,
+ FileIdGlobalTxDirectoryInformation,
+ FileIsRemoteDeviceInformation,
+ FileAttributeCacheInformation,
+ FileNumaNodeInformation,
+ FileStandardLinkInformation,
+ FileRemoteProtocolInformation,
+ FileMaximumInformation
+ } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
+
+ typedef enum {
+ FileFsVolumeInformation = 1,
+ FileFsLabelInformation = 2,
+ FileFsSizeInformation = 3,
+ FileFsDeviceInformation = 4,
+ FileFsAttributeInformation = 5,
+ FileFsControlInformation = 6,
+ FileFsFullSizeInformation = 7,
+ FileFsObjectIdInformation = 8,
+ FileFsDriverPathInformation = 9,
+ FileFsVolumeFlagsInformation = 10,
+ FileFsSectorSizeInformation = 11
+ } FS_INFORMATION_CLASS;
+
+ typedef enum {
+ ObjectBasicInformation = 0,
+ ObjectNameInformation = 1,
+ ObjectTypeInformation = 2
+ } OBJECT_INFORMATION_CLASS;
+
+#ifndef NTSTATUS
+#define NTSTATUS LONG
+#endif
+#ifndef STATUS_TIMEOUT
+#define STATUS_TIMEOUT ((NTSTATUS) 0x00000102)
+#endif
+#ifndef STATUS_PENDING
+#define STATUS_PENDING ((NTSTATUS) 0x00000103)
+#endif
+#ifndef STATUS_BUFFER_OVERFLOW
+#define STATUS_BUFFER_OVERFLOW ((NTSTATUS) 0x80000005)
+#endif
+
+ // From http://msdn.microsoft.com/en-us/library/windows/hardware/ff550671(v=vs.85).aspx
+ typedef struct _IO_STATUS_BLOCK {
+ union {
+ NTSTATUS Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+ } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+ // From http://msdn.microsoft.com/en-us/library/windows/desktop/aa380518(v=vs.85).aspx
+ typedef struct _LSA_UNICODE_STRING {
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+ } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
+
+ // From http://msdn.microsoft.com/en-us/library/windows/hardware/ff557749(v=vs.85).aspx
+ typedef struct _OBJECT_ATTRIBUTES {
+ ULONG Length;
+ HANDLE RootDirectory;
+ PUNICODE_STRING ObjectName;
+ ULONG Attributes;
+ PVOID SecurityDescriptor;
+ PVOID SecurityQualityOfService;
+ } OBJECT_ATTRIBUTES, *POBJECT_ATTRIBUTES;
+
+ typedef
+ VOID
+ (NTAPI *PIO_APC_ROUTINE) (
+ IN PVOID ApcContext,
+ IN PIO_STATUS_BLOCK IoStatusBlock,
+ IN ULONG Reserved
+ );
+
+ typedef struct _IMAGEHLP_LINE64 {
+ DWORD SizeOfStruct;
+ PVOID Key;
+ DWORD LineNumber;
+ PTSTR FileName;
+ DWORD64 Address;
+ } IMAGEHLP_LINE64, *PIMAGEHLP_LINE64;
+
+ // From https://msdn.microsoft.com/en-us/library/bb432383%28v=vs.85%29.aspx
+ typedef NTSTATUS (NTAPI *NtQueryObject_t)(
+ /*_In_opt_*/ HANDLE Handle,
+ /*_In_*/ OBJECT_INFORMATION_CLASS ObjectInformationClass,
+ /*_Out_opt_*/ PVOID ObjectInformation,
+ /*_In_*/ ULONG ObjectInformationLength,
+ /*_Out_opt_*/ PULONG ReturnLength
+ );
+
+ // From http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/File/NtQueryInformationFile.html
+ // and http://msdn.microsoft.com/en-us/library/windows/hardware/ff567052(v=vs.85).aspx
+ typedef NTSTATUS (NTAPI *NtQueryInformationFile_t)(
+ /*_In_*/ HANDLE FileHandle,
+ /*_Out_*/ PIO_STATUS_BLOCK IoStatusBlock,
+ /*_Out_*/ PVOID FileInformation,
+ /*_In_*/ ULONG Length,
+ /*_In_*/ FILE_INFORMATION_CLASS FileInformationClass
+ );
+
+ // From http://msdn.microsoft.com/en-us/library/windows/hardware/ff567070(v=vs.85).aspx
+ typedef NTSTATUS (NTAPI *NtQueryVolumeInformationFile_t)(
+ /*_In_*/ HANDLE FileHandle,
+ /*_Out_*/ PIO_STATUS_BLOCK IoStatusBlock,
+ /*_Out_*/ PVOID FsInformation,
+ /*_In_*/ ULONG Length,
+ /*_In_*/ FS_INFORMATION_CLASS FsInformationClass
+ );
+
+ // From http://msdn.microsoft.com/en-us/library/windows/hardware/ff566492(v=vs.85).aspx
+ typedef NTSTATUS (NTAPI *NtOpenDirectoryObject_t)(
+ /*_Out_*/ PHANDLE DirectoryHandle,
+ /*_In_*/ ACCESS_MASK DesiredAccess,
+ /*_In_*/ POBJECT_ATTRIBUTES ObjectAttributes
+ );
+
+
+ // From http://msdn.microsoft.com/en-us/library/windows/hardware/ff567011(v=vs.85).aspx
+ typedef NTSTATUS (NTAPI *NtOpenFile_t)(
+ /*_Out_*/ PHANDLE FileHandle,
+ /*_In_*/ ACCESS_MASK DesiredAccess,
+ /*_In_*/ POBJECT_ATTRIBUTES ObjectAttributes,
+ /*_Out_*/ PIO_STATUS_BLOCK IoStatusBlock,
+ /*_In_*/ ULONG ShareAccess,
+ /*_In_*/ ULONG OpenOptions
+ );
+
+ // From http://msdn.microsoft.com/en-us/library/windows/hardware/ff566424(v=vs.85).aspx
+ typedef NTSTATUS (NTAPI *NtCreateFile_t)(
+ /*_Out_*/ PHANDLE FileHandle,
+ /*_In_*/ ACCESS_MASK DesiredAccess,
+ /*_In_*/ POBJECT_ATTRIBUTES ObjectAttributes,
+ /*_Out_*/ PIO_STATUS_BLOCK IoStatusBlock,
+ /*_In_opt_*/ PLARGE_INTEGER AllocationSize,
+ /*_In_*/ ULONG FileAttributes,
+ /*_In_*/ ULONG ShareAccess,
+ /*_In_*/ ULONG CreateDisposition,
+ /*_In_*/ ULONG CreateOptions,
+ /*_In_opt_*/ PVOID EaBuffer,
+ /*_In_*/ ULONG EaLength
+ );
+
+ typedef NTSTATUS (NTAPI *NtDeleteFile_t)(
+ /*_In_*/ POBJECT_ATTRIBUTES ObjectAttributes
+ );
+
+ typedef NTSTATUS(NTAPI *NtClose_t)(
+ /*_Out_*/ HANDLE FileHandle
+ );
+
+ // From http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/File/NtQueryDirectoryFile.html
+ // and http://msdn.microsoft.com/en-us/library/windows/hardware/ff567047(v=vs.85).aspx
+ typedef NTSTATUS (NTAPI *NtQueryDirectoryFile_t)(
+ /*_In_*/ HANDLE FileHandle,
+ /*_In_opt_*/ HANDLE Event,
+ /*_In_opt_*/ PIO_APC_ROUTINE ApcRoutine,
+ /*_In_opt_*/ PVOID ApcContext,
+ /*_Out_*/ PIO_STATUS_BLOCK IoStatusBlock,
+ /*_Out_*/ PVOID FileInformation,
+ /*_In_*/ ULONG Length,
+ /*_In_*/ FILE_INFORMATION_CLASS FileInformationClass,
+ /*_In_*/ BOOLEAN ReturnSingleEntry,
+ /*_In_opt_*/ PUNICODE_STRING FileName,
+ /*_In_*/ BOOLEAN RestartScan
+ );
+
+ // From http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/File/NtSetInformationFile.html
+ // and http://msdn.microsoft.com/en-us/library/windows/hardware/ff567096(v=vs.85).aspx
+ typedef NTSTATUS (NTAPI *NtSetInformationFile_t)(
+ /*_In_*/ HANDLE FileHandle,
+ /*_Out_*/ PIO_STATUS_BLOCK IoStatusBlock,
+ /*_In_*/ PVOID FileInformation,
+ /*_In_*/ ULONG Length,
+ /*_In_*/ FILE_INFORMATION_CLASS FileInformationClass
+ );
+
+ // From http://msdn.microsoft.com/en-us/library/ms648412(v=vs.85).aspx
+ typedef NTSTATUS (NTAPI *NtWaitForSingleObject_t)(
+ /*_In_*/ HANDLE Handle,
+ /*_In_*/ BOOLEAN Alertable,
+ /*_In_*/ PLARGE_INTEGER Timeout
+ );
+
+ // From https://msdn.microsoft.com/en-us/library/windows/hardware/ff566474(v=vs.85).aspx
+ typedef NTSTATUS (NTAPI *NtLockFile_t)(
+ /*_In_*/ HANDLE FileHandle,
+ /*_In_opt_*/ HANDLE Event,
+ /*_In_opt_*/ PIO_APC_ROUTINE ApcRoutine,
+ /*_In_opt_*/ PVOID ApcContext,
+ /*_Out_*/ PIO_STATUS_BLOCK IoStatusBlock,
+ /*_In_*/ PLARGE_INTEGER ByteOffset,
+ /*_In_*/ PLARGE_INTEGER Length,
+ /*_In_*/ ULONG Key,
+ /*_In_*/ BOOLEAN FailImmediately,
+ /*_In_*/ BOOLEAN ExclusiveLock
+ );
+
+ // From https://msdn.microsoft.com/en-us/library/windows/hardware/ff567118(v=vs.85).aspx
+ typedef NTSTATUS (NTAPI *NtUnlockFile_t)(
+ /*_In_*/ HANDLE FileHandle,
+ /*_Out_*/ PIO_STATUS_BLOCK IoStatusBlock,
+ /*_In_*/ PLARGE_INTEGER ByteOffset,
+ /*_In_*/ PLARGE_INTEGER Length,
+ /*_In_*/ ULONG Key
+ );
+
+ typedef BOOLEAN (NTAPI *RtlGenRandom_t)(
+ /*_Out_*/ PVOID RandomBuffer,
+ /*_In_*/ ULONG RandomBufferLength
+ );
+
+ typedef BOOL (WINAPI *OpenProcessToken_t)(
+ /*_In_*/ HANDLE ProcessHandle,
+ /*_In_*/ DWORD DesiredAccess,
+ /*_Out_*/ PHANDLE TokenHandle
+ );
+
+ typedef BOOL (WINAPI *LookupPrivilegeValue_t)(
+ /*_In_opt_*/ LPCTSTR lpSystemName,
+ /*_In_*/ LPCTSTR lpName,
+ /*_Out_*/ PLUID lpLuid
+ );
+
+ typedef BOOL (WINAPI *AdjustTokenPrivileges_t)(
+ /*_In_*/ HANDLE TokenHandle,
+ /*_In_*/ BOOL DisableAllPrivileges,
+ /*_In_opt_*/ PTOKEN_PRIVILEGES NewState,
+ /*_In_*/ DWORD BufferLength,
+ /*_Out_opt_*/ PTOKEN_PRIVILEGES PreviousState,
+ /*_Out_opt_*/ PDWORD ReturnLength
+ );
+
+ typedef USHORT (WINAPI *RtlCaptureStackBackTrace_t)(
+ /*_In_*/ ULONG FramesToSkip,
+ /*_In_*/ ULONG FramesToCapture,
+ /*_Out_*/ PVOID *BackTrace,
+ /*_Out_opt_*/ PULONG BackTraceHash
+ );
+
+ typedef BOOL (WINAPI *SymInitialize_t)(
+ /*_In_*/ HANDLE hProcess,
+ /*_In_opt_*/ PCTSTR UserSearchPath,
+ /*_In_*/ BOOL fInvadeProcess
+ );
+
+ typedef BOOL (WINAPI *SymGetLineFromAddr64_t)(
+ /*_In_*/ HANDLE hProcess,
+ /*_In_*/ DWORD64 dwAddr,
+ /*_Out_*/ PDWORD pdwDisplacement,
+ /*_Out_*/ PIMAGEHLP_LINE64 Line
+ );
+
+ typedef struct _FILE_BASIC_INFORMATION {
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ ULONG FileAttributes;
+ } FILE_BASIC_INFORMATION, *PFILE_BASIC_INFORMATION;
+
+ typedef struct _FILE_STANDARD_INFORMATION {
+ LARGE_INTEGER AllocationSize;
+ LARGE_INTEGER EndOfFile;
+ ULONG NumberOfLinks;
+ BOOLEAN DeletePending;
+ BOOLEAN Directory;
+ } FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
+
+ typedef struct _FILE_INTERNAL_INFORMATION {
+ LARGE_INTEGER IndexNumber;
+ } FILE_INTERNAL_INFORMATION, *PFILE_INTERNAL_INFORMATION;
+
+ typedef struct _FILE_EA_INFORMATION {
+ union {
+ ULONG EaSize;
+ ULONG ReparsePointTag;
+ };
+ } FILE_EA_INFORMATION, *PFILE_EA_INFORMATION;
+
+ typedef struct _FILE_ACCESS_INFORMATION {
+ ACCESS_MASK AccessFlags;
+ } FILE_ACCESS_INFORMATION, *PFILE_ACCESS_INFORMATION;
+
+ typedef struct _FILE_POSITION_INFORMATION {
+ LARGE_INTEGER CurrentByteOffset;
+ } FILE_POSITION_INFORMATION, *PFILE_POSITION_INFORMATION;
+
+ typedef struct _FILE_MODE_INFORMATION {
+ ULONG Mode;
+ } FILE_MODE_INFORMATION, *PFILE_MODE_INFORMATION;
+
+ typedef struct _FILE_ALIGNMENT_INFORMATION {
+ ULONG AlignmentRequirement;
+ } FILE_ALIGNMENT_INFORMATION, *PFILE_ALIGNMENT_INFORMATION;
+
+ typedef struct _FILE_NAME_INFORMATION {
+ ULONG FileNameLength;
+ WCHAR FileName[1];
+ } FILE_NAME_INFORMATION, *PFILE_NAME_INFORMATION;
+
+ typedef struct _FILE_RENAME_INFORMATION {
+ BOOLEAN ReplaceIfExists;
+ HANDLE RootDirectory;
+ ULONG FileNameLength;
+ WCHAR FileName[1];
+ } FILE_RENAME_INFORMATION, *PFILE_RENAME_INFORMATION;
+
+ typedef struct _FILE_LINK_INFORMATION {
+ BOOLEAN ReplaceIfExists;
+ HANDLE RootDirectory;
+ ULONG FileNameLength;
+ WCHAR FileName[1];
+ } FILE_LINK_INFORMATION, *PFILE_LINK_INFORMATION;
+
+ typedef struct _FILE_DISPOSITION_INFORMATION {
+ BOOLEAN DeleteFile;
+ } FILE_DISPOSITION_INFORMATION, *PFILE_DISPOSITION_INFORMATION;
+
+ typedef struct _FILE_ALL_INFORMATION {
+ FILE_BASIC_INFORMATION BasicInformation;
+ FILE_STANDARD_INFORMATION StandardInformation;
+ FILE_INTERNAL_INFORMATION InternalInformation;
+ FILE_EA_INFORMATION EaInformation;
+ FILE_ACCESS_INFORMATION AccessInformation;
+ FILE_POSITION_INFORMATION PositionInformation;
+ FILE_MODE_INFORMATION ModeInformation;
+ FILE_ALIGNMENT_INFORMATION AlignmentInformation;
+ FILE_NAME_INFORMATION NameInformation;
+ } FILE_ALL_INFORMATION, *PFILE_ALL_INFORMATION;
+
+ typedef struct _FILE_FS_ATTRIBUTE_INFORMATION {
+ ULONG FileSystemAttributes;
+ LONG MaximumComponentNameLength;
+ ULONG FileSystemNameLength;
+ WCHAR FileSystemName[1];
+ } FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION;
+
+ typedef struct _FILE_FS_FULL_SIZE_INFORMATION {
+ LARGE_INTEGER TotalAllocationUnits;
+ LARGE_INTEGER CallerAvailableAllocationUnits;
+ LARGE_INTEGER ActualAvailableAllocationUnits;
+ ULONG SectorsPerAllocationUnit;
+ ULONG BytesPerSector;
+ } FILE_FS_FULL_SIZE_INFORMATION, *PFILE_FS_FULL_SIZE_INFORMATION;
+
+ typedef struct _FILE_FS_OBJECTID_INFORMATION {
+ UCHAR ObjectId[16];
+ UCHAR ExtendedInfo[48];
+ } FILE_FS_OBJECTID_INFORMATION, *PFILE_FS_OBJECTID_INFORMATION;
+
+ typedef struct _FILE_FS_SECTOR_SIZE_INFORMATION {
+ ULONG LogicalBytesPerSector;
+ ULONG PhysicalBytesPerSectorForAtomicity;
+ ULONG PhysicalBytesPerSectorForPerformance;
+ ULONG FileSystemEffectivePhysicalBytesPerSectorForAtomicity;
+ ULONG Flags;
+ ULONG ByteOffsetForSectorAlignment;
+ ULONG ByteOffsetForPartitionAlignment;
+ } FILE_FS_SECTOR_SIZE_INFORMATION, *PFILE_FS_SECTOR_SIZE_INFORMATION;
+
+ // From http://msdn.microsoft.com/en-us/library/windows/hardware/ff540310(v=vs.85).aspx
+ typedef struct _FILE_ID_FULL_DIR_INFORMATION {
+ ULONG NextEntryOffset;
+ ULONG FileIndex;
+ LARGE_INTEGER CreationTime;
+ LARGE_INTEGER LastAccessTime;
+ LARGE_INTEGER LastWriteTime;
+ LARGE_INTEGER ChangeTime;
+ LARGE_INTEGER EndOfFile;
+ LARGE_INTEGER AllocationSize;
+ ULONG FileAttributes;
+ ULONG FileNameLength;
+ union {
+ ULONG EaSize;
+ ULONG ReparsePointTag;
+ };
+ LARGE_INTEGER FileId;
+ WCHAR FileName[1];
+ } FILE_ID_FULL_DIR_INFORMATION, *PFILE_ID_FULL_DIR_INFORMATION;
+
+ // From https://msdn.microsoft.com/en-us/library/windows/hardware/ff540354(v=vs.85).aspx
+ typedef struct _FILE_REPARSE_POINT_INFORMATION {
+ LARGE_INTEGER FileReference;
+ ULONG Tag;
+ } FILE_REPARSE_POINT_INFORMATION, *PFILE_REPARSE_POINT_INFORMATION;
+
+ // From http://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
+ typedef struct _REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+ } REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
+
+ static NtQueryObject_t NtQueryObject;
+ static NtQueryInformationFile_t NtQueryInformationFile;
+ static NtQueryVolumeInformationFile_t NtQueryVolumeInformationFile;
+ static NtOpenDirectoryObject_t NtOpenDirectoryObject;
+ static NtOpenFile_t NtOpenFile;
+ static NtCreateFile_t NtCreateFile;
+ static NtDeleteFile_t NtDeleteFile;
+ static NtClose_t NtClose;
+ static NtQueryDirectoryFile_t NtQueryDirectoryFile;
+ static NtSetInformationFile_t NtSetInformationFile;
+ static NtWaitForSingleObject_t NtWaitForSingleObject;
+ static NtLockFile_t NtLockFile;
+ static NtUnlockFile_t NtUnlockFile;
+ static RtlGenRandom_t RtlGenRandom;
+ static OpenProcessToken_t OpenProcessToken;
+ static LookupPrivilegeValue_t LookupPrivilegeValue;
+ static AdjustTokenPrivileges_t AdjustTokenPrivileges;
+ static SymInitialize_t SymInitialize;
+ static SymGetLineFromAddr64_t SymGetLineFromAddr64;
+ static RtlCaptureStackBackTrace_t RtlCaptureStackBackTrace;
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 6387) // MSVC sanitiser warns that GetModuleHandleA() might fail (hah!)
+#endif
+ static inline void doinit()
+ {
+ if(RtlCaptureStackBackTrace)
+ return;
+ static spinlock<bool> lock;
+ lock_guard<decltype(lock)> g(lock);
+ if(!NtQueryObject)
+ if(!(NtQueryObject=(NtQueryObject_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtQueryObject")))
+ abort();
+ if(!NtQueryInformationFile)
+ if(!(NtQueryInformationFile=(NtQueryInformationFile_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtQueryInformationFile")))
+ abort();
+ if(!NtQueryVolumeInformationFile)
+ if(!(NtQueryVolumeInformationFile=(NtQueryVolumeInformationFile_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtQueryVolumeInformationFile")))
+ abort();
+ if(!NtOpenDirectoryObject)
+ if(!(NtOpenDirectoryObject=(NtOpenDirectoryObject_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtOpenDirectoryObject")))
+ abort();
+ if(!NtOpenFile)
+ if(!(NtOpenFile=(NtOpenFile_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtOpenFile")))
+ abort();
+ if(!NtCreateFile)
+ if(!(NtCreateFile=(NtCreateFile_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtCreateFile")))
+ abort();
+ if(!NtDeleteFile)
+ if(!(NtDeleteFile =(NtDeleteFile_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtDeleteFile")))
+ abort();
+ if(!NtClose)
+ if(!(NtClose=(NtClose_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtClose")))
+ abort();
+ if(!NtQueryDirectoryFile)
+ if(!(NtQueryDirectoryFile=(NtQueryDirectoryFile_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtQueryDirectoryFile")))
+ abort();
+ if(!NtSetInformationFile)
+ if(!(NtSetInformationFile=(NtSetInformationFile_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtSetInformationFile")))
+ abort();
+ if(!NtWaitForSingleObject)
+ if(!(NtWaitForSingleObject=(NtWaitForSingleObject_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtWaitForSingleObject")))
+ abort();
+ if(!NtLockFile)
+ if(!(NtLockFile=(NtLockFile_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtLockFile")))
+ abort();
+ if(!NtUnlockFile)
+ if(!(NtUnlockFile=(NtUnlockFile_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "NtUnlockFile")))
+ abort();
+ HMODULE advapi32=LoadLibraryA("ADVAPI32.DLL");
+ if(!RtlGenRandom)
+ if(!(RtlGenRandom=(RtlGenRandom_t) GetProcAddress(advapi32, "SystemFunction036")))
+ abort();
+ if(!OpenProcessToken)
+ if(!(OpenProcessToken=(OpenProcessToken_t) GetProcAddress(advapi32, "OpenProcessToken")))
+ abort();
+ if(!LookupPrivilegeValue)
+ if(!(LookupPrivilegeValue=(LookupPrivilegeValue_t) GetProcAddress(advapi32, "LookupPrivilegeValueW")))
+ abort();
+ if(!AdjustTokenPrivileges)
+ if(!(AdjustTokenPrivileges=(AdjustTokenPrivileges_t) GetProcAddress(advapi32, "AdjustTokenPrivileges")))
+ abort();
+ HMODULE dbghelp = LoadLibraryA("DBGHELP.DLL");
+#ifdef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+ if (dbghelp)
+ {
+ if (!(SymInitialize = (SymInitialize_t) GetProcAddress(dbghelp, "SymInitializeW")))
+ abort();
+ if(!SymInitialize(GetCurrentProcess(), nullptr, true))
+ abort();
+ if (!(SymGetLineFromAddr64 = (SymGetLineFromAddr64_t) GetProcAddress(dbghelp, "SymGetLineFromAddrW64")))
+ abort();
+ }
+#endif
+ if (!RtlCaptureStackBackTrace)
+ if (!(RtlCaptureStackBackTrace = (RtlCaptureStackBackTrace_t) GetProcAddress(GetModuleHandleA("NTDLL.DLL"), "RtlCaptureStackBackTrace")))
+ abort();
+ // MAKE SURE you update the early exit check at the top to whatever the last of these is!
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ static inline void init()
+ {
+ static bool initialised=false;
+ if(!initialised)
+ {
+ doinit();
+ initialised=true;
+ }
+ }
+
+ static inline filesystem::file_type to_st_type(ULONG FileAttributes, ULONG ReparsePointTag)
+ {
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ if(FileAttributes&FILE_ATTRIBUTE_REPARSE_POINT && (ReparsePointTag == IO_REPARSE_TAG_MOUNT_POINT || ReparsePointTag == IO_REPARSE_TAG_SYMLINK))
+ return filesystem::file_type::symlink_file;
+ //return filesystem::file_type::reparse_file;
+ else if(FileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ return filesystem::file_type::directory_file;
+ else
+ return filesystem::file_type::regular_file;
+#else
+ if(FileAttributes&FILE_ATTRIBUTE_REPARSE_POINT && (ReparsePointTag == IO_REPARSE_TAG_MOUNT_POINT || ReparsePointTag == IO_REPARSE_TAG_SYMLINK))
+ return filesystem::file_type::symlink;
+ //return filesystem::file_type::reparse_file;
+ else if(FileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ return filesystem::file_type::directory;
+ else
+ return filesystem::file_type::regular;
+#endif
+ }
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 6326) // comparison of constants
+#endif
+ static inline chrono::system_clock::time_point to_timepoint(LARGE_INTEGER time)
+ {
+ // We make the big assumption that the STL's system_clock is based on the time_t epoch 1st Jan 1970.
+ static BOOST_CONSTEXPR_OR_CONST unsigned long long FILETIME_OFFSET_TO_1970=((27111902ULL << 32U) + 3577643008ULL);
+ // Need to have this self-adapt to the STL being used
+ static BOOST_CONSTEXPR_OR_CONST unsigned long long STL_TICKS_PER_SEC=(unsigned long long) chrono::system_clock::period::den/chrono::system_clock::period::num;
+ static BOOST_CONSTEXPR_OR_CONST unsigned long long multiplier=STL_TICKS_PER_SEC>=10000000ULL ? STL_TICKS_PER_SEC/10000000ULL : 1;
+ static BOOST_CONSTEXPR_OR_CONST unsigned long long divider=STL_TICKS_PER_SEC>=10000000ULL ? 1 : 10000000ULL/STL_TICKS_PER_SEC;
+
+ unsigned long long ticks_since_1970=(time.QuadPart - FILETIME_OFFSET_TO_1970); // In 100ns increments
+ chrono::system_clock::duration duration(ticks_since_1970*multiplier/divider);
+ return chrono::system_clock::time_point(duration);
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ // Adapted from http://www.cprogramming.com/snippets/source-code/convert-ntstatus-win32-error
+ // Could use RtlNtStatusToDosError() instead
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 6387) // MSVC sanitiser warns on misuse of GetOverlappedResult
+#endif
+ static inline void SetWin32LastErrorFromNtStatus(NTSTATUS ntstatus)
+ {
+ DWORD br;
+ OVERLAPPED o;
+
+ o.Internal = ntstatus;
+ o.InternalHigh = 0;
+ o.Offset = 0;
+ o.OffsetHigh = 0;
+ o.hEvent = 0;
+ GetOverlappedResult(NULL, &o, &br, FALSE);
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ // C++ error code from GetLastError()
+ static inline error_code make_error_code()
+ {
+ return error_code((int) GetLastError(), system_category());
+ }
+
+ // C++ error code from NTSTATUS
+ static inline error_code make_error_code(NTSTATUS ntstat)
+ {
+ SetWin32LastErrorFromNtStatus(ntstat);
+ return error_code((int)GetLastError(), system_category());
+ }
+} // namespace
+
+// WinVista and later have the SetFileInformationByHandle() function, but for WinXP
+// compatibility we use the kernel syscall directly
+static inline bool wintruncate(HANDLE h, off_t newsize)
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ IO_STATUS_BLOCK isb={ 0 };
+ BOOST_AFIO_ERRHNT(NtSetInformationFile(h, &isb, &newsize, sizeof(newsize), FileEndOfFileInformation));
+ return true;
+}
+static inline int winftruncate(int fd, off_t _newsize)
+{
+#if 1
+ return wintruncate((HANDLE) _get_osfhandle(fd), _newsize) ? 0 : -1;
+#else
+ // This is a bit tricky ... overlapped files ignore their file position except in this one
+ // case, but clearly here we have a race condition. No choice but to rinse and repeat I guess.
+ LARGE_INTEGER size={0}, newsize={0};
+ HANDLE h=(HANDLE) _get_osfhandle(fd);
+ newsize.QuadPart=_newsize;
+ while(size.QuadPart!=newsize.QuadPart)
+ {
+ BOOST_AFIO_ERRHWIN(SetFilePointerEx(h, newsize, NULL, FILE_BEGIN));
+ BOOST_AFIO_ERRHWIN(SetEndOfFile(h));
+ BOOST_AFIO_ERRHWIN(GetFileSizeEx(h, &size));
+ }
+#endif
+}
+static inline void fill_stat_t(stat_t &stat, BOOST_AFIO_POSIX_STAT_STRUCT s, metadata_flags wanted)
+{
+ using namespace boost::afio;
+#ifndef WIN32
+ if(!!(wanted&metadata_flags::dev)) { stat.st_dev=s.st_dev; }
+#endif
+ if(!!(wanted&metadata_flags::ino)) { stat.st_ino=s.st_ino; }
+ if(!!(wanted&metadata_flags::type)) { stat.st_type=to_st_type(s.st_mode); }
+#ifndef WIN32
+ if(!!(wanted&metadata_flags::perms)) { stat.st_mode=s.st_perms; }
+#endif
+ if(!!(wanted&metadata_flags::nlink)) { stat.st_nlink=s.st_nlink; }
+#ifndef WIN32
+ if(!!(wanted&metadata_flags::uid)) { stat.st_uid=s.st_uid; }
+ if(!!(wanted&metadata_flags::gid)) { stat.st_gid=s.st_gid; }
+ if(!!(wanted&metadata_flags::rdev)) { stat.st_rdev=s.st_rdev; }
+#endif
+ if(!!(wanted&metadata_flags::atim)) { stat.st_atim=chrono::system_clock::from_time_t(s.st_atime); }
+ if(!!(wanted&metadata_flags::mtim)) { stat.st_mtim=chrono::system_clock::from_time_t(s.st_mtime); }
+ if(!!(wanted&metadata_flags::birthtim)) { stat.st_birthtim=chrono::system_clock::from_time_t(s.st_ctime); }
+ if(!!(wanted&metadata_flags::size)) { stat.st_size=s.st_size; }
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#endif
diff --git a/attic/include/boost/afio/v2/detail/valgrind/helgrind.h b/attic/include/boost/afio/v2/detail/valgrind/helgrind.h
new file mode 100644
index 00000000..f777c7b8
--- /dev/null
+++ b/attic/include/boost/afio/v2/detail/valgrind/helgrind.h
@@ -0,0 +1,739 @@
+/*
+ ----------------------------------------------------------------
+
+ Notice that the above BSD-style license applies to this one file
+ (helgrind.h) only. The entire rest of Valgrind is licensed under
+ the terms of the GNU General Public License, version 2. See the
+ COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+
+ This file is part of Helgrind, a Valgrind tool for detecting errors
+ in threaded programs.
+
+ Copyright (C) 2007-2013 OpenWorks LLP
+ info@open-works.co.uk
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ----------------------------------------------------------------
+
+ Notice that the above BSD-style license applies to this one file
+ (helgrind.h) only. The entire rest of Valgrind is licensed under
+ the terms of the GNU General Public License, version 2. See the
+ COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+*/
+
+#ifndef __HELGRIND_H
+#define __HELGRIND_H
+
+#include "valgrind.h"
+
+/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
+ This enum comprises an ABI exported by Valgrind to programs
+ which use client requests. DO NOT CHANGE THE ORDER OF THESE
+ ENTRIES, NOR DELETE ANY -- add new ones at the end. */
+typedef
+ enum {
+ VG_USERREQ__HG_CLEAN_MEMORY = VG_USERREQ_TOOL_BASE('H','G'),
+
+ /* The rest are for Helgrind's internal use. Not for end-user
+ use. Do not use them unless you are a Valgrind developer. */
+
+ /* Notify the tool what this thread's pthread_t is. */
+ _VG_USERREQ__HG_SET_MY_PTHREAD_T = VG_USERREQ_TOOL_BASE('H','G')
+ + 256,
+ _VG_USERREQ__HG_PTH_API_ERROR, /* char*, int */
+ _VG_USERREQ__HG_PTHREAD_JOIN_POST, /* pthread_t of quitter */
+ _VG_USERREQ__HG_PTHREAD_MUTEX_INIT_POST, /* pth_mx_t*, long mbRec */
+ _VG_USERREQ__HG_PTHREAD_MUTEX_DESTROY_PRE, /* pth_mx_t*, long isInit */
+ _VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE, /* pth_mx_t* */
+ _VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_POST, /* pth_mx_t* */
+ _VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE, /* pth_mx_t*, long isTryLock */
+ _VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST, /* pth_mx_t* */
+ _VG_USERREQ__HG_PTHREAD_COND_SIGNAL_PRE, /* pth_cond_t* */
+ _VG_USERREQ__HG_PTHREAD_COND_BROADCAST_PRE, /* pth_cond_t* */
+ _VG_USERREQ__HG_PTHREAD_COND_WAIT_PRE, /* pth_cond_t*, pth_mx_t* */
+ _VG_USERREQ__HG_PTHREAD_COND_WAIT_POST, /* pth_cond_t*, pth_mx_t* */
+ _VG_USERREQ__HG_PTHREAD_COND_DESTROY_PRE, /* pth_cond_t*, long isInit */
+ _VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST, /* pth_rwlk_t* */
+ _VG_USERREQ__HG_PTHREAD_RWLOCK_DESTROY_PRE, /* pth_rwlk_t* */
+ _VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_PRE, /* pth_rwlk_t*, long isW */
+ _VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST, /* pth_rwlk_t*, long isW */
+ _VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_PRE, /* pth_rwlk_t* */
+ _VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_POST, /* pth_rwlk_t* */
+ _VG_USERREQ__HG_POSIX_SEM_INIT_POST, /* sem_t*, ulong value */
+ _VG_USERREQ__HG_POSIX_SEM_DESTROY_PRE, /* sem_t* */
+ _VG_USERREQ__HG_POSIX_SEM_POST_PRE, /* sem_t* */
+ _VG_USERREQ__HG_POSIX_SEM_WAIT_POST, /* sem_t* */
+ _VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, /* pth_bar_t*, ulong, ulong */
+ _VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE, /* pth_bar_t* */
+ _VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE, /* pth_bar_t* */
+ _VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_PRE, /* pth_slk_t* */
+ _VG_USERREQ__HG_PTHREAD_SPIN_INIT_OR_UNLOCK_POST, /* pth_slk_t* */
+ _VG_USERREQ__HG_PTHREAD_SPIN_LOCK_PRE, /* pth_slk_t* */
+ _VG_USERREQ__HG_PTHREAD_SPIN_LOCK_POST, /* pth_slk_t* */
+ _VG_USERREQ__HG_PTHREAD_SPIN_DESTROY_PRE, /* pth_slk_t* */
+ _VG_USERREQ__HG_CLIENTREQ_UNIMP, /* char* */
+ _VG_USERREQ__HG_USERSO_SEND_PRE, /* arbitrary UWord SO-tag */
+ _VG_USERREQ__HG_USERSO_RECV_POST, /* arbitrary UWord SO-tag */
+ _VG_USERREQ__HG_USERSO_FORGET_ALL, /* arbitrary UWord SO-tag */
+ _VG_USERREQ__HG_RESERVED2, /* Do not use */
+ _VG_USERREQ__HG_RESERVED3, /* Do not use */
+ _VG_USERREQ__HG_RESERVED4, /* Do not use */
+ _VG_USERREQ__HG_ARANGE_MAKE_UNTRACKED, /* Addr a, ulong len */
+ _VG_USERREQ__HG_ARANGE_MAKE_TRACKED, /* Addr a, ulong len */
+ _VG_USERREQ__HG_PTHREAD_BARRIER_RESIZE_PRE, /* pth_bar_t*, ulong */
+ _VG_USERREQ__HG_CLEAN_MEMORY_HEAPBLOCK, /* Addr start_of_block */
+ _VG_USERREQ__HG_PTHREAD_COND_INIT_POST /* pth_cond_t*, pth_cond_attr_t*/
+
+ } Vg_TCheckClientRequest;
+
+
+/*----------------------------------------------------------------*/
+/*--- ---*/
+/*--- Implementation-only facilities. Not for end-user use. ---*/
+/*--- For end-user facilities see below (the next section in ---*/
+/*--- this file.) ---*/
+/*--- ---*/
+/*----------------------------------------------------------------*/
+
+/* Do a client request. These are macros rather than a functions so
+ as to avoid having an extra frame in stack traces.
+
+ NB: these duplicate definitions in hg_intercepts.c. But here, we
+ have to make do with weaker typing (no definition of Word etc) and
+ no assertions, whereas in helgrind.h we can use those facilities.
+ Obviously it's important the two sets of definitions are kept in
+ sync.
+
+ The commented-out asserts should actually hold, but unfortunately
+ they can't be allowed to be visible here, because that would
+ require the end-user code to #include <assert.h>.
+*/
+
+#define DO_CREQ_v_W(_creqF, _ty1F,_arg1F) \
+ do { \
+ long int _arg1; \
+ /* assert(sizeof(_ty1F) == sizeof(long int)); */ \
+ _arg1 = (long int)(_arg1F); \
+ VALGRIND_DO_CLIENT_REQUEST_STMT( \
+ (_creqF), \
+ _arg1, 0,0,0,0); \
+ } while (0)
+
+#define DO_CREQ_W_W(_resF, _dfltF, _creqF, _ty1F,_arg1F) \
+ do { \
+ long int arg1; \
+ /* assert(sizeof(_ty1F) == sizeof(long int)); */ \
+ _arg1 = (long int)(_arg1F); \
+ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ (_dfltF), \
+ (_creqF), \
+ _arg1, 0,0,0,0); \
+ _resF = _qzz_res; \
+ } while (0)
+
+#define DO_CREQ_v_WW(_creqF, _ty1F,_arg1F, _ty2F,_arg2F) \
+ do { \
+ long int _arg1, _arg2; \
+ /* assert(sizeof(_ty1F) == sizeof(long int)); */ \
+ /* assert(sizeof(_ty2F) == sizeof(long int)); */ \
+ _arg1 = (long int)(_arg1F); \
+ _arg2 = (long int)(_arg2F); \
+ VALGRIND_DO_CLIENT_REQUEST_STMT( \
+ (_creqF), \
+ _arg1,_arg2,0,0,0); \
+ } while (0)
+
+#define DO_CREQ_v_WWW(_creqF, _ty1F,_arg1F, \
+ _ty2F,_arg2F, _ty3F, _arg3F) \
+ do { \
+ long int _arg1, _arg2, _arg3; \
+ /* assert(sizeof(_ty1F) == sizeof(long int)); */ \
+ /* assert(sizeof(_ty2F) == sizeof(long int)); */ \
+ /* assert(sizeof(_ty3F) == sizeof(long int)); */ \
+ _arg1 = (long int)(_arg1F); \
+ _arg2 = (long int)(_arg2F); \
+ _arg3 = (long int)(_arg3F); \
+ VALGRIND_DO_CLIENT_REQUEST_STMT( \
+ (_creqF), \
+ _arg1,_arg2,_arg3,0,0); \
+ } while (0)
+
+
+#define _HG_CLIENTREQ_UNIMP(_qzz_str) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_CLIENTREQ_UNIMP, \
+ (char*),(_qzz_str))
+
+
+/*----------------------------------------------------------------*/
+/*--- ---*/
+/*--- Helgrind-native requests. These allow access to ---*/
+/*--- the same set of annotation primitives that are used ---*/
+/*--- to build the POSIX pthread wrappers. ---*/
+/*--- ---*/
+/*----------------------------------------------------------------*/
+
+/* ----------------------------------------------------------
+ For describing ordinary mutexes (non-rwlocks). For rwlock
+ descriptions see ANNOTATE_RWLOCK_* below.
+ ---------------------------------------------------------- */
+
+/* Notify here immediately after mutex creation. _mbRec == 0 for a
+ non-recursive mutex, 1 for a recursive mutex. */
+#define VALGRIND_HG_MUTEX_INIT_POST(_mutex, _mbRec) \
+ DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_INIT_POST, \
+ void*,(_mutex), long,(_mbRec))
+
+/* Notify here immediately before mutex acquisition. _isTryLock == 0
+ for a normal acquisition, 1 for a "try" style acquisition. */
+#define VALGRIND_HG_MUTEX_LOCK_PRE(_mutex, _isTryLock) \
+ DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_PRE, \
+ void*,(_mutex), long,(_isTryLock))
+
+/* Notify here immediately after a successful mutex acquisition. */
+#define VALGRIND_HG_MUTEX_LOCK_POST(_mutex) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_LOCK_POST, \
+ void*,(_mutex))
+
+/* Notify here immediately before a mutex release. */
+#define VALGRIND_HG_MUTEX_UNLOCK_PRE(_mutex) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_PRE, \
+ void*,(_mutex))
+
+/* Notify here immediately after a mutex release. */
+#define VALGRIND_HG_MUTEX_UNLOCK_POST(_mutex) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_UNLOCK_POST, \
+ void*,(_mutex))
+
+/* Notify here immediately before mutex destruction. */
+#define VALGRIND_HG_MUTEX_DESTROY_PRE(_mutex) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_MUTEX_DESTROY_PRE, \
+ void*,(_mutex))
+
+/* ----------------------------------------------------------
+ For describing semaphores.
+ ---------------------------------------------------------- */
+
+/* Notify here immediately after semaphore creation. */
+#define VALGRIND_HG_SEM_INIT_POST(_sem, _value) \
+ DO_CREQ_v_WW(_VG_USERREQ__HG_POSIX_SEM_INIT_POST, \
+ void*, (_sem), unsigned long, (_value))
+
+/* Notify here immediately after a semaphore wait (an acquire-style
+ operation) */
+#define VALGRIND_HG_SEM_WAIT_POST(_sem) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_WAIT_POST, \
+ void*,(_sem))
+
+/* Notify here immediately before semaphore post (a release-style
+ operation) */
+#define VALGRIND_HG_SEM_POST_PRE(_sem) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_POST_PRE, \
+ void*,(_sem))
+
+/* Notify here immediately before semaphore destruction. */
+#define VALGRIND_HG_SEM_DESTROY_PRE(_sem) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_POSIX_SEM_DESTROY_PRE, \
+ void*, (_sem))
+
+/* ----------------------------------------------------------
+ For describing barriers.
+ ---------------------------------------------------------- */
+
+/* Notify here immediately before barrier creation. _count is the
+ capacity. _resizable == 0 means the barrier may not be resized, 1
+ means it may be. */
+#define VALGRIND_HG_BARRIER_INIT_PRE(_bar, _count, _resizable) \
+ DO_CREQ_v_WWW(_VG_USERREQ__HG_PTHREAD_BARRIER_INIT_PRE, \
+ void*,(_bar), \
+ unsigned long,(_count), \
+ unsigned long,(_resizable))
+
+/* Notify here immediately before arrival at a barrier. */
+#define VALGRIND_HG_BARRIER_WAIT_PRE(_bar) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_BARRIER_WAIT_PRE, \
+ void*,(_bar))
+
+/* Notify here immediately before a resize (change of barrier
+ capacity). If _newcount >= the existing capacity, then there is no
+ change in the state of any threads waiting at the barrier. If
+ _newcount < the existing capacity, and >= _newcount threads are
+ currently waiting at the barrier, then this notification is
+ considered to also have the effect of telling the checker that all
+ waiting threads have now moved past the barrier. (I can't think of
+ any other sane semantics.) */
+#define VALGRIND_HG_BARRIER_RESIZE_PRE(_bar, _newcount) \
+ DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_BARRIER_RESIZE_PRE, \
+ void*,(_bar), \
+ unsigned long,(_newcount))
+
+/* Notify here immediately before barrier destruction. */
+#define VALGRIND_HG_BARRIER_DESTROY_PRE(_bar) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_BARRIER_DESTROY_PRE, \
+ void*,(_bar))
+
+/* ----------------------------------------------------------
+ For describing memory ownership changes.
+ ---------------------------------------------------------- */
+
+/* Clean memory state. This makes Helgrind forget everything it knew
+ about the specified memory range. Effectively this announces that
+ the specified memory range now "belongs" to the calling thread, so
+ that: (1) the calling thread can access it safely without
+ synchronisation, and (2) all other threads must sync with this one
+ to access it safely. This is particularly useful for memory
+ allocators that wish to recycle memory. */
+#define VALGRIND_HG_CLEAN_MEMORY(_qzz_start, _qzz_len) \
+ DO_CREQ_v_WW(VG_USERREQ__HG_CLEAN_MEMORY, \
+ void*,(_qzz_start), \
+ unsigned long,(_qzz_len))
+
+/* The same, but for the heap block starting at _qzz_blockstart. This
+ allows painting when we only know the address of an object, but not
+ its size, which is sometimes the case in C++ code involving
+ inheritance, and in which RTTI is not, for whatever reason,
+ available. Returns the number of bytes painted, which can be zero
+ for a zero-sized block. Hence, return values >= 0 indicate success
+ (the block was found), and the value -1 indicates block not
+ found, and -2 is returned when not running on Helgrind. */
+#define VALGRIND_HG_CLEAN_MEMORY_HEAPBLOCK(_qzz_blockstart) \
+ (__extension__ \
+ ({long int _npainted; \
+ DO_CREQ_W_W(_npainted, (-2)/*default*/, \
+ _VG_USERREQ__HG_CLEAN_MEMORY_HEAPBLOCK, \
+ void*,(_qzz_blockstart)); \
+ _npainted; \
+ }))
+
+/* ----------------------------------------------------------
+ For error control.
+ ---------------------------------------------------------- */
+
+/* Tell H that an address range is not to be "tracked" until further
+ notice. This puts it in the NOACCESS state, in which case we
+ ignore all reads and writes to it. Useful for ignoring ranges of
+ memory where there might be races we don't want to see. If the
+ memory is subsequently reallocated via malloc/new/stack allocation,
+ then it is put back in the trackable state. Hence it is safe in
+ the situation where checking is disabled, the containing area is
+ deallocated and later reallocated for some other purpose. */
+#define VALGRIND_HG_DISABLE_CHECKING(_qzz_start, _qzz_len) \
+ DO_CREQ_v_WW(_VG_USERREQ__HG_ARANGE_MAKE_UNTRACKED, \
+ void*,(_qzz_start), \
+ unsigned long,(_qzz_len))
+
+/* And put it back into the normal "tracked" state, that is, make it
+ once again subject to the normal race-checking machinery. This
+ puts it in the same state as new memory allocated by this thread --
+ that is, basically owned exclusively by this thread. */
+#define VALGRIND_HG_ENABLE_CHECKING(_qzz_start, _qzz_len) \
+ DO_CREQ_v_WW(_VG_USERREQ__HG_ARANGE_MAKE_TRACKED, \
+ void*,(_qzz_start), \
+ unsigned long,(_qzz_len))
+
+
+/*----------------------------------------------------------------*/
+/*--- ---*/
+/*--- ThreadSanitizer-compatible requests ---*/
+/*--- (mostly unimplemented) ---*/
+/*--- ---*/
+/*----------------------------------------------------------------*/
+
+/* A quite-broad set of annotations, as used in the ThreadSanitizer
+ project. This implementation aims to be a (source-level)
+ compatible implementation of the macros defined in:
+
+ http://code.google.com/p/data-race-test/source
+ /browse/trunk/dynamic_annotations/dynamic_annotations.h
+
+ (some of the comments below are taken from the above file)
+
+ The implementation here is very incomplete, and intended as a
+ starting point. Many of the macros are unimplemented. Rather than
+ allowing unimplemented macros to silently do nothing, they cause an
+ assertion. Intention is to implement them on demand.
+
+ The major use of these macros is to make visible to race detectors,
+ the behaviour (effects) of user-implemented synchronisation
+ primitives, that the detectors could not otherwise deduce from the
+ normal observation of pthread etc calls.
+
+ Some of the macros are no-ops in Helgrind. That's because Helgrind
+ is a pure happens-before detector, whereas ThreadSanitizer uses a
+ hybrid lockset and happens-before scheme, which requires more
+ accurate annotations for correct operation.
+
+ The macros are listed in the same order as in dynamic_annotations.h
+ (URL just above).
+
+ I should point out that I am less than clear about the intended
+ semantics of quite a number of them. Comments and clarifications
+ welcomed!
+*/
+
+/* ----------------------------------------------------------------
+ These four allow description of user-level condition variables,
+ apparently in the style of POSIX's pthread_cond_t. Currently
+ unimplemented and will assert.
+ ----------------------------------------------------------------
+*/
+/* Report that wait on the condition variable at address CV has
+ succeeded and the lock at address LOCK is now held. CV and LOCK
+ are completely arbitrary memory addresses which presumably mean
+ something to the application, but are meaningless to Helgrind. */
+#define ANNOTATE_CONDVAR_LOCK_WAIT(cv, lock) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_LOCK_WAIT")
+
+/* Report that wait on the condition variable at CV has succeeded.
+ Variant w/o lock. */
+#define ANNOTATE_CONDVAR_WAIT(cv) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_WAIT")
+
+/* Report that we are about to signal on the condition variable at
+ address CV. */
+#define ANNOTATE_CONDVAR_SIGNAL(cv) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_SIGNAL")
+
+/* Report that we are about to signal_all on the condition variable at
+ CV. */
+#define ANNOTATE_CONDVAR_SIGNAL_ALL(cv) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_CONDVAR_SIGNAL_ALL")
+
+
+/* ----------------------------------------------------------------
+ Create completely arbitrary happens-before edges between threads.
+
+ If threads T1 .. Tn all do ANNOTATE_HAPPENS_BEFORE(obj) and later
+ (w.r.t. some notional global clock for the computation) thread Tm
+ does ANNOTATE_HAPPENS_AFTER(obj), then Helgrind will regard all
+ memory accesses done by T1 .. Tn before the ..BEFORE.. call as
+ happening-before all memory accesses done by Tm after the
+ ..AFTER.. call. Hence Helgrind won't complain about races if Tm's
+ accesses afterwards are to the same locations as accesses before by
+ any of T1 .. Tn.
+
+ OBJ is a machine word (unsigned long, or void*), is completely
+ arbitrary, and denotes the identity of some synchronisation object
+ you're modelling.
+
+ You must do the _BEFORE call just before the real sync event on the
+ signaller's side, and _AFTER just after the real sync event on the
+ waiter's side.
+
+ If none of the rest of these macros make sense to you, at least
+ take the time to understand these two. They form the very essence
+ of describing arbitrary inter-thread synchronisation events to
+ Helgrind. You can get a long way just with them alone.
+
+ See also, extensive discussion on semantics of this in
+ https://bugs.kde.org/show_bug.cgi?id=243935
+
+ ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(obj) is interim until such time
+ as bug 243935 is fully resolved. It instructs Helgrind to forget
+ about any ANNOTATE_HAPPENS_BEFORE calls on the specified object, in
+ effect putting it back in its original state. Once in that state,
+ a use of ANNOTATE_HAPPENS_AFTER on it has no effect on the calling
+ thread.
+
+ An implementation may optionally release resources it has
+ associated with 'obj' when ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(obj)
+ happens. Users are recommended to use
+ ANNOTATE_HAPPENS_BEFORE_FORGET_ALL to indicate when a
+ synchronisation object is no longer needed, so as to avoid
+ potential indefinite resource leaks.
+ ----------------------------------------------------------------
+*/
+#define ANNOTATE_HAPPENS_BEFORE(obj) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_SEND_PRE, void*,(obj))
+
+#define ANNOTATE_HAPPENS_AFTER(obj) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_RECV_POST, void*,(obj))
+
+#define ANNOTATE_HAPPENS_BEFORE_FORGET_ALL(obj) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_USERSO_FORGET_ALL, void*,(obj))
+
+/* ----------------------------------------------------------------
+ Memory publishing. The TSan sources say:
+
+ Report that the bytes in the range [pointer, pointer+size) are about
+ to be published safely. The race checker will create a happens-before
+ arc from the call ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) to
+ subsequent accesses to this memory.
+
+ I'm not sure I understand what this means exactly, nor whether it
+ is relevant for a pure h-b detector. Leaving unimplemented for
+ now.
+ ----------------------------------------------------------------
+*/
+#define ANNOTATE_PUBLISH_MEMORY_RANGE(pointer, size) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_PUBLISH_MEMORY_RANGE")
+
+/* DEPRECATED. Don't use it. */
+/* #define ANNOTATE_UNPUBLISH_MEMORY_RANGE(pointer, size) */
+
+/* DEPRECATED. Don't use it. */
+/* #define ANNOTATE_SWAP_MEMORY_RANGE(pointer, size) */
+
+
+/* ----------------------------------------------------------------
+ TSan sources say:
+
+ Instruct the tool to create a happens-before arc between
+ MU->Unlock() and MU->Lock(). This annotation may slow down the
+ race detector; normally it is used only when it would be
+ difficult to annotate each of the mutex's critical sections
+ individually using the annotations above.
+
+ If MU is a posix pthread_mutex_t then Helgrind will do this anyway.
+ In any case, leave as unimp for now. I'm unsure about the intended
+ behaviour.
+ ----------------------------------------------------------------
+*/
+#define ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX(mu) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX")
+
+/* Deprecated. Use ANNOTATE_PURE_HAPPENS_BEFORE_MUTEX. */
+/* #define ANNOTATE_MUTEX_IS_USED_AS_CONDVAR(mu) */
+
+
+/* ----------------------------------------------------------------
+ TSan sources say:
+
+ Annotations useful when defining memory allocators, or when
+ memory that was protected in one way starts to be protected in
+ another.
+
+ Report that a new memory at "address" of size "size" has been
+ allocated. This might be used when the memory has been retrieved
+ from a free list and is about to be reused, or when a the locking
+ discipline for a variable changes.
+
+ AFAICS this is the same as VALGRIND_HG_CLEAN_MEMORY.
+ ----------------------------------------------------------------
+*/
+#define ANNOTATE_NEW_MEMORY(address, size) \
+ VALGRIND_HG_CLEAN_MEMORY((address), (size))
+
+
+/* ----------------------------------------------------------------
+ TSan sources say:
+
+ Annotations useful when defining FIFO queues that transfer data
+ between threads.
+
+ All unimplemented. Am not claiming to understand this (yet).
+ ----------------------------------------------------------------
+*/
+
+/* Report that the producer-consumer queue object at address PCQ has
+ been created. The ANNOTATE_PCQ_* annotations should be used only
+ for FIFO queues. For non-FIFO queues use ANNOTATE_HAPPENS_BEFORE
+ (for put) and ANNOTATE_HAPPENS_AFTER (for get). */
+#define ANNOTATE_PCQ_CREATE(pcq) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_CREATE")
+
+/* Report that the queue at address PCQ is about to be destroyed. */
+#define ANNOTATE_PCQ_DESTROY(pcq) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_DESTROY")
+
+/* Report that we are about to put an element into a FIFO queue at
+ address PCQ. */
+#define ANNOTATE_PCQ_PUT(pcq) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_PUT")
+
+/* Report that we've just got an element from a FIFO queue at address
+ PCQ. */
+#define ANNOTATE_PCQ_GET(pcq) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_PCQ_GET")
+
+
+/* ----------------------------------------------------------------
+ Annotations that suppress errors. It is usually better to express
+ the program's synchronization using the other annotations, but
+ these can be used when all else fails.
+
+ Currently these are all unimplemented. I can't think of a simple
+ way to implement them without at least some performance overhead.
+ ----------------------------------------------------------------
+*/
+
+/* Report that we may have a benign race at "pointer", with size
+ "sizeof(*(pointer))". "pointer" must be a non-void* pointer. Insert at the
+ point where "pointer" has been allocated, preferably close to the point
+ where the race happens. See also ANNOTATE_BENIGN_RACE_STATIC.
+
+ XXX: what's this actually supposed to do? And what's the type of
+ DESCRIPTION? When does the annotation stop having an effect?
+*/
+#define ANNOTATE_BENIGN_RACE(pointer, description) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_BENIGN_RACE")
+
+/* Same as ANNOTATE_BENIGN_RACE(address, description), but applies to
+ the memory range [address, address+size). */
+#define ANNOTATE_BENIGN_RACE_SIZED(address, size, description) \
+ VALGRIND_HG_DISABLE_CHECKING(address, size)
+
+/* Request the analysis tool to ignore all reads in the current thread
+ until ANNOTATE_IGNORE_READS_END is called. Useful to ignore
+ intentional racey reads, while still checking other reads and all
+ writes. */
+#define ANNOTATE_IGNORE_READS_BEGIN() \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_READS_BEGIN")
+
+/* Stop ignoring reads. */
+#define ANNOTATE_IGNORE_READS_END() \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_READS_END")
+
+/* Similar to ANNOTATE_IGNORE_READS_BEGIN, but ignore writes. */
+#define ANNOTATE_IGNORE_WRITES_BEGIN() \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_WRITES_BEGIN")
+
+/* Stop ignoring writes. */
+#define ANNOTATE_IGNORE_WRITES_END() \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_IGNORE_WRITES_END")
+
+/* Start ignoring all memory accesses (reads and writes). */
+#define ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN() \
+ do { \
+ ANNOTATE_IGNORE_READS_BEGIN(); \
+ ANNOTATE_IGNORE_WRITES_BEGIN(); \
+ } while (0)
+
+/* Stop ignoring all memory accesses. */
+#define ANNOTATE_IGNORE_READS_AND_WRITES_END() \
+ do { \
+ ANNOTATE_IGNORE_WRITES_END(); \
+ ANNOTATE_IGNORE_READS_END(); \
+ } while (0)
+
+
+/* ----------------------------------------------------------------
+ Annotations useful for debugging.
+
+ Again, so for unimplemented, partly for performance reasons.
+ ----------------------------------------------------------------
+*/
+
+/* Request to trace every access to ADDRESS. */
+#define ANNOTATE_TRACE_MEMORY(address) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_TRACE_MEMORY")
+
+/* Report the current thread name to a race detector. */
+#define ANNOTATE_THREAD_NAME(name) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_THREAD_NAME")
+
+
+/* ----------------------------------------------------------------
+ Annotations for describing behaviour of user-implemented lock
+ primitives. In all cases, the LOCK argument is a completely
+ arbitrary machine word (unsigned long, or void*) and can be any
+ value which gives a unique identity to the lock objects being
+ modelled.
+
+ We just pretend they're ordinary posix rwlocks. That'll probably
+ give some rather confusing wording in error messages, claiming that
+ the arbitrary LOCK values are pthread_rwlock_t*'s, when in fact
+ they are not. Ah well.
+ ----------------------------------------------------------------
+*/
+/* Report that a lock has just been created at address LOCK. */
+#define ANNOTATE_RWLOCK_CREATE(lock) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_INIT_POST, \
+ void*,(lock))
+
+/* Report that the lock at address LOCK is about to be destroyed. */
+#define ANNOTATE_RWLOCK_DESTROY(lock) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_DESTROY_PRE, \
+ void*,(lock))
+
+/* Report that the lock at address LOCK has just been acquired.
+ is_w=1 for writer lock, is_w=0 for reader lock. */
+#define ANNOTATE_RWLOCK_ACQUIRED(lock, is_w) \
+ DO_CREQ_v_WW(_VG_USERREQ__HG_PTHREAD_RWLOCK_LOCK_POST, \
+ void*,(lock), unsigned long,(is_w))
+
+/* Report that the lock at address LOCK is about to be released. */
+#define ANNOTATE_RWLOCK_RELEASED(lock, is_w) \
+ DO_CREQ_v_W(_VG_USERREQ__HG_PTHREAD_RWLOCK_UNLOCK_PRE, \
+ void*,(lock)) /* is_w is ignored */
+
+
+/* -------------------------------------------------------------
+ Annotations useful when implementing barriers. They are not
+ normally needed by modules that merely use barriers.
+ The "barrier" argument is a pointer to the barrier object.
+ ----------------------------------------------------------------
+*/
+
+/* Report that the "barrier" has been initialized with initial
+ "count". If 'reinitialization_allowed' is true, initialization is
+ allowed to happen multiple times w/o calling barrier_destroy() */
+#define ANNOTATE_BARRIER_INIT(barrier, count, reinitialization_allowed) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_INIT")
+
+/* Report that we are about to enter barrier_wait("barrier"). */
+#define ANNOTATE_BARRIER_WAIT_BEFORE(barrier) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY")
+
+/* Report that we just exited barrier_wait("barrier"). */
+#define ANNOTATE_BARRIER_WAIT_AFTER(barrier) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY")
+
+/* Report that the "barrier" has been destroyed. */
+#define ANNOTATE_BARRIER_DESTROY(barrier) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_BARRIER_DESTROY")
+
+
+/* ----------------------------------------------------------------
+ Annotations useful for testing race detectors.
+ ----------------------------------------------------------------
+*/
+
+/* Report that we expect a race on the variable at ADDRESS. Use only
+ in unit tests for a race detector. */
+#define ANNOTATE_EXPECT_RACE(address, description) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_EXPECT_RACE")
+
+/* A no-op. Insert where you like to test the interceptors. */
+#define ANNOTATE_NO_OP(arg) \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_NO_OP")
+
+/* Force the race detector to flush its state. The actual effect depends on
+ * the implementation of the detector. */
+#define ANNOTATE_FLUSH_STATE() \
+ _HG_CLIENTREQ_UNIMP("ANNOTATE_FLUSH_STATE")
+
+#endif /* __HELGRIND_H */
diff --git a/attic/include/boost/afio/v2/detail/valgrind/memcheck.h b/attic/include/boost/afio/v2/detail/valgrind/memcheck.h
new file mode 100644
index 00000000..3af0728f
--- /dev/null
+++ b/attic/include/boost/afio/v2/detail/valgrind/memcheck.h
@@ -0,0 +1,287 @@
+
+/*
+ ----------------------------------------------------------------
+
+ Notice that the following BSD-style license applies to this one
+ file (memcheck.h) only. The rest of Valgrind is licensed under the
+ terms of the GNU General Public License, version 2, unless
+ otherwise indicated. See the COPYING file in the source
+ distribution for details.
+
+ ----------------------------------------------------------------
+
+ This file is part of MemCheck, a heavyweight Valgrind tool for
+ detecting memory errors.
+
+ Copyright (C) 2000-2013 Julian Seward. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ----------------------------------------------------------------
+
+ Notice that the above BSD-style license applies to this one file
+ (memcheck.h) only. The entire rest of Valgrind is licensed under
+ the terms of the GNU General Public License, version 2. See the
+ COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+*/
+
+
+#ifndef __MEMCHECK_H
+#define __MEMCHECK_H
+
+
+/* This file is for inclusion into client (your!) code.
+
+ You can use these macros to manipulate and query memory permissions
+ inside your own programs.
+
+ See comment near the top of valgrind.h on how to use them.
+*/
+
+#include "valgrind.h"
+
+/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
+ This enum comprises an ABI exported by Valgrind to programs
+ which use client requests. DO NOT CHANGE THE ORDER OF THESE
+ ENTRIES, NOR DELETE ANY -- add new ones at the end. */
+typedef
+ enum {
+ VG_USERREQ__MAKE_MEM_NOACCESS = VG_USERREQ_TOOL_BASE('M','C'),
+ VG_USERREQ__MAKE_MEM_UNDEFINED,
+ VG_USERREQ__MAKE_MEM_DEFINED,
+ VG_USERREQ__DISCARD,
+ VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE,
+ VG_USERREQ__CHECK_MEM_IS_DEFINED,
+ VG_USERREQ__DO_LEAK_CHECK,
+ VG_USERREQ__COUNT_LEAKS,
+
+ VG_USERREQ__GET_VBITS,
+ VG_USERREQ__SET_VBITS,
+
+ VG_USERREQ__CREATE_BLOCK,
+
+ VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE,
+
+ /* Not next to VG_USERREQ__COUNT_LEAKS because it was added later. */
+ VG_USERREQ__COUNT_LEAK_BLOCKS,
+
+ /* This is just for memcheck's internal use - don't use it */
+ _VG_USERREQ__MEMCHECK_RECORD_OVERLAP_ERROR
+ = VG_USERREQ_TOOL_BASE('M','C') + 256
+ } Vg_MemCheckClientRequest;
+
+
+
+/* Client-code macros to manipulate the state of memory. */
+
+/* Mark memory at _qzz_addr as unaddressable for _qzz_len bytes. */
+#define VALGRIND_MAKE_MEM_NOACCESS(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_NOACCESS, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Similarly, mark memory at _qzz_addr as addressable but undefined
+ for _qzz_len bytes. */
+#define VALGRIND_MAKE_MEM_UNDEFINED(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_UNDEFINED, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Similarly, mark memory at _qzz_addr as addressable and defined
+ for _qzz_len bytes. */
+#define VALGRIND_MAKE_MEM_DEFINED(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_DEFINED, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Similar to VALGRIND_MAKE_MEM_DEFINED except that addressability is
+ not altered: bytes which are addressable are marked as defined,
+ but those which are not addressable are left unchanged. */
+#define VALGRIND_MAKE_MEM_DEFINED_IF_ADDRESSABLE(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__MAKE_MEM_DEFINED_IF_ADDRESSABLE, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Create a block-description handle. The description is an ascii
+ string which is included in any messages pertaining to addresses
+ within the specified memory range. Has no other effect on the
+ properties of the memory range. */
+#define VALGRIND_CREATE_BLOCK(_qzz_addr,_qzz_len, _qzz_desc) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__CREATE_BLOCK, \
+ (_qzz_addr), (_qzz_len), (_qzz_desc), \
+ 0, 0)
+
+/* Discard a block-description-handle. Returns 1 for an
+ invalid handle, 0 for a valid handle. */
+#define VALGRIND_DISCARD(_qzz_blkindex) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__DISCARD, \
+ 0, (_qzz_blkindex), 0, 0, 0)
+
+
+/* Client-code macros to check the state of memory. */
+
+/* Check that memory at _qzz_addr is addressable for _qzz_len bytes.
+ If suitable addressibility is not established, Valgrind prints an
+ error message and returns the address of the first offending byte.
+ Otherwise it returns zero. */
+#define VALGRIND_CHECK_MEM_IS_ADDRESSABLE(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__CHECK_MEM_IS_ADDRESSABLE, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Check that memory at _qzz_addr is addressable and defined for
+ _qzz_len bytes. If suitable addressibility and definedness are not
+ established, Valgrind prints an error message and returns the
+ address of the first offending byte. Otherwise it returns zero. */
+#define VALGRIND_CHECK_MEM_IS_DEFINED(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__CHECK_MEM_IS_DEFINED, \
+ (_qzz_addr), (_qzz_len), 0, 0, 0)
+
+/* Use this macro to force the definedness and addressibility of an
+ lvalue to be checked. If suitable addressibility and definedness
+ are not established, Valgrind prints an error message and returns
+ the address of the first offending byte. Otherwise it returns
+ zero. */
+#define VALGRIND_CHECK_VALUE_IS_DEFINED(__lvalue) \
+ VALGRIND_CHECK_MEM_IS_DEFINED( \
+ (volatile unsigned char *)&(__lvalue), \
+ (unsigned long)(sizeof (__lvalue)))
+
+
+/* Do a full memory leak check (like --leak-check=full) mid-execution. */
+#define VALGRIND_DO_LEAK_CHECK \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \
+ 0, 0, 0, 0, 0)
+
+/* Same as VALGRIND_DO_LEAK_CHECK but only showing the entries for
+ which there was an increase in leaked bytes or leaked nr of blocks
+ since the previous leak search. */
+#define VALGRIND_DO_ADDED_LEAK_CHECK \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \
+ 0, 1, 0, 0, 0)
+
+/* Same as VALGRIND_DO_ADDED_LEAK_CHECK but showing entries with
+ increased or decreased leaked bytes/blocks since previous leak
+ search. */
+#define VALGRIND_DO_CHANGED_LEAK_CHECK \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \
+ 0, 2, 0, 0, 0)
+
+/* Do a summary memory leak check (like --leak-check=summary) mid-execution. */
+#define VALGRIND_DO_QUICK_LEAK_CHECK \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DO_LEAK_CHECK, \
+ 1, 0, 0, 0, 0)
+
+/* Return number of leaked, dubious, reachable and suppressed bytes found by
+ all previous leak checks. They must be lvalues. */
+#define VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed) \
+ /* For safety on 64-bit platforms we assign the results to private
+ unsigned long variables, then assign these to the lvalues the user
+ specified, which works no matter what type 'leaked', 'dubious', etc
+ are. We also initialise '_qzz_leaked', etc because
+ VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
+ defined. */ \
+ { \
+ unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
+ unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
+ VALGRIND_DO_CLIENT_REQUEST_STMT( \
+ VG_USERREQ__COUNT_LEAKS, \
+ &_qzz_leaked, &_qzz_dubious, \
+ &_qzz_reachable, &_qzz_suppressed, 0); \
+ leaked = _qzz_leaked; \
+ dubious = _qzz_dubious; \
+ reachable = _qzz_reachable; \
+ suppressed = _qzz_suppressed; \
+ }
+
+/* Return number of leaked, dubious, reachable and suppressed bytes found by
+ all previous leak checks. They must be lvalues. */
+#define VALGRIND_COUNT_LEAK_BLOCKS(leaked, dubious, reachable, suppressed) \
+ /* For safety on 64-bit platforms we assign the results to private
+ unsigned long variables, then assign these to the lvalues the user
+ specified, which works no matter what type 'leaked', 'dubious', etc
+ are. We also initialise '_qzz_leaked', etc because
+ VG_USERREQ__COUNT_LEAKS doesn't mark the values returned as
+ defined. */ \
+ { \
+ unsigned long _qzz_leaked = 0, _qzz_dubious = 0; \
+ unsigned long _qzz_reachable = 0, _qzz_suppressed = 0; \
+ VALGRIND_DO_CLIENT_REQUEST_STMT( \
+ VG_USERREQ__COUNT_LEAK_BLOCKS, \
+ &_qzz_leaked, &_qzz_dubious, \
+ &_qzz_reachable, &_qzz_suppressed, 0); \
+ leaked = _qzz_leaked; \
+ dubious = _qzz_dubious; \
+ reachable = _qzz_reachable; \
+ suppressed = _qzz_suppressed; \
+ }
+
+
+/* Get the validity data for addresses [zza..zza+zznbytes-1] and copy it
+ into the provided zzvbits array. Return values:
+ 0 if not running on valgrind
+ 1 success
+ 2 [previously indicated unaligned arrays; these are now allowed]
+ 3 if any parts of zzsrc/zzvbits are not addressable.
+ The metadata is not copied in cases 0, 2 or 3 so it should be
+ impossible to segfault your system by using this call.
+*/
+#define VALGRIND_GET_VBITS(zza,zzvbits,zznbytes) \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__GET_VBITS, \
+ (const char*)(zza), \
+ (char*)(zzvbits), \
+ (zznbytes), 0, 0)
+
+/* Set the validity data for addresses [zza..zza+zznbytes-1], copying it
+ from the provided zzvbits array. Return values:
+ 0 if not running on valgrind
+ 1 success
+ 2 [previously indicated unaligned arrays; these are now allowed]
+ 3 if any parts of zza/zzvbits are not addressable.
+ The metadata is not copied in cases 0, 2 or 3 so it should be
+ impossible to segfault your system by using this call.
+*/
+#define VALGRIND_SET_VBITS(zza,zzvbits,zznbytes) \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__SET_VBITS, \
+ (const char*)(zza), \
+ (const char*)(zzvbits), \
+ (zznbytes), 0, 0 )
+
+#endif
+
diff --git a/attic/include/boost/afio/v2/detail/valgrind/valgrind.h b/attic/include/boost/afio/v2/detail/valgrind/valgrind.h
new file mode 100644
index 00000000..224a3f43
--- /dev/null
+++ b/attic/include/boost/afio/v2/detail/valgrind/valgrind.h
@@ -0,0 +1,5415 @@
+/* -*- c -*-
+ ----------------------------------------------------------------
+
+ Notice that the following BSD-style license applies to this one
+ file (valgrind.h) only. The rest of Valgrind is licensed under the
+ terms of the GNU General Public License, version 2, unless
+ otherwise indicated. See the COPYING file in the source
+ distribution for details.
+
+ ----------------------------------------------------------------
+
+ This file is part of Valgrind, a dynamic binary instrumentation
+ framework.
+
+ Copyright (C) 2000-2013 Julian Seward. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ 2. The origin of this software must not be misrepresented; you must
+ not claim that you wrote the original software. If you use this
+ software in a product, an acknowledgment in the product
+ documentation would be appreciated but is not required.
+
+ 3. Altered source versions must be plainly marked as such, and must
+ not be misrepresented as being the original software.
+
+ 4. The name of the author may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+ ----------------------------------------------------------------
+
+ Notice that the above BSD-style license applies to this one file
+ (valgrind.h) only. The entire rest of Valgrind is licensed under
+ the terms of the GNU General Public License, version 2. See the
+ COPYING file in the source distribution for details.
+
+ ----------------------------------------------------------------
+*/
+
+
+/* This file is for inclusion into client (your!) code.
+
+ You can use these macros to manipulate and query Valgrind's
+ execution inside your own programs.
+
+ The resulting executables will still run without Valgrind, just a
+ little bit more slowly than they otherwise would, but otherwise
+ unchanged. When not running on valgrind, each client request
+ consumes very few (eg. 7) instructions, so the resulting performance
+ loss is negligible unless you plan to execute client requests
+ millions of times per second. Nevertheless, if that is still a
+ problem, you can compile with the NVALGRIND symbol defined (gcc
+ -DNVALGRIND) so that client requests are not even compiled in. */
+
+#ifndef __VALGRIND_H
+#define __VALGRIND_H
+
+
+/* ------------------------------------------------------------------ */
+/* VERSION NUMBER OF VALGRIND */
+/* ------------------------------------------------------------------ */
+
+/* Specify Valgrind's version number, so that user code can
+ conditionally compile based on our version number. Note that these
+ were introduced at version 3.6 and so do not exist in version 3.5
+ or earlier. The recommended way to use them to check for "version
+ X.Y or later" is (eg)
+
+#if defined(__VALGRIND_MAJOR__) && defined(__VALGRIND_MINOR__) \
+ && (__VALGRIND_MAJOR__ > 3 \
+ || (__VALGRIND_MAJOR__ == 3 && __VALGRIND_MINOR__ >= 6))
+*/
+#define __VALGRIND_MAJOR__ 3
+#define __VALGRIND_MINOR__ 8
+
+
+#include <stdarg.h>
+
+/* Nb: this file might be included in a file compiled with -ansi. So
+ we can't use C++ style "//" comments nor the "asm" keyword (instead
+ use "__asm__"). */
+
+/* Derive some tags indicating what the target platform is. Note
+ that in this file we're using the compiler's CPP symbols for
+ identifying architectures, which are different to the ones we use
+ within the rest of Valgrind. Note, __powerpc__ is active for both
+ 32 and 64-bit PPC, whereas __powerpc64__ is only active for the
+ latter (on Linux, that is).
+
+ Misc note: how to find out what's predefined in gcc by default:
+ gcc -Wp,-dM somefile.c
+*/
+#undef PLAT_x86_darwin
+#undef PLAT_amd64_darwin
+#undef PLAT_x86_win32
+#undef PLAT_amd64_win64
+#undef PLAT_x86_linux
+#undef PLAT_amd64_linux
+#undef PLAT_ppc32_linux
+#undef PLAT_ppc64_linux
+#undef PLAT_arm_linux
+#undef PLAT_s390x_linux
+#undef PLAT_mips32_linux
+#undef PLAT_mips64_linux
+
+
+#if defined(__APPLE__) && defined(__i386__)
+# define PLAT_x86_darwin 1
+#elif defined(__APPLE__) && defined(__x86_64__)
+# define PLAT_amd64_darwin 1
+#elif defined(__MINGW32__) || defined(__CYGWIN32__) \
+ || (defined(_WIN32) && defined(_M_IX86))
+# define PLAT_x86_win32 1
+#elif defined(__MINGW64__) || (defined(_WIN64) && defined(_M_X64))
+# define PLAT_amd64_win64 1
+// ned: Looks like PLAT_amd64_win64 isn't really implemented
+# define NVALGRIND 1
+#elif defined(__linux__) && defined(__i386__)
+# define PLAT_x86_linux 1
+#elif defined(__linux__) && defined(__x86_64__)
+# define PLAT_amd64_linux 1
+#elif defined(__linux__) && defined(__powerpc__) && !defined(__powerpc64__)
+# define PLAT_ppc32_linux 1
+#elif defined(__linux__) && defined(__powerpc__) && defined(__powerpc64__)
+# define PLAT_ppc64_linux 1
+#elif defined(__linux__) && defined(__arm__)
+# define PLAT_arm_linux 1
+#elif defined(__linux__) && defined(__s390__) && defined(__s390x__)
+# define PLAT_s390x_linux 1
+#elif defined(__linux__) && defined(__mips__)
+#if (__mips==64)
+# define PLAT_mips64_linux 1
+#else
+# define PLAT_mips32_linux 1
+#endif
+#else
+/* If we're not compiling for our target platform, don't generate
+ any inline asms. */
+# if !defined(NVALGRIND)
+# define NVALGRIND 1
+# endif
+#endif
+
+
+/* ------------------------------------------------------------------ */
+/* ARCHITECTURE SPECIFICS for SPECIAL INSTRUCTIONS. There is nothing */
+/* in here of use to end-users -- skip to the next section. */
+/* ------------------------------------------------------------------ */
+
+/*
+ * VALGRIND_DO_CLIENT_REQUEST(): a statement that invokes a Valgrind client
+ * request. Accepts both pointers and integers as arguments.
+ *
+ * VALGRIND_DO_CLIENT_REQUEST_STMT(): a statement that invokes a Valgrind
+ * client request that does not return a value.
+
+ * VALGRIND_DO_CLIENT_REQUEST_EXPR(): a C expression that invokes a Valgrind
+ * client request and whose value equals the client request result. Accepts
+ * both pointers and integers as arguments. Note that such calls are not
+ * necessarily pure functions -- they may have side effects.
+ */
+
+#define VALGRIND_DO_CLIENT_REQUEST(_zzq_rlval, _zzq_default, \
+ _zzq_request, _zzq_arg1, _zzq_arg2, \
+ _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ do { (_zzq_rlval) = VALGRIND_DO_CLIENT_REQUEST_EXPR((_zzq_default), \
+ (_zzq_request), (_zzq_arg1), (_zzq_arg2), \
+ (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0)
+
+#define VALGRIND_DO_CLIENT_REQUEST_STMT(_zzq_request, _zzq_arg1, \
+ _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ do { (void) VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ (_zzq_request), (_zzq_arg1), (_zzq_arg2), \
+ (_zzq_arg3), (_zzq_arg4), (_zzq_arg5)); } while (0)
+
+#if defined(NVALGRIND)
+
+/* Define NVALGRIND to completely remove the Valgrind magic sequence
+ from the compiled code (analogous to NDEBUG's effects on
+ assert()) */
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ (_zzq_default)
+
+#else /* ! NVALGRIND */
+
+/* The following defines the magic code sequences which the JITter
+ spots and handles magically. Don't look too closely at them as
+ they will rot your brain.
+
+ The assembly code sequences for all architectures is in this one
+ file. This is because this file must be stand-alone, and we don't
+ want to have multiple files.
+
+ For VALGRIND_DO_CLIENT_REQUEST, we must ensure that the default
+ value gets put in the return slot, so that everything works when
+ this is executed not under Valgrind. Args are passed in a memory
+ block, and so there's no intrinsic limit to the number that could
+ be passed, but it's currently five.
+
+ The macro args are:
+ _zzq_rlval result lvalue
+ _zzq_default default value (result returned when running on real CPU)
+ _zzq_request request code
+ _zzq_arg1..5 request params
+
+ The other two macros are used to support function wrapping, and are
+ a lot simpler. VALGRIND_GET_NR_CONTEXT returns the value of the
+ guest's NRADDR pseudo-register and whatever other information is
+ needed to safely run the call original from the wrapper: on
+ ppc64-linux, the R2 value at the divert point is also needed. This
+ information is abstracted into a user-visible type, OrigFn.
+
+ VALGRIND_CALL_NOREDIR_* behaves the same as the following on the
+ guest, but guarantees that the branch instruction will not be
+ redirected: x86: call *%eax, amd64: call *%rax, ppc32/ppc64:
+ branch-and-link-to-r11. VALGRIND_CALL_NOREDIR is just text, not a
+ complete inline asm, since it needs to be combined with more magic
+ inline asm stuff to be useful.
+*/
+
+/* ------------------------- x86-{linux,darwin} ---------------- */
+
+#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin) \
+ || (defined(PLAT_x86_win32) && defined(__GNUC__))
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "roll $3, %%edi ; roll $13, %%edi\n\t" \
+ "roll $29, %%edi ; roll $19, %%edi\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ __extension__ \
+ ({volatile unsigned int _zzq_args[6]; \
+ volatile unsigned int _zzq_result; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EDX = client_request ( %EAX ) */ \
+ "xchgl %%ebx,%%ebx" \
+ : "=d" (_zzq_result) \
+ : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "cc", "memory" \
+ ); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EAX = guest_NRADDR */ \
+ "xchgl %%ecx,%%ecx" \
+ : "=a" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_EAX \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir *%EAX */ \
+ "xchgl %%edx,%%edx\n\t"
+
+#define VALGRIND_VEX_INJECT_IR() \
+ do { \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ "xchgl %%edi,%%edi\n\t" \
+ : : : "cc", "memory" \
+ ); \
+ } while (0)
+
+#endif /* PLAT_x86_linux || PLAT_x86_darwin || (PLAT_x86_win32 && __GNUC__) */
+
+/* ------------------------- x86-Win32 ------------------------- */
+
+#if defined(PLAT_x86_win32) && !defined(__GNUC__)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#if defined(_MSC_VER)
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ __asm rol edi, 3 __asm rol edi, 13 \
+ __asm rol edi, 29 __asm rol edi, 19
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ valgrind_do_client_request_expr((uintptr_t)(_zzq_default), \
+ (uintptr_t)(_zzq_request), (uintptr_t)(_zzq_arg1), \
+ (uintptr_t)(_zzq_arg2), (uintptr_t)(_zzq_arg3), \
+ (uintptr_t)(_zzq_arg4), (uintptr_t)(_zzq_arg5))
+
+static __inline uintptr_t
+valgrind_do_client_request_expr(uintptr_t _zzq_default, uintptr_t _zzq_request,
+ uintptr_t _zzq_arg1, uintptr_t _zzq_arg2,
+ uintptr_t _zzq_arg3, uintptr_t _zzq_arg4,
+ uintptr_t _zzq_arg5)
+{
+ volatile uintptr_t _zzq_args[6];
+ volatile unsigned int _zzq_result;
+ _zzq_args[0] = (uintptr_t)(_zzq_request);
+ _zzq_args[1] = (uintptr_t)(_zzq_arg1);
+ _zzq_args[2] = (uintptr_t)(_zzq_arg2);
+ _zzq_args[3] = (uintptr_t)(_zzq_arg3);
+ _zzq_args[4] = (uintptr_t)(_zzq_arg4);
+ _zzq_args[5] = (uintptr_t)(_zzq_arg5);
+ __asm { __asm lea eax, _zzq_args __asm mov edx, _zzq_default
+ __SPECIAL_INSTRUCTION_PREAMBLE
+ /* %EDX = client_request ( %EAX ) */
+ __asm xchg ebx,ebx
+ __asm mov _zzq_result, edx
+ }
+ return _zzq_result;
+}
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned int __addr; \
+ __asm { __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %EAX = guest_NRADDR */ \
+ __asm xchg ecx,ecx \
+ __asm mov __addr, eax \
+ } \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_EAX ERROR
+
+#define VALGRIND_VEX_INJECT_IR() \
+ do { \
+ __asm { __SPECIAL_INSTRUCTION_PREAMBLE \
+ __asm xchg edi,edi \
+ } \
+ } while (0)
+
+#else
+#error Unsupported compiler.
+#endif
+
+#endif /* PLAT_x86_win32 */
+
+/* ------------------------ amd64-{linux,darwin} --------------- */
+
+#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin)
+
+typedef
+ struct {
+ unsigned long long int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rolq $3, %%rdi ; rolq $13, %%rdi\n\t" \
+ "rolq $61, %%rdi ; rolq $51, %%rdi\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ __extension__ \
+ ({ volatile unsigned long long int _zzq_args[6]; \
+ volatile unsigned long long int _zzq_result; \
+ _zzq_args[0] = (unsigned long long int)(_zzq_request); \
+ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %RDX = client_request ( %RAX ) */ \
+ "xchgq %%rbx,%%rbx" \
+ : "=d" (_zzq_result) \
+ : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "cc", "memory" \
+ ); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned long long int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %RAX = guest_NRADDR */ \
+ "xchgq %%rcx,%%rcx" \
+ : "=a" (__addr) \
+ : \
+ : "cc", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_RAX \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir *%RAX */ \
+ "xchgq %%rdx,%%rdx\n\t"
+
+#define VALGRIND_VEX_INJECT_IR() \
+ do { \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ "xchgq %%rdi,%%rdi\n\t" \
+ : : : "cc", "memory" \
+ ); \
+ } while (0)
+
+#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */
+
+/* ------------------------ ppc32-linux ------------------------ */
+
+#if defined(PLAT_ppc32_linux)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rlwinm 0,0,3,0,0 ; rlwinm 0,0,13,0,0\n\t" \
+ "rlwinm 0,0,29,0,0 ; rlwinm 0,0,19,0,0\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ __extension__ \
+ ({ unsigned int _zzq_args[6]; \
+ unsigned int _zzq_result; \
+ unsigned int* _zzq_ptr; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile("mr 3,%1\n\t" /*default*/ \
+ "mr 4,%2\n\t" /*ptr*/ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1\n\t" \
+ "mr %0,3" /*result*/ \
+ : "=b" (_zzq_result) \
+ : "b" (_zzq_default), "b" (_zzq_ptr) \
+ : "cc", "memory", "r3", "r4"); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "cc", "memory", "r3" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+
+#define VALGRIND_VEX_INJECT_IR() \
+ do { \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ "or 5,5,5\n\t" \
+ ); \
+ } while (0)
+
+#endif /* PLAT_ppc32_linux */
+
+/* ------------------------ ppc64-linux ------------------------ */
+
+#if defined(PLAT_ppc64_linux)
+
+typedef
+ struct {
+ unsigned long long int nraddr; /* where's the code? */
+ unsigned long long int r2; /* what tocptr do we need? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "rotldi 0,0,3 ; rotldi 0,0,13\n\t" \
+ "rotldi 0,0,61 ; rotldi 0,0,51\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ __extension__ \
+ ({ unsigned long long int _zzq_args[6]; \
+ unsigned long long int _zzq_result; \
+ unsigned long long int* _zzq_ptr; \
+ _zzq_args[0] = (unsigned long long int)(_zzq_request); \
+ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \
+ _zzq_ptr = _zzq_args; \
+ __asm__ volatile("mr 3,%1\n\t" /*default*/ \
+ "mr 4,%2\n\t" /*ptr*/ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = client_request ( %R4 ) */ \
+ "or 1,1,1\n\t" \
+ "mr %0,3" /*result*/ \
+ : "=b" (_zzq_result) \
+ : "b" (_zzq_default), "b" (_zzq_ptr) \
+ : "cc", "memory", "r3", "r4"); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ unsigned long long int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR */ \
+ "or 2,2,2\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "cc", "memory", "r3" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %R3 = guest_NRADDR_GPR2 */ \
+ "or 4,4,4\n\t" \
+ "mr %0,3" \
+ : "=b" (__addr) \
+ : \
+ : "cc", "memory", "r3" \
+ ); \
+ _zzq_orig->r2 = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R11 */ \
+ "or 3,3,3\n\t"
+
+#define VALGRIND_VEX_INJECT_IR() \
+ do { \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ "or 5,5,5\n\t" \
+ ); \
+ } while (0)
+
+#endif /* PLAT_ppc64_linux */
+
+/* ------------------------- arm-linux ------------------------- */
+
+#if defined(PLAT_arm_linux)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "mov r12, r12, ror #3 ; mov r12, r12, ror #13 \n\t" \
+ "mov r12, r12, ror #29 ; mov r12, r12, ror #19 \n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ \
+ __extension__ \
+ ({volatile unsigned int _zzq_args[6]; \
+ volatile unsigned int _zzq_result; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ __asm__ volatile("mov r3, %1\n\t" /*default*/ \
+ "mov r4, %2\n\t" /*ptr*/ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* R3 = client_request ( R4 ) */ \
+ "orr r10, r10, r10\n\t" \
+ "mov %0, r3" /*result*/ \
+ : "=r" (_zzq_result) \
+ : "r" (_zzq_default), "r" (&_zzq_args[0]) \
+ : "cc","memory", "r3", "r4"); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* R3 = guest_NRADDR */ \
+ "orr r11, r11, r11\n\t" \
+ "mov %0, r3" \
+ : "=r" (__addr) \
+ : \
+ : "cc", "memory", "r3" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* branch-and-link-to-noredir *%R4 */ \
+ "orr r12, r12, r12\n\t"
+
+#define VALGRIND_VEX_INJECT_IR() \
+ do { \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ "orr r9, r9, r9\n\t" \
+ : : : "cc", "memory" \
+ ); \
+ } while (0)
+
+#endif /* PLAT_arm_linux */
+
+/* ------------------------ s390x-linux ------------------------ */
+
+#if defined(PLAT_s390x_linux)
+
+typedef
+ struct {
+ unsigned long long int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+/* __SPECIAL_INSTRUCTION_PREAMBLE will be used to identify Valgrind specific
+ * code. This detection is implemented in platform specific toIR.c
+ * (e.g. VEX/priv/guest_s390_decoder.c).
+ */
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "lr 15,15\n\t" \
+ "lr 1,1\n\t" \
+ "lr 2,2\n\t" \
+ "lr 3,3\n\t"
+
+#define __CLIENT_REQUEST_CODE "lr 2,2\n\t"
+#define __GET_NR_CONTEXT_CODE "lr 3,3\n\t"
+#define __CALL_NO_REDIR_CODE "lr 4,4\n\t"
+#define __VEX_INJECT_IR_CODE "lr 5,5\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ __extension__ \
+ ({volatile unsigned long long int _zzq_args[6]; \
+ volatile unsigned long long int _zzq_result; \
+ _zzq_args[0] = (unsigned long long int)(_zzq_request); \
+ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \
+ __asm__ volatile(/* r2 = args */ \
+ "lgr 2,%1\n\t" \
+ /* r3 = default */ \
+ "lgr 3,%2\n\t" \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ __CLIENT_REQUEST_CODE \
+ /* results = r3 */ \
+ "lgr %0, 3\n\t" \
+ : "=d" (_zzq_result) \
+ : "a" (&_zzq_args[0]), "0" (_zzq_default) \
+ : "cc", "2", "3", "memory" \
+ ); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned long long int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ __GET_NR_CONTEXT_CODE \
+ "lgr %0, 3\n\t" \
+ : "=a" (__addr) \
+ : \
+ : "cc", "3", "memory" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_R1 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ __CALL_NO_REDIR_CODE
+
+#define VALGRIND_VEX_INJECT_IR() \
+ do { \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ __VEX_INJECT_IR_CODE); \
+ } while (0)
+
+#endif /* PLAT_s390x_linux */
+
+/* ------------------------- mips32-linux ---------------- */
+
+#if defined(PLAT_mips32_linux)
+
+typedef
+ struct {
+ unsigned int nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+/* .word 0x342
+ * .word 0x742
+ * .word 0xC2
+ * .word 0x4C2*/
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "srl $0, $0, 13\n\t" \
+ "srl $0, $0, 29\n\t" \
+ "srl $0, $0, 3\n\t" \
+ "srl $0, $0, 19\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ __extension__ \
+ ({ volatile unsigned int _zzq_args[6]; \
+ volatile unsigned int _zzq_result; \
+ _zzq_args[0] = (unsigned int)(_zzq_request); \
+ _zzq_args[1] = (unsigned int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned int)(_zzq_arg5); \
+ __asm__ volatile("move $11, %1\n\t" /*default*/ \
+ "move $12, %2\n\t" /*ptr*/ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* T3 = client_request ( T4 ) */ \
+ "or $13, $13, $13\n\t" \
+ "move %0, $11\n\t" /*result*/ \
+ : "=r" (_zzq_result) \
+ : "r" (_zzq_default), "r" (&_zzq_args[0]) \
+ : "$11", "$12"); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* %t9 = guest_NRADDR */ \
+ "or $14, $14, $14\n\t" \
+ "move %0, $11" /*result*/ \
+ : "=r" (__addr) \
+ : \
+ : "$11" \
+ ); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_T9 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir *%t9 */ \
+ "or $15, $15, $15\n\t"
+
+#define VALGRIND_VEX_INJECT_IR() \
+ do { \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ "or $11, $11, $11\n\t" \
+ ); \
+ } while (0)
+
+
+#endif /* PLAT_mips32_linux */
+
+/* ------------------------- mips64-linux ---------------- */
+
+#if defined(PLAT_mips64_linux)
+
+typedef
+ struct {
+ unsigned long long nraddr; /* where's the code? */
+ }
+ OrigFn;
+
+/* dsll $0,$0, 3
+ * dsll $0,$0, 13
+ * dsll $0,$0, 29
+ * dsll $0,$0, 19*/
+#define __SPECIAL_INSTRUCTION_PREAMBLE \
+ "dsll $0,$0, 3 ; dsll $0,$0,13\n\t" \
+ "dsll $0,$0,29 ; dsll $0,$0,19\n\t"
+
+#define VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ _zzq_default, _zzq_request, \
+ _zzq_arg1, _zzq_arg2, _zzq_arg3, _zzq_arg4, _zzq_arg5) \
+ __extension__ \
+ ({ volatile unsigned long long int _zzq_args[6]; \
+ volatile unsigned long long int _zzq_result; \
+ _zzq_args[0] = (unsigned long long int)(_zzq_request); \
+ _zzq_args[1] = (unsigned long long int)(_zzq_arg1); \
+ _zzq_args[2] = (unsigned long long int)(_zzq_arg2); \
+ _zzq_args[3] = (unsigned long long int)(_zzq_arg3); \
+ _zzq_args[4] = (unsigned long long int)(_zzq_arg4); \
+ _zzq_args[5] = (unsigned long long int)(_zzq_arg5); \
+ __asm__ volatile("move $11, %1\n\t" /*default*/ \
+ "move $12, %2\n\t" /*ptr*/ \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* $11 = client_request ( $12 ) */ \
+ "or $13, $13, $13\n\t" \
+ "move %0, $11\n\t" /*result*/ \
+ : "=r" (_zzq_result) \
+ : "r" (_zzq_default), "r" (&_zzq_args[0]) \
+ : "$11", "$12"); \
+ _zzq_result; \
+ })
+
+#define VALGRIND_GET_NR_CONTEXT(_zzq_rlval) \
+ { volatile OrigFn* _zzq_orig = &(_zzq_rlval); \
+ volatile unsigned long long int __addr; \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ /* $11 = guest_NRADDR */ \
+ "or $14, $14, $14\n\t" \
+ "move %0, $11" /*result*/ \
+ : "=r" (__addr) \
+ : \
+ : "$11"); \
+ _zzq_orig->nraddr = __addr; \
+ }
+
+#define VALGRIND_CALL_NOREDIR_T9 \
+ __SPECIAL_INSTRUCTION_PREAMBLE \
+ /* call-noredir $25 */ \
+ "or $15, $15, $15\n\t"
+
+#define VALGRIND_VEX_INJECT_IR() \
+ do { \
+ __asm__ volatile(__SPECIAL_INSTRUCTION_PREAMBLE \
+ "or $11, $11, $11\n\t" \
+ ); \
+ } while (0)
+
+#endif /* PLAT_mips64_linux */
+
+/* Insert assembly code for other platforms here... */
+
+#endif /* NVALGRIND */
+
+
+/* ------------------------------------------------------------------ */
+/* PLATFORM SPECIFICS for FUNCTION WRAPPING. This is all very */
+/* ugly. It's the least-worst tradeoff I can think of. */
+/* ------------------------------------------------------------------ */
+
+/* This section defines magic (a.k.a appalling-hack) macros for doing
+ guaranteed-no-redirection macros, so as to get from function
+ wrappers to the functions they are wrapping. The whole point is to
+ construct standard call sequences, but to do the call itself with a
+ special no-redirect call pseudo-instruction that the JIT
+ understands and handles specially. This section is long and
+ repetitious, and I can't see a way to make it shorter.
+
+ The naming scheme is as follows:
+
+ CALL_FN_{W,v}_{v,W,WW,WWW,WWWW,5W,6W,7W,etc}
+
+ 'W' stands for "word" and 'v' for "void". Hence there are
+ different macros for calling arity 0, 1, 2, 3, 4, etc, functions,
+ and for each, the possibility of returning a word-typed result, or
+ no result.
+*/
+
+/* Use these to write the name of your wrapper. NOTE: duplicates
+ VG_WRAP_FUNCTION_Z{U,Z} in pub_tool_redir.h. NOTE also: inserts
+ the default behaviour equivalance class tag "0000" into the name.
+ See pub_tool_redir.h for details -- normally you don't need to
+ think about this, though. */
+
+/* Use an extra level of macroisation so as to ensure the soname/fnname
+ args are fully macro-expanded before pasting them together. */
+#define VG_CONCAT4(_aa,_bb,_cc,_dd) _aa##_bb##_cc##_dd
+
+#define I_WRAP_SONAME_FNNAME_ZU(soname,fnname) \
+ VG_CONCAT4(_vgw00000ZU_,soname,_,fnname)
+
+#define I_WRAP_SONAME_FNNAME_ZZ(soname,fnname) \
+ VG_CONCAT4(_vgw00000ZZ_,soname,_,fnname)
+
+/* Use this macro from within a wrapper function to collect the
+ context (address and possibly other info) of the original function.
+ Once you have that you can then use it in one of the CALL_FN_
+ macros. The type of the argument _lval is OrigFn. */
+#define VALGRIND_GET_ORIG_FN(_lval) VALGRIND_GET_NR_CONTEXT(_lval)
+
+/* Also provide end-user facilities for function replacement, rather
+ than wrapping. A replacement function differs from a wrapper in
+ that it has no way to get hold of the original function being
+ called, and hence no way to call onwards to it. In a replacement
+ function, VALGRIND_GET_ORIG_FN always returns zero. */
+
+#define I_REPLACE_SONAME_FNNAME_ZU(soname,fnname) \
+ VG_CONCAT4(_vgr00000ZU_,soname,_,fnname)
+
+#define I_REPLACE_SONAME_FNNAME_ZZ(soname,fnname) \
+ VG_CONCAT4(_vgr00000ZZ_,soname,_,fnname)
+
+/* Derivatives of the main macros below, for calling functions
+ returning void. */
+
+#define CALL_FN_v_v(fnptr) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_v(_junk,fnptr); } while (0)
+
+#define CALL_FN_v_W(fnptr, arg1) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_W(_junk,fnptr,arg1); } while (0)
+
+#define CALL_FN_v_WW(fnptr, arg1,arg2) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WW(_junk,fnptr,arg1,arg2); } while (0)
+
+#define CALL_FN_v_WWW(fnptr, arg1,arg2,arg3) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WWW(_junk,fnptr,arg1,arg2,arg3); } while (0)
+
+#define CALL_FN_v_WWWW(fnptr, arg1,arg2,arg3,arg4) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_WWWW(_junk,fnptr,arg1,arg2,arg3,arg4); } while (0)
+
+#define CALL_FN_v_5W(fnptr, arg1,arg2,arg3,arg4,arg5) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_5W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5); } while (0)
+
+#define CALL_FN_v_6W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_6W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6); } while (0)
+
+#define CALL_FN_v_7W(fnptr, arg1,arg2,arg3,arg4,arg5,arg6,arg7) \
+ do { volatile unsigned long _junk; \
+ CALL_FN_W_7W(_junk,fnptr,arg1,arg2,arg3,arg4,arg5,arg6,arg7); } while (0)
+
+/* ------------------------- x86-{linux,darwin} ---------------- */
+
+#if defined(PLAT_x86_linux) || defined(PLAT_x86_darwin)
+
+/* These regs are trashed by the hidden call. No need to mention eax
+ as gcc can already see that, plus causes gcc to bomb. */
+#define __CALLER_SAVED_REGS /*"eax"*/ "ecx", "edx"
+
+/* Macros to save and align the stack before making a function
+ call and restore it afterwards as gcc may not keep the stack
+ pointer aligned if it doesn't realise calls are being made
+ to other functions. */
+
+#define VALGRIND_ALIGN_STACK \
+ "movl %%esp,%%edi\n\t" \
+ "andl $0xfffffff0,%%esp\n\t"
+#define VALGRIND_RESTORE_STACK \
+ "movl %%edi,%%esp\n\t"
+
+/* These CALL_FN_ macros assume that on x86-linux, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "subl $12, %%esp\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "subl $8, %%esp\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "subl $4, %%esp\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "subl $12, %%esp\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "subl $8, %%esp\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "subl $4, %%esp\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "subl $12, %%esp\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "subl $8, %%esp\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "subl $4, %%esp\n\t" \
+ "pushl 44(%%eax)\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "pushl 48(%%eax)\n\t" \
+ "pushl 44(%%eax)\n\t" \
+ "pushl 40(%%eax)\n\t" \
+ "pushl 36(%%eax)\n\t" \
+ "pushl 32(%%eax)\n\t" \
+ "pushl 28(%%eax)\n\t" \
+ "pushl 24(%%eax)\n\t" \
+ "pushl 20(%%eax)\n\t" \
+ "pushl 16(%%eax)\n\t" \
+ "pushl 12(%%eax)\n\t" \
+ "pushl 8(%%eax)\n\t" \
+ "pushl 4(%%eax)\n\t" \
+ "movl (%%eax), %%eax\n\t" /* target->%eax */ \
+ VALGRIND_CALL_NOREDIR_EAX \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "edi" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_x86_linux || PLAT_x86_darwin */
+
+/* ------------------------ amd64-{linux,darwin} --------------- */
+
+#if defined(PLAT_amd64_linux) || defined(PLAT_amd64_darwin)
+
+/* ARGREGS: rdi rsi rdx rcx r8 r9 (the rest on stack in R-to-L order) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS /*"rax",*/ "rcx", "rdx", "rsi", \
+ "rdi", "r8", "r9", "r10", "r11"
+
+/* This is all pretty complex. It's so as to make stack unwinding
+ work reliably. See bug 243270. The basic problem is the sub and
+ add of 128 of %rsp in all of the following macros. If gcc believes
+ the CFA is in %rsp, then unwinding may fail, because what's at the
+ CFA is not what gcc "expected" when it constructs the CFIs for the
+ places where the macros are instantiated.
+
+ But we can't just add a CFI annotation to increase the CFA offset
+ by 128, to match the sub of 128 from %rsp, because we don't know
+ whether gcc has chosen %rsp as the CFA at that point, or whether it
+ has chosen some other register (eg, %rbp). In the latter case,
+ adding a CFI annotation to change the CFA offset is simply wrong.
+
+ So the solution is to get hold of the CFA using
+ __builtin_dwarf_cfa(), put it in a known register, and add a
+ CFI annotation to say what the register is. We choose %rbp for
+ this (perhaps perversely), because:
+
+ (1) %rbp is already subject to unwinding. If a new register was
+ chosen then the unwinder would have to unwind it in all stack
+ traces, which is expensive, and
+
+ (2) %rbp is already subject to precise exception updates in the
+ JIT. If a new register was chosen, we'd have to have precise
+ exceptions for it too, which reduces performance of the
+ generated code.
+
+ However .. one extra complication. We can't just whack the result
+ of __builtin_dwarf_cfa() into %rbp and then add %rbp to the
+ list of trashed registers at the end of the inline assembly
+ fragments; gcc won't allow %rbp to appear in that list. Hence
+ instead we need to stash %rbp in %r15 for the duration of the asm,
+ and say that %r15 is trashed instead. gcc seems happy to go with
+ that.
+
+ Oh .. and this all needs to be conditionalised so that it is
+ unchanged from before this commit, when compiled with older gccs
+ that don't support __builtin_dwarf_cfa. Furthermore, since
+ this header file is freestanding, it has to be independent of
+ config.h, and so the following conditionalisation cannot depend on
+ configure time checks.
+
+ Although it's not clear from
+ 'defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)',
+ this expression excludes Darwin.
+ .cfi directives in Darwin assembly appear to be completely
+ different and I haven't investigated how they work.
+
+ For even more entertainment value, note we have to use the
+ completely undocumented __builtin_dwarf_cfa(), which appears to
+ really compute the CFA, whereas __builtin_frame_address(0) claims
+ to but actually doesn't. See
+ https://bugs.kde.org/show_bug.cgi?id=243270#c47
+*/
+#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)
+# define __FRAME_POINTER \
+ ,"r"(__builtin_dwarf_cfa())
+# define VALGRIND_CFI_PROLOGUE \
+ "movq %%rbp, %%r15\n\t" \
+ "movq %2, %%rbp\n\t" \
+ ".cfi_remember_state\n\t" \
+ ".cfi_def_cfa rbp, 0\n\t"
+# define VALGRIND_CFI_EPILOGUE \
+ "movq %%r15, %%rbp\n\t" \
+ ".cfi_restore_state\n\t"
+#else
+# define __FRAME_POINTER
+# define VALGRIND_CFI_PROLOGUE
+# define VALGRIND_CFI_EPILOGUE
+#endif
+
+/* Macros to save and align the stack before making a function
+ call and restore it afterwards as gcc may not keep the stack
+ pointer aligned if it doesn't realise calls are being made
+ to other functions. */
+
+#define VALGRIND_ALIGN_STACK \
+ "movq %%rsp,%%r14\n\t" \
+ "andq $0xfffffffffffffff0,%%rsp\n\t"
+#define VALGRIND_RESTORE_STACK \
+ "movq %%r14,%%rsp\n\t"
+
+/* These CALL_FN_ macros assume that on amd64-linux, sizeof(unsigned
+ long) == 8. */
+
+/* NB 9 Sept 07. There is a nasty kludge here in all these CALL_FN_
+ macros. In order not to trash the stack redzone, we need to drop
+ %rsp by 128 before the hidden call, and restore afterwards. The
+ nastyness is that it is only by luck that the stack still appears
+ to be unwindable during the hidden call - since then the behaviour
+ of any routine using this macro does not match what the CFI data
+ says. Sigh.
+
+ Why is this important? Imagine that a wrapper has a stack
+ allocated local, and passes to the hidden call, a pointer to it.
+ Because gcc does not know about the hidden call, it may allocate
+ that local in the redzone. Unfortunately the hidden call may then
+ trash it before it comes to use it. So we must step clear of the
+ redzone, for the duration of the hidden call, to make it safe.
+
+ Probably the same problem afflicts the other redzone-style ABIs too
+ (ppc64-linux); but for those, the stack is
+ self describing (none of this CFI nonsense) so at least messing
+ with the stack pointer doesn't give a danger of non-unwindable
+ stack. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $128,%%rsp\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $128,%%rsp\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $128,%%rsp\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $128,%%rsp\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $128,%%rsp\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $128,%%rsp\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $128,%%rsp\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $136,%%rsp\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $128,%%rsp\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $136,%%rsp\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $128,%%rsp\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $136,%%rsp\n\t" \
+ "pushq 88(%%rax)\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ VALGRIND_ALIGN_STACK \
+ "subq $128,%%rsp\n\t" \
+ "pushq 96(%%rax)\n\t" \
+ "pushq 88(%%rax)\n\t" \
+ "pushq 80(%%rax)\n\t" \
+ "pushq 72(%%rax)\n\t" \
+ "pushq 64(%%rax)\n\t" \
+ "pushq 56(%%rax)\n\t" \
+ "movq 48(%%rax), %%r9\n\t" \
+ "movq 40(%%rax), %%r8\n\t" \
+ "movq 32(%%rax), %%rcx\n\t" \
+ "movq 24(%%rax), %%rdx\n\t" \
+ "movq 16(%%rax), %%rsi\n\t" \
+ "movq 8(%%rax), %%rdi\n\t" \
+ "movq (%%rax), %%rax\n\t" /* target->%rax */ \
+ VALGRIND_CALL_NOREDIR_RAX \
+ VALGRIND_RESTORE_STACK \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=a" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r14", "r15" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_amd64_linux || PLAT_amd64_darwin */
+
+/* ------------------------ ppc32-linux ------------------------ */
+
+#if defined(PLAT_ppc32_linux)
+
+/* This is useful for finding out about the on-stack stuff:
+
+ extern int f9 ( int,int,int,int,int,int,int,int,int );
+ extern int f10 ( int,int,int,int,int,int,int,int,int,int );
+ extern int f11 ( int,int,int,int,int,int,int,int,int,int,int );
+ extern int f12 ( int,int,int,int,int,int,int,int,int,int,int,int );
+
+ int g9 ( void ) {
+ return f9(11,22,33,44,55,66,77,88,99);
+ }
+ int g10 ( void ) {
+ return f10(11,22,33,44,55,66,77,88,99,110);
+ }
+ int g11 ( void ) {
+ return f11(11,22,33,44,55,66,77,88,99,110,121);
+ }
+ int g12 ( void ) {
+ return f12(11,22,33,44,55,66,77,88,99,110,121,132);
+ }
+*/
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* Macros to save and align the stack before making a function
+ call and restore it afterwards as gcc may not keep the stack
+ pointer aligned if it doesn't realise calls are being made
+ to other functions. */
+
+#define VALGRIND_ALIGN_STACK \
+ "mr 28,1\n\t" \
+ "rlwinm 1,1,0,0,27\n\t"
+#define VALGRIND_RESTORE_STACK \
+ "mr 1,28\n\t"
+
+/* These CALL_FN_ macros assume that on ppc32-linux,
+ sizeof(unsigned long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-16\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-16\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-32\n\t" \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,16(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ _argvec[12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "addi 1,1,-32\n\t" \
+ /* arg12 */ \
+ "lwz 3,48(11)\n\t" \
+ "stw 3,20(1)\n\t" \
+ /* arg11 */ \
+ "lwz 3,44(11)\n\t" \
+ "stw 3,16(1)\n\t" \
+ /* arg10 */ \
+ "lwz 3,40(11)\n\t" \
+ "stw 3,12(1)\n\t" \
+ /* arg9 */ \
+ "lwz 3,36(11)\n\t" \
+ "stw 3,8(1)\n\t" \
+ /* args1-8 */ \
+ "lwz 3,4(11)\n\t" /* arg1->r3 */ \
+ "lwz 4,8(11)\n\t" \
+ "lwz 5,12(11)\n\t" \
+ "lwz 6,16(11)\n\t" /* arg4->r6 */ \
+ "lwz 7,20(11)\n\t" \
+ "lwz 8,24(11)\n\t" \
+ "lwz 9,28(11)\n\t" \
+ "lwz 10,32(11)\n\t" /* arg8->r10 */ \
+ "lwz 11,0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ VALGRIND_RESTORE_STACK \
+ "mr %0,3" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc32_linux */
+
+/* ------------------------ ppc64-linux ------------------------ */
+
+#if defined(PLAT_ppc64_linux)
+
+/* ARGREGS: r3 r4 r5 r6 r7 r8 r9 r10 (the rest on stack somewhere) */
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS \
+ "lr", "ctr", "xer", \
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", \
+ "r0", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", \
+ "r11", "r12", "r13"
+
+/* Macros to save and align the stack before making a function
+ call and restore it afterwards as gcc may not keep the stack
+ pointer aligned if it doesn't realise calls are being made
+ to other functions. */
+
+#define VALGRIND_ALIGN_STACK \
+ "mr 28,1\n\t" \
+ "rldicr 1,1,0,59\n\t"
+#define VALGRIND_RESTORE_STACK \
+ "mr 1,28\n\t"
+
+/* These CALL_FN_ macros assume that on ppc64-linux, sizeof(unsigned
+ long) == 8. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+0]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+1]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+2]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+3]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+4]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+5]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+6]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+7]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+8]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+9]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-128\n\t" /* expand stack frame */ \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+10]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-128\n\t" /* expand stack frame */ \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+11]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-144\n\t" /* expand stack frame */ \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10,arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3+12]; \
+ volatile unsigned long _res; \
+ /* _argvec[0] holds current r2 across the call */ \
+ _argvec[1] = (unsigned long)_orig.r2; \
+ _argvec[2] = (unsigned long)_orig.nraddr; \
+ _argvec[2+1] = (unsigned long)arg1; \
+ _argvec[2+2] = (unsigned long)arg2; \
+ _argvec[2+3] = (unsigned long)arg3; \
+ _argvec[2+4] = (unsigned long)arg4; \
+ _argvec[2+5] = (unsigned long)arg5; \
+ _argvec[2+6] = (unsigned long)arg6; \
+ _argvec[2+7] = (unsigned long)arg7; \
+ _argvec[2+8] = (unsigned long)arg8; \
+ _argvec[2+9] = (unsigned long)arg9; \
+ _argvec[2+10] = (unsigned long)arg10; \
+ _argvec[2+11] = (unsigned long)arg11; \
+ _argvec[2+12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "mr 11,%1\n\t" \
+ "std 2,-16(11)\n\t" /* save tocptr */ \
+ "ld 2,-8(11)\n\t" /* use nraddr's tocptr */ \
+ "addi 1,1,-144\n\t" /* expand stack frame */ \
+ /* arg12 */ \
+ "ld 3,96(11)\n\t" \
+ "std 3,136(1)\n\t" \
+ /* arg11 */ \
+ "ld 3,88(11)\n\t" \
+ "std 3,128(1)\n\t" \
+ /* arg10 */ \
+ "ld 3,80(11)\n\t" \
+ "std 3,120(1)\n\t" \
+ /* arg9 */ \
+ "ld 3,72(11)\n\t" \
+ "std 3,112(1)\n\t" \
+ /* args1-8 */ \
+ "ld 3, 8(11)\n\t" /* arg1->r3 */ \
+ "ld 4, 16(11)\n\t" /* arg2->r4 */ \
+ "ld 5, 24(11)\n\t" /* arg3->r5 */ \
+ "ld 6, 32(11)\n\t" /* arg4->r6 */ \
+ "ld 7, 40(11)\n\t" /* arg5->r7 */ \
+ "ld 8, 48(11)\n\t" /* arg6->r8 */ \
+ "ld 9, 56(11)\n\t" /* arg7->r9 */ \
+ "ld 10, 64(11)\n\t" /* arg8->r10 */ \
+ "ld 11, 0(11)\n\t" /* target->r11 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R11 \
+ "mr 11,%1\n\t" \
+ "mr %0,3\n\t" \
+ "ld 2,-16(11)\n\t" /* restore tocptr */ \
+ VALGRIND_RESTORE_STACK \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[2]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r28" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_ppc64_linux */
+
+/* ------------------------- arm-linux ------------------------- */
+
+#if defined(PLAT_arm_linux)
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS "r0", "r1", "r2", "r3","r4","r14"
+
+/* Macros to save and align the stack before making a function
+ call and restore it afterwards as gcc may not keep the stack
+ pointer aligned if it doesn't realise calls are being made
+ to other functions. */
+
+/* This is a bit tricky. We store the original stack pointer in r10
+ as it is callee-saves. gcc doesn't allow the use of r11 for some
+ reason. Also, we can't directly "bic" the stack pointer in thumb
+ mode since r13 isn't an allowed register number in that context.
+ So use r4 as a temporary, since that is about to get trashed
+ anyway, just after each use of this macro. Side effect is we need
+ to be very careful about any future changes, since
+ VALGRIND_ALIGN_STACK simply assumes r4 is usable. */
+#define VALGRIND_ALIGN_STACK \
+ "mov r10, sp\n\t" \
+ "mov r4, sp\n\t" \
+ "bic r4, r4, #7\n\t" \
+ "mov sp, r4\n\t"
+#define VALGRIND_RESTORE_STACK \
+ "mov sp, r10\n\t"
+
+/* These CALL_FN_ macros assume that on arm-linux, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "sub sp, sp, #4 \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "push {r0} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "push {r0, r1} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "sub sp, sp, #4 \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "push {r0, r1, r2} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "push {r0, r1, r2, r3} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "sub sp, sp, #4 \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "ldr r0, [%1, #40] \n\t" \
+ "push {r0} \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "sub sp, sp, #4 \n\t" \
+ "ldr r0, [%1, #40] \n\t" \
+ "ldr r1, [%1, #44] \n\t" \
+ "push {r0, r1} \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ VALGRIND_ALIGN_STACK \
+ "ldr r0, [%1, #40] \n\t" \
+ "ldr r1, [%1, #44] \n\t" \
+ "ldr r2, [%1, #48] \n\t" \
+ "push {r0, r1, r2} \n\t" \
+ "ldr r0, [%1, #20] \n\t" \
+ "ldr r1, [%1, #24] \n\t" \
+ "ldr r2, [%1, #28] \n\t" \
+ "ldr r3, [%1, #32] \n\t" \
+ "ldr r4, [%1, #36] \n\t" \
+ "push {r0, r1, r2, r3, r4} \n\t" \
+ "ldr r0, [%1, #4] \n\t" \
+ "ldr r1, [%1, #8] \n\t" \
+ "ldr r2, [%1, #12] \n\t" \
+ "ldr r3, [%1, #16] \n\t" \
+ "ldr r4, [%1] \n\t" /* target->r4 */ \
+ VALGRIND_BRANCH_AND_LINK_TO_NOREDIR_R4 \
+ VALGRIND_RESTORE_STACK \
+ "mov %0, r0" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS, "r10" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_arm_linux */
+
+/* ------------------------- s390x-linux ------------------------- */
+
+#if defined(PLAT_s390x_linux)
+
+/* Similar workaround as amd64 (see above), but we use r11 as frame
+ pointer and save the old r11 in r7. r11 might be used for
+ argvec, therefore we copy argvec in r1 since r1 is clobbered
+ after the call anyway. */
+#if defined(__GNUC__) && defined(__GCC_HAVE_DWARF2_CFI_ASM)
+# define __FRAME_POINTER \
+ ,"d"(__builtin_dwarf_cfa())
+# define VALGRIND_CFI_PROLOGUE \
+ ".cfi_remember_state\n\t" \
+ "lgr 1,%1\n\t" /* copy the argvec pointer in r1 */ \
+ "lgr 7,11\n\t" \
+ "lgr 11,%2\n\t" \
+ ".cfi_def_cfa r11, 0\n\t"
+# define VALGRIND_CFI_EPILOGUE \
+ "lgr 11, 7\n\t" \
+ ".cfi_restore_state\n\t"
+#else
+# define __FRAME_POINTER
+# define VALGRIND_CFI_PROLOGUE \
+ "lgr 1,%1\n\t"
+# define VALGRIND_CFI_EPILOGUE
+#endif
+
+/* Nb: On s390 the stack pointer is properly aligned *at all times*
+ according to the s390 GCC maintainer. (The ABI specification is not
+ precise in this regard.) Therefore, VALGRIND_ALIGN_STACK and
+ VALGRIND_RESTORE_STACK are not defined here. */
+
+/* These regs are trashed by the hidden call. Note that we overwrite
+ r14 in s390_irgen_noredir (VEX/priv/guest_s390_irgen.c) to give the
+ function a proper return address. All others are ABI defined call
+ clobbers. */
+#define __CALLER_SAVED_REGS "0","1","2","3","4","5","14", \
+ "f0","f1","f2","f3","f4","f5","f6","f7"
+
+/* Nb: Although r11 is modified in the asm snippets below (inside
+ VALGRIND_CFI_PROLOGUE) it is not listed in the clobber section, for
+ two reasons:
+ (1) r11 is restored in VALGRIND_CFI_EPILOGUE, so effectively it is not
+ modified
+ (2) GCC will complain that r11 cannot appear inside a clobber section,
+ when compiled with -O -fno-omit-frame-pointer
+ */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 1, 0(1)\n\t" /* target->r1 */ \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "d" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+/* The call abi has the arguments in r2-r6 and stack */
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1, arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1, arg2, arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1, arg2, arg3, arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1, arg2, arg3, arg4, arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-160\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,160\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-168\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,168\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-176\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,176\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7 ,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-184\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "mvc 176(8,15), 64(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,184\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7 ,arg8, arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-192\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "mvc 176(8,15), 64(1)\n\t" \
+ "mvc 184(8,15), 72(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,192\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7 ,arg8, arg9, arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-200\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "mvc 176(8,15), 64(1)\n\t" \
+ "mvc 184(8,15), 72(1)\n\t" \
+ "mvc 192(8,15), 80(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,200\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7 ,arg8, arg9, arg10, arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-208\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "mvc 176(8,15), 64(1)\n\t" \
+ "mvc 184(8,15), 72(1)\n\t" \
+ "mvc 192(8,15), 80(1)\n\t" \
+ "mvc 200(8,15), 88(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,208\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1, arg2, arg3, arg4, arg5, \
+ arg6, arg7 ,arg8, arg9, arg10, arg11, arg12)\
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)arg1; \
+ _argvec[2] = (unsigned long)arg2; \
+ _argvec[3] = (unsigned long)arg3; \
+ _argvec[4] = (unsigned long)arg4; \
+ _argvec[5] = (unsigned long)arg5; \
+ _argvec[6] = (unsigned long)arg6; \
+ _argvec[7] = (unsigned long)arg7; \
+ _argvec[8] = (unsigned long)arg8; \
+ _argvec[9] = (unsigned long)arg9; \
+ _argvec[10] = (unsigned long)arg10; \
+ _argvec[11] = (unsigned long)arg11; \
+ _argvec[12] = (unsigned long)arg12; \
+ __asm__ volatile( \
+ VALGRIND_CFI_PROLOGUE \
+ "aghi 15,-216\n\t" \
+ "lg 2, 8(1)\n\t" \
+ "lg 3,16(1)\n\t" \
+ "lg 4,24(1)\n\t" \
+ "lg 5,32(1)\n\t" \
+ "lg 6,40(1)\n\t" \
+ "mvc 160(8,15), 48(1)\n\t" \
+ "mvc 168(8,15), 56(1)\n\t" \
+ "mvc 176(8,15), 64(1)\n\t" \
+ "mvc 184(8,15), 72(1)\n\t" \
+ "mvc 192(8,15), 80(1)\n\t" \
+ "mvc 200(8,15), 88(1)\n\t" \
+ "mvc 208(8,15), 96(1)\n\t" \
+ "lg 1, 0(1)\n\t" \
+ VALGRIND_CALL_NOREDIR_R1 \
+ "lgr %0, 2\n\t" \
+ "aghi 15,216\n\t" \
+ VALGRIND_CFI_EPILOGUE \
+ : /*out*/ "=d" (_res) \
+ : /*in*/ "a" (&_argvec[0]) __FRAME_POINTER \
+ : /*trash*/ "cc", "memory", __CALLER_SAVED_REGS,"6","7" \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+
+#endif /* PLAT_s390x_linux */
+
+/* ------------------------- mips32-linux ----------------------- */
+
+#if defined(PLAT_mips32_linux)
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \
+"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \
+"$25", "$31"
+
+/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "subu $29, $29, 16 \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 16\n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "subu $29, $29, 16 \n\t" \
+ "lw $4, 4(%1) \n\t" /* arg1*/ \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 16 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "subu $29, $29, 16 \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 16 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "subu $29, $29, 16 \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $6, 12(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 16 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "subu $29, $29, 16 \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $6, 12(%1) \n\t" \
+ "lw $7, 16(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 16 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "lw $4, 20(%1) \n\t" \
+ "subu $29, $29, 24\n\t" \
+ "sw $4, 16($29) \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $6, 12(%1) \n\t" \
+ "lw $7, 16(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 24 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "lw $4, 20(%1) \n\t" \
+ "subu $29, $29, 32\n\t" \
+ "sw $4, 16($29) \n\t" \
+ "lw $4, 24(%1) \n\t" \
+ "nop\n\t" \
+ "sw $4, 20($29) \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $6, 12(%1) \n\t" \
+ "lw $7, 16(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 32 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "lw $4, 20(%1) \n\t" \
+ "subu $29, $29, 32\n\t" \
+ "sw $4, 16($29) \n\t" \
+ "lw $4, 24(%1) \n\t" \
+ "sw $4, 20($29) \n\t" \
+ "lw $4, 28(%1) \n\t" \
+ "sw $4, 24($29) \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $6, 12(%1) \n\t" \
+ "lw $7, 16(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 32 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "lw $4, 20(%1) \n\t" \
+ "subu $29, $29, 40\n\t" \
+ "sw $4, 16($29) \n\t" \
+ "lw $4, 24(%1) \n\t" \
+ "sw $4, 20($29) \n\t" \
+ "lw $4, 28(%1) \n\t" \
+ "sw $4, 24($29) \n\t" \
+ "lw $4, 32(%1) \n\t" \
+ "sw $4, 28($29) \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $6, 12(%1) \n\t" \
+ "lw $7, 16(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 40 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "lw $4, 20(%1) \n\t" \
+ "subu $29, $29, 40\n\t" \
+ "sw $4, 16($29) \n\t" \
+ "lw $4, 24(%1) \n\t" \
+ "sw $4, 20($29) \n\t" \
+ "lw $4, 28(%1) \n\t" \
+ "sw $4, 24($29) \n\t" \
+ "lw $4, 32(%1) \n\t" \
+ "sw $4, 28($29) \n\t" \
+ "lw $4, 36(%1) \n\t" \
+ "sw $4, 32($29) \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $6, 12(%1) \n\t" \
+ "lw $7, 16(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 40 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "lw $4, 20(%1) \n\t" \
+ "subu $29, $29, 48\n\t" \
+ "sw $4, 16($29) \n\t" \
+ "lw $4, 24(%1) \n\t" \
+ "sw $4, 20($29) \n\t" \
+ "lw $4, 28(%1) \n\t" \
+ "sw $4, 24($29) \n\t" \
+ "lw $4, 32(%1) \n\t" \
+ "sw $4, 28($29) \n\t" \
+ "lw $4, 36(%1) \n\t" \
+ "sw $4, 32($29) \n\t" \
+ "lw $4, 40(%1) \n\t" \
+ "sw $4, 36($29) \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $6, 12(%1) \n\t" \
+ "lw $7, 16(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 48 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "lw $4, 20(%1) \n\t" \
+ "subu $29, $29, 48\n\t" \
+ "sw $4, 16($29) \n\t" \
+ "lw $4, 24(%1) \n\t" \
+ "sw $4, 20($29) \n\t" \
+ "lw $4, 28(%1) \n\t" \
+ "sw $4, 24($29) \n\t" \
+ "lw $4, 32(%1) \n\t" \
+ "sw $4, 28($29) \n\t" \
+ "lw $4, 36(%1) \n\t" \
+ "sw $4, 32($29) \n\t" \
+ "lw $4, 40(%1) \n\t" \
+ "sw $4, 36($29) \n\t" \
+ "lw $4, 44(%1) \n\t" \
+ "sw $4, 40($29) \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $6, 12(%1) \n\t" \
+ "lw $7, 16(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 48 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ "subu $29, $29, 8 \n\t" \
+ "sw $28, 0($29) \n\t" \
+ "sw $31, 4($29) \n\t" \
+ "lw $4, 20(%1) \n\t" \
+ "subu $29, $29, 56\n\t" \
+ "sw $4, 16($29) \n\t" \
+ "lw $4, 24(%1) \n\t" \
+ "sw $4, 20($29) \n\t" \
+ "lw $4, 28(%1) \n\t" \
+ "sw $4, 24($29) \n\t" \
+ "lw $4, 32(%1) \n\t" \
+ "sw $4, 28($29) \n\t" \
+ "lw $4, 36(%1) \n\t" \
+ "sw $4, 32($29) \n\t" \
+ "lw $4, 40(%1) \n\t" \
+ "sw $4, 36($29) \n\t" \
+ "lw $4, 44(%1) \n\t" \
+ "sw $4, 40($29) \n\t" \
+ "lw $4, 48(%1) \n\t" \
+ "sw $4, 44($29) \n\t" \
+ "lw $4, 4(%1) \n\t" \
+ "lw $5, 8(%1) \n\t" \
+ "lw $6, 12(%1) \n\t" \
+ "lw $7, 16(%1) \n\t" \
+ "lw $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "addu $29, $29, 56 \n\t" \
+ "lw $28, 0($29) \n\t" \
+ "lw $31, 4($29) \n\t" \
+ "addu $29, $29, 8 \n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_mips32_linux */
+
+/* ------------------------- mips64-linux ------------------------- */
+
+#if defined(PLAT_mips64_linux)
+
+/* These regs are trashed by the hidden call. */
+#define __CALLER_SAVED_REGS "$2", "$3", "$4", "$5", "$6", \
+"$7", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", \
+"$25", "$31"
+
+/* These CALL_FN_ macros assume that on mips-linux, sizeof(unsigned
+ long) == 4. */
+
+#define CALL_FN_W_v(lval, orig) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[1]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ __asm__ volatile( \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "0" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_W(lval, orig, arg1) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[2]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ __asm__ volatile( \
+ "ld $4, 8(%1)\n\t" /* arg1*/ \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WW(lval, orig, arg1,arg2) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[3]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ __asm__ volatile( \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWW(lval, orig, arg1,arg2,arg3) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[4]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ __asm__ volatile( \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $6, 24(%1)\n\t" \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_WWWW(lval, orig, arg1,arg2,arg3,arg4) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[5]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ __asm__ volatile( \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $6, 24(%1)\n\t" \
+ "ld $7, 32(%1)\n\t" \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_5W(lval, orig, arg1,arg2,arg3,arg4,arg5) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[6]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ __asm__ volatile( \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $6, 24(%1)\n\t" \
+ "ld $7, 32(%1)\n\t" \
+ "ld $8, 40(%1)\n\t" \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_6W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[7]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ __asm__ volatile( \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $6, 24(%1)\n\t" \
+ "ld $7, 32(%1)\n\t" \
+ "ld $8, 40(%1)\n\t" \
+ "ld $9, 48(%1)\n\t" \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_7W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[8]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ __asm__ volatile( \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $6, 24(%1)\n\t" \
+ "ld $7, 32(%1)\n\t" \
+ "ld $8, 40(%1)\n\t" \
+ "ld $9, 48(%1)\n\t" \
+ "ld $10, 56(%1)\n\t" \
+ "ld $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_8W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[9]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ __asm__ volatile( \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $6, 24(%1)\n\t" \
+ "ld $7, 32(%1)\n\t" \
+ "ld $8, 40(%1)\n\t" \
+ "ld $9, 48(%1)\n\t" \
+ "ld $10, 56(%1)\n\t" \
+ "ld $11, 64(%1)\n\t" \
+ "ld $25, 0(%1) \n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_9W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[10]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ __asm__ volatile( \
+ "dsubu $29, $29, 8\n\t" \
+ "ld $4, 72(%1)\n\t" \
+ "sd $4, 0($29)\n\t" \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $6, 24(%1)\n\t" \
+ "ld $7, 32(%1)\n\t" \
+ "ld $8, 40(%1)\n\t" \
+ "ld $9, 48(%1)\n\t" \
+ "ld $10, 56(%1)\n\t" \
+ "ld $11, 64(%1)\n\t" \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "daddu $29, $29, 8\n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_10W(lval, orig, arg1,arg2,arg3,arg4,arg5,arg6, \
+ arg7,arg8,arg9,arg10) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[11]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ __asm__ volatile( \
+ "dsubu $29, $29, 16\n\t" \
+ "ld $4, 72(%1)\n\t" \
+ "sd $4, 0($29)\n\t" \
+ "ld $4, 80(%1)\n\t" \
+ "sd $4, 8($29)\n\t" \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $6, 24(%1)\n\t" \
+ "ld $7, 32(%1)\n\t" \
+ "ld $8, 40(%1)\n\t" \
+ "ld $9, 48(%1)\n\t" \
+ "ld $10, 56(%1)\n\t" \
+ "ld $11, 64(%1)\n\t" \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "daddu $29, $29, 16\n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_11W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[12]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ __asm__ volatile( \
+ "dsubu $29, $29, 24\n\t" \
+ "ld $4, 72(%1)\n\t" \
+ "sd $4, 0($29)\n\t" \
+ "ld $4, 80(%1)\n\t" \
+ "sd $4, 8($29)\n\t" \
+ "ld $4, 88(%1)\n\t" \
+ "sd $4, 16($29)\n\t" \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $6, 24(%1)\n\t" \
+ "ld $7, 32(%1)\n\t" \
+ "ld $8, 40(%1)\n\t" \
+ "ld $9, 48(%1)\n\t" \
+ "ld $10, 56(%1)\n\t" \
+ "ld $11, 64(%1)\n\t" \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "daddu $29, $29, 24\n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#define CALL_FN_W_12W(lval, orig, arg1,arg2,arg3,arg4,arg5, \
+ arg6,arg7,arg8,arg9,arg10, \
+ arg11,arg12) \
+ do { \
+ volatile OrigFn _orig = (orig); \
+ volatile unsigned long _argvec[13]; \
+ volatile unsigned long _res; \
+ _argvec[0] = (unsigned long)_orig.nraddr; \
+ _argvec[1] = (unsigned long)(arg1); \
+ _argvec[2] = (unsigned long)(arg2); \
+ _argvec[3] = (unsigned long)(arg3); \
+ _argvec[4] = (unsigned long)(arg4); \
+ _argvec[5] = (unsigned long)(arg5); \
+ _argvec[6] = (unsigned long)(arg6); \
+ _argvec[7] = (unsigned long)(arg7); \
+ _argvec[8] = (unsigned long)(arg8); \
+ _argvec[9] = (unsigned long)(arg9); \
+ _argvec[10] = (unsigned long)(arg10); \
+ _argvec[11] = (unsigned long)(arg11); \
+ _argvec[12] = (unsigned long)(arg12); \
+ __asm__ volatile( \
+ "dsubu $29, $29, 32\n\t" \
+ "ld $4, 72(%1)\n\t" \
+ "sd $4, 0($29)\n\t" \
+ "ld $4, 80(%1)\n\t" \
+ "sd $4, 8($29)\n\t" \
+ "ld $4, 88(%1)\n\t" \
+ "sd $4, 16($29)\n\t" \
+ "ld $4, 96(%1)\n\t" \
+ "sd $4, 24($29)\n\t" \
+ "ld $4, 8(%1)\n\t" \
+ "ld $5, 16(%1)\n\t" \
+ "ld $6, 24(%1)\n\t" \
+ "ld $7, 32(%1)\n\t" \
+ "ld $8, 40(%1)\n\t" \
+ "ld $9, 48(%1)\n\t" \
+ "ld $10, 56(%1)\n\t" \
+ "ld $11, 64(%1)\n\t" \
+ "ld $25, 0(%1)\n\t" /* target->t9 */ \
+ VALGRIND_CALL_NOREDIR_T9 \
+ "daddu $29, $29, 32\n\t" \
+ "move %0, $2\n" \
+ : /*out*/ "=r" (_res) \
+ : /*in*/ "r" (&_argvec[0]) \
+ : /*trash*/ "memory", __CALLER_SAVED_REGS \
+ ); \
+ lval = (__typeof__(lval)) _res; \
+ } while (0)
+
+#endif /* PLAT_mips64_linux */
+
+
+/* ------------------------------------------------------------------ */
+/* ARCHITECTURE INDEPENDENT MACROS for CLIENT REQUESTS. */
+/* */
+/* ------------------------------------------------------------------ */
+
+/* Some request codes. There are many more of these, but most are not
+ exposed to end-user view. These are the public ones, all of the
+ form 0x1000 + small_number.
+
+ Core ones are in the range 0x00000000--0x0000ffff. The non-public
+ ones start at 0x2000.
+*/
+
+/* These macros are used by tools -- they must be public, but don't
+ embed them into other programs. */
+#define VG_USERREQ_TOOL_BASE(a,b) \
+ ((unsigned int)(((a)&0xff) << 24 | ((b)&0xff) << 16))
+#define VG_IS_TOOL_USERREQ(a, b, v) \
+ (VG_USERREQ_TOOL_BASE(a,b) == ((v) & 0xffff0000))
+
+/* !! ABIWARNING !! ABIWARNING !! ABIWARNING !! ABIWARNING !!
+ This enum comprises an ABI exported by Valgrind to programs
+ which use client requests. DO NOT CHANGE THE ORDER OF THESE
+ ENTRIES, NOR DELETE ANY -- add new ones at the end. */
+typedef
+ enum { VG_USERREQ__RUNNING_ON_VALGRIND = 0x1001,
+ VG_USERREQ__DISCARD_TRANSLATIONS = 0x1002,
+
+ /* These allow any function to be called from the simulated
+ CPU but run on the real CPU. Nb: the first arg passed to
+ the function is always the ThreadId of the running
+ thread! So CLIENT_CALL0 actually requires a 1 arg
+ function, etc. */
+ VG_USERREQ__CLIENT_CALL0 = 0x1101,
+ VG_USERREQ__CLIENT_CALL1 = 0x1102,
+ VG_USERREQ__CLIENT_CALL2 = 0x1103,
+ VG_USERREQ__CLIENT_CALL3 = 0x1104,
+
+ /* Can be useful in regression testing suites -- eg. can
+ send Valgrind's output to /dev/null and still count
+ errors. */
+ VG_USERREQ__COUNT_ERRORS = 0x1201,
+
+ /* Allows the client program and/or gdbserver to execute a monitor
+ command. */
+ VG_USERREQ__GDB_MONITOR_COMMAND = 0x1202,
+
+ /* These are useful and can be interpreted by any tool that
+ tracks malloc() et al, by using vg_replace_malloc.c. */
+ VG_USERREQ__MALLOCLIKE_BLOCK = 0x1301,
+ VG_USERREQ__RESIZEINPLACE_BLOCK = 0x130b,
+ VG_USERREQ__FREELIKE_BLOCK = 0x1302,
+ /* Memory pool support. */
+ VG_USERREQ__CREATE_MEMPOOL = 0x1303,
+ VG_USERREQ__DESTROY_MEMPOOL = 0x1304,
+ VG_USERREQ__MEMPOOL_ALLOC = 0x1305,
+ VG_USERREQ__MEMPOOL_FREE = 0x1306,
+ VG_USERREQ__MEMPOOL_TRIM = 0x1307,
+ VG_USERREQ__MOVE_MEMPOOL = 0x1308,
+ VG_USERREQ__MEMPOOL_CHANGE = 0x1309,
+ VG_USERREQ__MEMPOOL_EXISTS = 0x130a,
+
+ /* Allow printfs to valgrind log. */
+ /* The first two pass the va_list argument by value, which
+ assumes it is the same size as or smaller than a UWord,
+ which generally isn't the case. Hence are deprecated.
+ The second two pass the vargs by reference and so are
+ immune to this problem. */
+ /* both :: char* fmt, va_list vargs (DEPRECATED) */
+ VG_USERREQ__PRINTF = 0x1401,
+ VG_USERREQ__PRINTF_BACKTRACE = 0x1402,
+ /* both :: char* fmt, va_list* vargs */
+ VG_USERREQ__PRINTF_VALIST_BY_REF = 0x1403,
+ VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF = 0x1404,
+
+ /* Stack support. */
+ VG_USERREQ__STACK_REGISTER = 0x1501,
+ VG_USERREQ__STACK_DEREGISTER = 0x1502,
+ VG_USERREQ__STACK_CHANGE = 0x1503,
+
+ /* Wine support */
+ VG_USERREQ__LOAD_PDB_DEBUGINFO = 0x1601,
+
+ /* Querying of debug info. */
+ VG_USERREQ__MAP_IP_TO_SRCLOC = 0x1701,
+
+ /* Disable/enable error reporting level. Takes a single
+ Word arg which is the delta to this thread's error
+ disablement indicator. Hence 1 disables or further
+ disables errors, and -1 moves back towards enablement.
+ Other values are not allowed. */
+ VG_USERREQ__CHANGE_ERR_DISABLEMENT = 0x1801,
+
+ /* Initialise IR injection */
+ VG_USERREQ__VEX_INIT_FOR_IRI = 0x1901
+ } Vg_ClientRequest;
+
+#if !defined(__GNUC__)
+# define __extension__ /* */
+#endif
+
+
+/* Returns the number of Valgrinds this code is running under. That
+ is, 0 if running natively, 1 if running under Valgrind, 2 if
+ running under Valgrind which is running under another Valgrind,
+ etc. */
+#define RUNNING_ON_VALGRIND \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* if not */, \
+ VG_USERREQ__RUNNING_ON_VALGRIND, \
+ 0, 0, 0, 0, 0) \
+
+
+/* Discard translation of code in the range [_qzz_addr .. _qzz_addr +
+ _qzz_len - 1]. Useful if you are debugging a JITter or some such,
+ since it provides a way to make sure valgrind will retranslate the
+ invalidated area. Returns no value. */
+#define VALGRIND_DISCARD_TRANSLATIONS(_qzz_addr,_qzz_len) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DISCARD_TRANSLATIONS, \
+ _qzz_addr, _qzz_len, 0, 0, 0)
+
+
+/* These requests are for getting Valgrind itself to print something.
+ Possibly with a backtrace. This is a really ugly hack. The return value
+ is the number of characters printed, excluding the "**<pid>** " part at the
+ start and the backtrace (if present). */
+
+#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER)
+/* Modern GCC will optimize the static routine out if unused,
+ and unused attribute will shut down warnings about it. */
+static int VALGRIND_PRINTF(const char *format, ...)
+ __attribute__((format(__printf__, 1, 2), __unused__));
+#endif
+static int
+#if defined(_MSC_VER)
+__inline
+#endif
+VALGRIND_PRINTF(const char *format, ...)
+{
+#if defined(NVALGRIND)
+ return 0;
+#else /* NVALGRIND */
+#if defined(_MSC_VER) || defined(__MINGW64__)
+ uintptr_t _qzz_res;
+#else
+ unsigned long _qzz_res;
+#endif
+ va_list vargs;
+ va_start(vargs, format);
+#if defined(_MSC_VER) || defined(__MINGW64__)
+ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0,
+ VG_USERREQ__PRINTF_VALIST_BY_REF,
+ (uintptr_t)format,
+ (uintptr_t)&vargs,
+ 0, 0, 0);
+#else
+ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0,
+ VG_USERREQ__PRINTF_VALIST_BY_REF,
+ (unsigned long)format,
+ (unsigned long)&vargs,
+ 0, 0, 0);
+#endif
+ va_end(vargs);
+ return (int)_qzz_res;
+#endif /* NVALGRIND */
+}
+
+#if defined(__GNUC__) || defined(__INTEL_COMPILER) && !defined(_MSC_VER)
+static int VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
+ __attribute__((format(__printf__, 1, 2), __unused__));
+#endif
+static int
+#if defined(_MSC_VER)
+__inline
+#endif
+VALGRIND_PRINTF_BACKTRACE(const char *format, ...)
+{
+#if defined(NVALGRIND)
+ return 0;
+#else /* NVALGRIND */
+#if defined(_MSC_VER) || defined(__MINGW64__)
+ uintptr_t _qzz_res;
+#else
+ unsigned long _qzz_res;
+#endif
+ va_list vargs;
+ va_start(vargs, format);
+#if defined(_MSC_VER) || defined(__MINGW64__)
+ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0,
+ VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF,
+ (uintptr_t)format,
+ (uintptr_t)&vargs,
+ 0, 0, 0);
+#else
+ _qzz_res = VALGRIND_DO_CLIENT_REQUEST_EXPR(0,
+ VG_USERREQ__PRINTF_BACKTRACE_VALIST_BY_REF,
+ (unsigned long)format,
+ (unsigned long)&vargs,
+ 0, 0, 0);
+#endif
+ va_end(vargs);
+ return (int)_qzz_res;
+#endif /* NVALGRIND */
+}
+
+
+/* These requests allow control to move from the simulated CPU to the
+ real CPU, calling an arbitary function.
+
+ Note that the current ThreadId is inserted as the first argument.
+ So this call:
+
+ VALGRIND_NON_SIMD_CALL2(f, arg1, arg2)
+
+ requires f to have this signature:
+
+ Word f(Word tid, Word arg1, Word arg2)
+
+ where "Word" is a word-sized type.
+
+ Note that these client requests are not entirely reliable. For example,
+ if you call a function with them that subsequently calls printf(),
+ there's a high chance Valgrind will crash. Generally, your prospects of
+ these working are made higher if the called function does not refer to
+ any global variables, and does not refer to any libc or other functions
+ (printf et al). Any kind of entanglement with libc or dynamic linking is
+ likely to have a bad outcome, for tricky reasons which we've grappled
+ with a lot in the past.
+*/
+#define VALGRIND_NON_SIMD_CALL0(_qyy_fn) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL0, \
+ _qyy_fn, \
+ 0, 0, 0, 0)
+
+#define VALGRIND_NON_SIMD_CALL1(_qyy_fn, _qyy_arg1) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL1, \
+ _qyy_fn, \
+ _qyy_arg1, 0, 0, 0)
+
+#define VALGRIND_NON_SIMD_CALL2(_qyy_fn, _qyy_arg1, _qyy_arg2) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL2, \
+ _qyy_fn, \
+ _qyy_arg1, _qyy_arg2, 0, 0)
+
+#define VALGRIND_NON_SIMD_CALL3(_qyy_fn, _qyy_arg1, _qyy_arg2, _qyy_arg3) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0 /* default return */, \
+ VG_USERREQ__CLIENT_CALL3, \
+ _qyy_fn, \
+ _qyy_arg1, _qyy_arg2, \
+ _qyy_arg3, 0)
+
+
+/* Counts the number of errors that have been recorded by a tool. Nb:
+ the tool must record the errors with VG_(maybe_record_error)() or
+ VG_(unique_error)() for them to be counted. */
+#define VALGRIND_COUNT_ERRORS \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR( \
+ 0 /* default return */, \
+ VG_USERREQ__COUNT_ERRORS, \
+ 0, 0, 0, 0, 0)
+
+/* Several Valgrind tools (Memcheck, Massif, Helgrind, DRD) rely on knowing
+ when heap blocks are allocated in order to give accurate results. This
+ happens automatically for the standard allocator functions such as
+ malloc(), calloc(), realloc(), memalign(), new, new[], free(), delete,
+ delete[], etc.
+
+ But if your program uses a custom allocator, this doesn't automatically
+ happen, and Valgrind will not do as well. For example, if you allocate
+ superblocks with mmap() and then allocates chunks of the superblocks, all
+ Valgrind's observations will be at the mmap() level and it won't know that
+ the chunks should be considered separate entities. In Memcheck's case,
+ that means you probably won't get heap block overrun detection (because
+ there won't be redzones marked as unaddressable) and you definitely won't
+ get any leak detection.
+
+ The following client requests allow a custom allocator to be annotated so
+ that it can be handled accurately by Valgrind.
+
+ VALGRIND_MALLOCLIKE_BLOCK marks a region of memory as having been allocated
+ by a malloc()-like function. For Memcheck (an illustrative case), this
+ does two things:
+
+ - It records that the block has been allocated. This means any addresses
+ within the block mentioned in error messages will be
+ identified as belonging to the block. It also means that if the block
+ isn't freed it will be detected by the leak checker.
+
+ - It marks the block as being addressable and undefined (if 'is_zeroed' is
+ not set), or addressable and defined (if 'is_zeroed' is set). This
+ controls how accesses to the block by the program are handled.
+
+ 'addr' is the start of the usable block (ie. after any
+ redzone), 'sizeB' is its size. 'rzB' is the redzone size if the allocator
+ can apply redzones -- these are blocks of padding at the start and end of
+ each block. Adding redzones is recommended as it makes it much more likely
+ Valgrind will spot block overruns. `is_zeroed' indicates if the memory is
+ zeroed (or filled with another predictable value), as is the case for
+ calloc().
+
+ VALGRIND_MALLOCLIKE_BLOCK should be put immediately after the point where a
+ heap block -- that will be used by the client program -- is allocated.
+ It's best to put it at the outermost level of the allocator if possible;
+ for example, if you have a function my_alloc() which calls
+ internal_alloc(), and the client request is put inside internal_alloc(),
+ stack traces relating to the heap block will contain entries for both
+ my_alloc() and internal_alloc(), which is probably not what you want.
+
+ For Memcheck users: if you use VALGRIND_MALLOCLIKE_BLOCK to carve out
+ custom blocks from within a heap block, B, that has been allocated with
+ malloc/calloc/new/etc, then block B will be *ignored* during leak-checking
+ -- the custom blocks will take precedence.
+
+ VALGRIND_FREELIKE_BLOCK is the partner to VALGRIND_MALLOCLIKE_BLOCK. For
+ Memcheck, it does two things:
+
+ - It records that the block has been deallocated. This assumes that the
+ block was annotated as having been allocated via
+ VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued.
+
+ - It marks the block as being unaddressable.
+
+ VALGRIND_FREELIKE_BLOCK should be put immediately after the point where a
+ heap block is deallocated.
+
+ VALGRIND_RESIZEINPLACE_BLOCK informs a tool about reallocation. For
+ Memcheck, it does four things:
+
+ - It records that the size of a block has been changed. This assumes that
+ the block was annotated as having been allocated via
+ VALGRIND_MALLOCLIKE_BLOCK. Otherwise, an error will be issued.
+
+ - If the block shrunk, it marks the freed memory as being unaddressable.
+
+ - If the block grew, it marks the new area as undefined and defines a red
+ zone past the end of the new block.
+
+ - The V-bits of the overlap between the old and the new block are preserved.
+
+ VALGRIND_RESIZEINPLACE_BLOCK should be put after allocation of the new block
+ and before deallocation of the old block.
+
+ In many cases, these three client requests will not be enough to get your
+ allocator working well with Memcheck. More specifically, if your allocator
+ writes to freed blocks in any way then a VALGRIND_MAKE_MEM_UNDEFINED call
+ will be necessary to mark the memory as addressable just before the zeroing
+ occurs, otherwise you'll get a lot of invalid write errors. For example,
+ you'll need to do this if your allocator recycles freed blocks, but it
+ zeroes them before handing them back out (via VALGRIND_MALLOCLIKE_BLOCK).
+ Alternatively, if your allocator reuses freed blocks for allocator-internal
+ data structures, VALGRIND_MAKE_MEM_UNDEFINED calls will also be necessary.
+
+ Really, what's happening is a blurring of the lines between the client
+ program and the allocator... after VALGRIND_FREELIKE_BLOCK is called, the
+ memory should be considered unaddressable to the client program, but the
+ allocator knows more than the rest of the client program and so may be able
+ to safely access it. Extra client requests are necessary for Valgrind to
+ understand the distinction between the allocator and the rest of the
+ program.
+
+ Ignored if addr == 0.
+*/
+#define VALGRIND_MALLOCLIKE_BLOCK(addr, sizeB, rzB, is_zeroed) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MALLOCLIKE_BLOCK, \
+ addr, sizeB, rzB, is_zeroed, 0)
+
+/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details.
+ Ignored if addr == 0.
+*/
+#define VALGRIND_RESIZEINPLACE_BLOCK(addr, oldSizeB, newSizeB, rzB) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__RESIZEINPLACE_BLOCK, \
+ addr, oldSizeB, newSizeB, rzB, 0)
+
+/* See the comment for VALGRIND_MALLOCLIKE_BLOCK for details.
+ Ignored if addr == 0.
+*/
+#define VALGRIND_FREELIKE_BLOCK(addr, rzB) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__FREELIKE_BLOCK, \
+ addr, rzB, 0, 0, 0)
+
+/* Create a memory pool. */
+#define VALGRIND_CREATE_MEMPOOL(pool, rzB, is_zeroed) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CREATE_MEMPOOL, \
+ pool, rzB, is_zeroed, 0, 0)
+
+/* Destroy a memory pool. */
+#define VALGRIND_DESTROY_MEMPOOL(pool) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__DESTROY_MEMPOOL, \
+ pool, 0, 0, 0, 0)
+
+/* Associate a piece of memory with a memory pool. */
+#define VALGRIND_MEMPOOL_ALLOC(pool, addr, size) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_ALLOC, \
+ pool, addr, size, 0, 0)
+
+/* Disassociate a piece of memory from a memory pool. */
+#define VALGRIND_MEMPOOL_FREE(pool, addr) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_FREE, \
+ pool, addr, 0, 0, 0)
+
+/* Disassociate any pieces outside a particular range. */
+#define VALGRIND_MEMPOOL_TRIM(pool, addr, size) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_TRIM, \
+ pool, addr, size, 0, 0)
+
+/* Resize and/or move a piece associated with a memory pool. */
+#define VALGRIND_MOVE_MEMPOOL(poolA, poolB) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MOVE_MEMPOOL, \
+ poolA, poolB, 0, 0, 0)
+
+/* Resize and/or move a piece associated with a memory pool. */
+#define VALGRIND_MEMPOOL_CHANGE(pool, addrA, addrB, size) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__MEMPOOL_CHANGE, \
+ pool, addrA, addrB, size, 0)
+
+/* Return 1 if a mempool exists, else 0. */
+#define VALGRIND_MEMPOOL_EXISTS(pool) \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__MEMPOOL_EXISTS, \
+ pool, 0, 0, 0, 0)
+
+/* Mark a piece of memory as being a stack. Returns a stack id. */
+#define VALGRIND_STACK_REGISTER(start, end) \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__STACK_REGISTER, \
+ start, end, 0, 0, 0)
+
+/* Unmark the piece of memory associated with a stack id as being a
+ stack. */
+#define VALGRIND_STACK_DEREGISTER(id) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_DEREGISTER, \
+ id, 0, 0, 0, 0)
+
+/* Change the start and end address of the stack id. */
+#define VALGRIND_STACK_CHANGE(id, start, end) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__STACK_CHANGE, \
+ id, start, end, 0, 0)
+
+/* Load PDB debug info for Wine PE image_map. */
+#define VALGRIND_LOAD_PDB_DEBUGINFO(fd, ptr, total_size, delta) \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__LOAD_PDB_DEBUGINFO, \
+ fd, ptr, total_size, delta, 0)
+
+/* Map a code address to a source file name and line number. buf64
+ must point to a 64-byte buffer in the caller's address space. The
+ result will be dumped in there and is guaranteed to be zero
+ terminated. If no info is found, the first byte is set to zero. */
+#define VALGRIND_MAP_IP_TO_SRCLOC(addr, buf64) \
+ (unsigned)VALGRIND_DO_CLIENT_REQUEST_EXPR(0, \
+ VG_USERREQ__MAP_IP_TO_SRCLOC, \
+ addr, buf64, 0, 0, 0)
+
+/* Disable error reporting for this thread. Behaves in a stack like
+ way, so you can safely call this multiple times provided that
+ VALGRIND_ENABLE_ERROR_REPORTING is called the same number of times
+ to re-enable reporting. The first call of this macro disables
+ reporting. Subsequent calls have no effect except to increase the
+ number of VALGRIND_ENABLE_ERROR_REPORTING calls needed to re-enable
+ reporting. Child threads do not inherit this setting from their
+ parents -- they are always created with reporting enabled. */
+#define VALGRIND_DISABLE_ERROR_REPORTING \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \
+ 1, 0, 0, 0, 0)
+
+/* Re-enable error reporting, as per comments on
+ VALGRIND_DISABLE_ERROR_REPORTING. */
+#define VALGRIND_ENABLE_ERROR_REPORTING \
+ VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__CHANGE_ERR_DISABLEMENT, \
+ -1, 0, 0, 0, 0)
+
+/* Execute a monitor command from the client program.
+ If a connection is opened with GDB, the output will be sent
+ according to the output mode set for vgdb.
+ If no connection is opened, output will go to the log output.
+ Returns 1 if command not recognised, 0 otherwise. */
+#define VALGRIND_MONITOR_COMMAND(command) \
+ VALGRIND_DO_CLIENT_REQUEST_EXPR(0, VG_USERREQ__GDB_MONITOR_COMMAND, \
+ command, 0, 0, 0, 0)
+
+
+#undef PLAT_x86_darwin
+#undef PLAT_amd64_darwin
+#undef PLAT_x86_win32
+#undef PLAT_amd64_win64
+#undef PLAT_x86_linux
+#undef PLAT_amd64_linux
+#undef PLAT_ppc32_linux
+#undef PLAT_ppc64_linux
+#undef PLAT_arm_linux
+#undef PLAT_s390x_linux
+#undef PLAT_mips32_linux
+#undef PLAT_mips64_linux
+
+#endif /* __VALGRIND_H */
diff --git a/attic/index.html b/attic/index.html
new file mode 100644
index 00000000..f1f621c4
--- /dev/null
+++ b/attic/index.html
@@ -0,0 +1,3 @@
+ <!DOCTYPE html><html><head>
+<meta http-equiv="refresh" content="0; url=doc/html/afio.html" />
+</head></html>
diff --git a/attic/multiabi_alltests_gcc.sh b/attic/multiabi_alltests_gcc.sh
new file mode 100755
index 00000000..8bf238c9
--- /dev/null
+++ b/attic/multiabi_alltests_gcc.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+if [ -z "$CXX" ]; then
+ if [ "$HOSTTYPE" = "FreeBSD" ]; then
+ CXX=clang++
+ else
+ CXX=g++
+ fi
+fi
+HOSTOS=$(uname)
+if [ "$HOSTOS" = "Linux" ]; then
+ LIBATOMIC="-ldl"
+ if [ "$CXX" != "${CXX#clang++}" ] && [ "$NODE_NAME" = "linux-gcc-clang" ]; then
+ LIBATOMIC="$LIBATOMIC -latomic"
+ fi
+fi
+if [ "$HOSTOS" = "FreeBSD" ]; then
+ LIBATOMIC="-I/usr/local/include -L/usr/local/lib -lexecinfo"
+fi
+if [ ! -d asio ]; then
+ sh -c "git clone https://github.com/chriskohlhoff/asio.git"
+fi
+cd test
+sh ./test_file_glob.sh
+cd ..
+rm -rf test_all
+$CXX -o test_all -g -O3 -std=c++11 -rdynamic -fstrict-aliasing -Wstrict-aliasing -Wno-unused -fasynchronous-unwind-tables test/test_all_multiabi.cpp detail/SpookyV2.cpp -DBOOST_THREAD_VERSION=3 -Wno-constexpr-not-const -Wno-c++1y-extensions -Wno-unused-value -I ~/boost-release -Iinclude -Itest -Iasio/asio/include -lboost_thread -lboost_chrono -lboost_filesystem -lboost_system -lpthread $LIBATOMIC
diff --git a/attic/multiabi_alltests_msvc.bat b/attic/multiabi_alltests_msvc.bat
new file mode 100644
index 00000000..0cd7bc92
--- /dev/null
+++ b/attic/multiabi_alltests_msvc.bat
@@ -0,0 +1,10 @@
+if not exist asio git clone https://github.com/chriskohlhoff/asio.git
+cd asio
+git pull
+cd ..
+
+IF "%VisualStudioVersion%" == "14.0" (
+cl /Zi /EHsc /O2 /arch:SSE2 /MD /GF /GR /Gy /bigobj /wd4503 test\test_all_multiabi.cpp detail\SpookyV2.cpp /DUNICODE=1 /DWIN32=1 /D_UNICODE=1 /D_WIN32=1 /DBOOST_THREAD_VERSION=3 /Iinclude /Itest /Iasio/asio/include /I..\.. /link /LIBPATH:..\..\stage\lib
+) ELSE (
+echo Sorry need inline namespace support for this
+)
diff --git a/attic/send_to_wandbox.sh b/attic/send_to_wandbox.sh
new file mode 100755
index 00000000..bb468fda
--- /dev/null
+++ b/attic/send_to_wandbox.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+rm -rf send_to_wandbox_tmp
+mkdir send_to_wandbox_tmp
+include/boost/afio/bindlib/scripts/GenSingleHeader.py -DBOOST_AFIO_USE_BOOST_FILESYSTEM=1 -DBOOST_AFIO_DISABLE_VALGRIND=1 -Eafio_iocp.ipp -Ent_kernel_stuff -Evalgrind -Amonad_policy -Afuture_policy include/boost/afio/afio.hpp > include/boost/afio/single_include.hpp
+sed "1s/.*/#include \"afio_single_include.hpp\"/" example/readwrite_example.cpp > send_to_wandbox.cpp
+#cd send_to_wandbox_tmp
+#sed "s/#include/@include/g" ../include/boost/afio/single_include.hpp > afio_single_include.hpp
+#g++ -std=c++11 -E afio_single_include.hpp > afio_single_include2.hpp
+#sed "s/@include/#include/g" afio_single_include2.hpp > afio_single_include.hpp
+#sed "s/# [0-9][0-9]* \".*\".*//g" afio_single_include.hpp > afio_single_include2.hpp
+#sed "/^$/d" afio_single_include2.hpp > afio_single_include.hpp
+#rm afio_single_include2.hpp
+#cd ..
+gcc -fpreprocessed -dD -E -P include/boost/afio/single_include.hpp > send_to_wandbox_tmp/afio_single_include2.hpp 2>/dev/null
+sed "/^$/d" send_to_wandbox_tmp/afio_single_include2.hpp > send_to_wandbox_tmp/afio_single_include.hpp
+rm -rf send_to_wandbox_tmp/afio_single_include2.hpp
+#include/boost/afio/bindlib/scripts/send_to_wandbox.py send_to_wandbox_tmp send_to_wandbox.cpp
+URL=`include/boost/afio/bindlib/scripts/send_to_wandbox.py send_to_wandbox_tmp send_to_wandbox.cpp | sed -e 's/.*\(http:\/\/[^ '"'"']*\).*/\1/'`
+if [[ $FRAME != "" ]]; then
+ echo '<iframe src="'$URL'" frameborder="0" style="height: 100%; width: 100%;" height="100%" width="100%"></iframe>'
+else
+ echo $URL
+fi
+rm -rf send_to_wandbox_tmp
diff --git a/attic/src/afio.cpp b/attic/src/afio.cpp
new file mode 100644
index 00000000..61fb13cf
--- /dev/null
+++ b/attic/src/afio.cpp
@@ -0,0 +1,27 @@
+/* async_file_io
+Provides a threadpool and asynchronous file i/o infrastructure based on Boost.ASIO, Boost.Iostreams and filesystem
+(C) 2013-2014 Niall Douglas http://www.nedprod.com/
+File Created: Mar 2013
+*/
+
+// Boost ASIO needs this
+#if !defined(_WIN32_WINNT) && defined(WIN32)
+#define _WIN32_WINNT 0x0501
+#endif
+
+// Need to include a copy of ASIO
+#ifdef BOOST_ASIO_SEPARATE_COMPILATION
+#include "boost/asio/impl/src.hpp"
+#endif
+
+#define BOOST_AFIO_HEADERS_ONLY 0
+#if !defined(BOOST_AFIO_NEVER_VALIDATE_INPUTS) && !defined(BOOST_AFIO_COMPILING_FOR_GCOV)
+#define BOOST_AFIO_VALIDATE_INPUTS 1
+#endif
+
+#include "boost/afio/afio.hpp"
+#if BOOST_AFIO_LATEST_VERSION != 2
+# error Mismatched afio.cpp to latest version
+#endif
+#include "boost/afio/v2/detail/impl/afio.ipp"
+
diff --git a/attic/standalone_alltests_gcc.bat b/attic/standalone_alltests_gcc.bat
new file mode 100644
index 00000000..9bae18f0
--- /dev/null
+++ b/attic/standalone_alltests_gcc.bat
@@ -0,0 +1 @@
+g++ -o test_all -g -O3 -DNDEBUG -std=c++11 -pthread test/test_all.cpp detail/SpookyV2.cpp -Iinclude -Itest -DUNICODE=1 -DWIN32=1 -D_UNICODE=1 -D_WIN32=1 -DAFIO_STANDALONE=1 -Iasio/asio/include -DASIO_STANDALONE=1 -DBOOST_AFIO_RUNNING_IN_CI=1 -I../boost-release -L../boost-release/stage/lib -lboost_filesystem-mgw49-mt-1_57 -lboost_system-mgw49-mt-1_57 -lws2_32 -Wl,-subsystem,console
diff --git a/attic/standalone_alltests_gcc.sh b/attic/standalone_alltests_gcc.sh
new file mode 100755
index 00000000..cc00e4dc
--- /dev/null
+++ b/attic/standalone_alltests_gcc.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+if [ -z "$CXX" ]; then
+ if [ "$HOSTTYPE" = "FreeBSD" ]; then
+ CXX=clang++
+ else
+ CXX=g++
+ fi
+fi
+HOSTOS=$(uname)
+if [ "$HOSTOS" = "Linux" ]; then
+ LIBATOMIC="-ldl"
+ if [ "$CXX" != "${CXX#clang++}" ] && [ "$NODE_NAME" = "linux-gcc-clang" ]; then
+ LIBATOMIC="$LIBATOMIC -latomic"
+ fi
+fi
+if [ "$HOSTOS" = "FreeBSD" ]; then
+ LIBATOMIC="-I/usr/local/include -L/usr/local/lib -lexecinfo"
+fi
+if [ ! -d asio ]; then
+ sh -c "git clone https://github.com/chriskohlhoff/asio.git"
+fi
+cd test
+sh ./test_file_glob.sh
+cd ..
+rm -rf test_all
+$CXX -o test_all -g -O3 -DNDEBUG -std=c++11 -rdynamic -fstrict-aliasing -Wstrict-aliasing -Wno-unused -fasynchronous-unwind-tables test/test_all.cpp detail/SpookyV2.cpp -Iinclude -Itest -DAFIO_STANDALONE=1 -Iasio/asio/include -DSPINLOCK_STANDALONE=1 -DASIO_STANDALONE=1 -DBOOST_AFIO_RUNNING_IN_CI=1 -Wno-unused-value -lboost_filesystem -lboost_system -lpthread $LIBATOMIC
diff --git a/attic/standalone_alltests_msvc.bat b/attic/standalone_alltests_msvc.bat
new file mode 100644
index 00000000..423b5315
--- /dev/null
+++ b/attic/standalone_alltests_msvc.bat
@@ -0,0 +1,18 @@
+if not exist asio git clone https://github.com/chriskohlhoff/asio.git
+cd asio
+git pull
+cd ..
+
+IF "%VisualStudioVersion%" == "14.0" (
+ rem Totally standalone edition (VS2015 required)
+ if "%1" == "single_include" (
+ include\boost\afio\bindlib\scripts\GenSingleHeader.py -DAFIO_STANDALONE=1 -DSPINLOCK_STANDALONE=1 -DASIO_STANDALONE=1 include/boost/afio/afio.hpp > test\single_include_test_all.cpp
+ type test\test_all.cpp >> test\single_include_test_all.cpp
+ cl /Zi /EHsc /O2 /DNDEBUG /arch:SSE2 /MD /GF /GR /Gy /bigobj /wd4503 test\single_include_test_all.cpp detail\SpookyV2.cpp /DUNICODE=1 /DWIN32=1 /D_UNICODE=1 /D_WIN32=1 /Iinclude /Itest /Iasio/asio/include /DBOOST_AFIO_RUNNING_IN_CI=1
+ ) else (
+ cl /Zi /EHsc /O2 /DNDEBUG /arch:SSE2 /MD /GF /GR /Gy /bigobj /wd4503 test\test_all.cpp detail\SpookyV2.cpp /DUNICODE=1 /DWIN32=1 /D_UNICODE=1 /D_WIN32=1 /Iinclude /Itest /DAFIO_STANDALONE=1 /Iasio/asio/include /DSPINLOCK_STANDALONE=1 /DASIO_STANDALONE=1 /DBOOST_AFIO_RUNNING_IN_CI=1
+ )
+) ELSE (
+ rem Needs filesystem
+ cl /Zi /EHsc /O2 /DNDEBUG /MD /GF /GR /Gy /bigobj /wd4503 test\test_all.cpp detail\SpookyV2.cpp /DUNICODE=1 /DWIN32=1 /D_UNICODE=1 /D_WIN32=1 /Iinclude /Itest /DAFIO_STANDALONE=1 /Iasio/asio/include /DSPINLOCK_STANDALONE=1 /DASIO_STANDALONE=1 /DBOOST_AFIO_RUNNING_IN_CI=1 /I..\boost-release /link ..\boost-release\stage\lib\libboost_filesystem-vc120-mt-1_57.lib ..\boost-release\stage\lib\libboost_system-vc120-mt-1_57.lib
+)
diff --git a/attic/test/Aligned_Allocator.hpp b/attic/test/Aligned_Allocator.hpp
new file mode 100644
index 00000000..48a2e0ca
--- /dev/null
+++ b/attic/test/Aligned_Allocator.hpp
@@ -0,0 +1,259 @@
+/*
+ * File: Aligned_Allocator.hpp
+ * Author: atlas
+ *
+ * Created on July 5, 2013, 6:52 PM
+
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+#include <typeinfo>
+#include <vector>
+
+
+//! \def BOOST_AFIO_PACKEDTYPE(typedecl) The markup this compiler uses to pack a structure as tightly as possible
+#ifndef BOOST_AFIO_PACKEDTYPE
+#ifdef BOOST_MSVC
+#define BOOST_AFIO_PACKEDTYPE(typedecl) __pragma(pack(push, 1)) typedecl __pragma(pack(pop))
+#elif defined(__GNUC__)
+#define BOOST_AFIO_PACKEDTYPE(typedecl) typedecl __attribute__((packed))
+#else
+#define BOOST_AFIO_PACKEDTYPE(typedecl) unknown_type_pack_markup_for_this_compiler
+#endif
+#endif
+
+
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+ namespace detail {
+enum class allocator_alignment : size_t
+{
+ Default = sizeof(void*), //!< The default alignment on this machine.
+ SSE = 16, //!< The alignment for SSE. Better to use M128 for NEON et al support.
+ M128 = 16, //!< The alignment for a 128 bit vector.
+ AVX = 32, //!< The alignment for AVX. Better to use M256 for NEON et al support.
+ M256 = 32 //!< The alignment for a 256 bit vector.
+};
+#ifdef BOOST_WINDOWS
+ extern "C" void *_aligned_malloc(size_t size, size_t alignment);
+ extern "C" void _aligned_free(void *blk);
+#else
+ extern "C" int posix_memalign(void **memptr, size_t alignment, size_t size);
+#endif
+ inline void* allocate_aligned_memory(size_t align, size_t size)
+ {
+#ifdef BOOST_WINDOWS
+ return _aligned_malloc(size, align);
+#else
+ void *ret=nullptr;
+ if(posix_memalign(&ret, align, size)) return nullptr;
+ return ret;
+#endif
+ }
+ inline void deallocate_aligned_memory(void* ptr) noexcept
+ {
+#ifdef BOOST_WINDOWS
+ _aligned_free(ptr);
+#else
+ free(ptr);
+#endif
+ }
+
+
+/*! \class aligned_allocator
+\brief An STL allocator which allocates aligned memory
+
+Stolen from http://stackoverflow.com/questions/12942548/making-stdvector-allocate-aligned-memory
+*/
+template <typename T, size_t Align=std::alignment_of<T>::value, bool initialize=true>
+class aligned_allocator
+{
+public:
+ typedef T value_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ enum { alignment=Align };
+
+ typedef std::true_type propagate_on_container_move_assignment;
+
+ template <class U>
+ struct rebind { typedef aligned_allocator<U, Align, initialize> other; };
+
+public:
+ aligned_allocator() noexcept
+ {}
+
+ template <class U>
+ aligned_allocator(const aligned_allocator<U, Align, initialize>&) noexcept
+ {}
+
+ size_type
+ max_size() const noexcept
+ { return (size_type(~0) - size_type(Align)) / sizeof(T); }
+
+ pointer
+ address(reference x) const noexcept
+ { return std::addressof(x); }
+
+ const_pointer
+ address(const_reference x) const noexcept
+ { return std::addressof(x); }
+
+ pointer
+ allocate(size_type n, typename aligned_allocator<void, Align, initialize>::const_pointer = 0)
+ {
+ const size_type alignment = static_cast<size_type>( Align );
+ void* ptr = detail::allocate_aligned_memory(alignment , n * sizeof(T));
+ if (ptr == nullptr) {
+ throw std::bad_alloc();
+ }
+
+ return reinterpret_cast<pointer>(ptr);
+ }
+
+ void
+ deallocate(pointer p, size_type) noexcept
+ { return detail::deallocate_aligned_memory(p); }
+
+ template <class U, class ...Args>
+ void
+ construct(U* p, Args&&... args)
+ { if(initialize || !std::is_same<char, U>::value) ::new(reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...); }
+
+ void
+ destroy(pointer p)
+ { (void) p; p->~T(); }
+};
+
+template <size_t Align, bool initialize> class aligned_allocator<void, Align, initialize>
+{
+public:
+ typedef void value_type;
+ typedef void * pointer;
+ typedef const void * const_pointer;
+ typedef void reference;
+ typedef const void const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ enum { alignment=Align };
+};
+template <size_t Align, bool initialize> class aligned_allocator<const void, Align, initialize>
+{
+public:
+ typedef const void value_type;
+ typedef const void* pointer;
+ typedef const void* const_pointer;
+ typedef void reference;
+ typedef const void const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ enum { alignment=Align };
+};
+
+template <typename T, size_t Align, bool initialize>
+class aligned_allocator<const T, Align, initialize>
+{
+public:
+ typedef T value_type;
+ typedef const T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ enum { alignment=Align };
+
+ typedef std::true_type propagate_on_container_move_assignment;
+
+ template <class U>
+ struct rebind { typedef aligned_allocator<U, Align, initialize> other; };
+
+public:
+ aligned_allocator() noexcept
+ {}
+
+ template <class U>
+ aligned_allocator(const aligned_allocator<U, Align, initialize>&) noexcept
+ {}
+
+ size_type
+ max_size() const noexcept
+ { return (size_type(~0) - size_type(Align)) / sizeof(T); }
+
+ const_pointer
+ address(const_reference x) const noexcept
+ { return std::addressof(x); }
+
+ pointer
+ allocate(size_type n, typename aligned_allocator<void, Align, initialize>::const_pointer = 0)
+ {
+ const size_type alignment = static_cast<size_type>( Align );
+ void* ptr = detail::allocate_aligned_memory(alignment , n * sizeof(T));
+ if (ptr == nullptr) {
+ throw std::bad_alloc();
+ }
+
+ return reinterpret_cast<pointer>(ptr);
+ }
+
+ void
+ deallocate(pointer p, size_type) noexcept
+ { return detail::deallocate_aligned_memory(p); }
+
+ template <class U, class ...Args>
+ void
+ construct(U* p, Args&&... args)
+ { if(initialize || !std::is_same<char, U>::value) ::new(reinterpret_cast<void*>(p)) U(std::forward<Args>(args)...); }
+
+ void
+ destroy(pointer p)
+ { p->~T(); }
+};
+
+template <typename T, size_t TAlign, bool Tinit, typename U, size_t UAlign, bool Uinit>
+inline
+bool
+operator== (const aligned_allocator<T,TAlign,Tinit>&, const aligned_allocator<U, UAlign, Uinit>&) noexcept
+{ return TAlign == UAlign; }
+
+template <typename T, size_t TAlign, bool Tinit, typename U, size_t UAlign, bool Uinit>
+inline
+bool
+operator!= (const aligned_allocator<T,TAlign,Tinit>&, const aligned_allocator<U, UAlign, Uinit>&) noexcept
+{ return TAlign != UAlign; }
+
+
+ }//namespace detail
+BOOST_AFIO_V2_NAMESPACE_END
+
diff --git a/attic/test/Jamfile.v2 b/attic/test/Jamfile.v2
new file mode 100644
index 00000000..462ef89b
--- /dev/null
+++ b/attic/test/Jamfile.v2
@@ -0,0 +1,240 @@
+# Boost.AFIO Library test Jamfile
+#
+# Copyright (c) 2013 Paul Kirth and Niall Douglas
+#
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at
+# http://www.boost.org/LICENSE_1_0.txt)
+
+import modules ;
+import ../../config/checks/config : requires ;
+.argv = [ modules.peek : ARGV ] ;
+
+#local boost-path = [ modules.peek : BOOST_ROOT ] ; #can probably get rid of this
+
+import os ;
+import testing ;
+import pch ;
+
+# If we're running in a CI, reduce the length of the unit tests considerably
+rule running-in-ci ( properties * )
+{
+ local result ;
+ if "--running-in-ci" in $(.argv)
+ {
+ result += <define>BOOST_AFIO_RUNNING_IN_CI ;
+ }
+ return $(result) ;
+}
+
+# If we're on Niall's Atom 220 netbook, make it so Niall isn't waiting an hour
+# or more just to compile AFIO every time I change a single character.
+rule fast-build ( properties * )
+{
+ local result ;
+ if "--fast-build" in $(.argv)
+ {
+ result += <define>BOOST_AFIO_HEADERS_ONLY=0
+ <define>BOOST_ASIO_SEPARATE_COMPILATION
+ <define>BOOST_ASIO_DYN_LINK
+ <library>../build//boost_afio_dyn_asio
+ ;
+ }
+ return $(result) ;
+}
+
+# Turn on LTO
+rule lto ( properties * )
+{
+ local result ;
+ if "--lto" in $(.argv)
+ {
+ if <toolset>gcc in $(properties)
+ {
+ result += <cxxflags>"-flto" <linkflags>"-flto -O3 -fuse-linker-plugin" ;
+ }
+ if <toolset>msvc in $(properties)
+ {
+ result += <cxxflags>"/GL" <linkflags>"/LTCG" ;
+ }
+ }
+ return $(result) ;
+}
+
+# Turn on the thread and undefined behaviour sanitisers
+rule sanitize ( properties * )
+{
+ local result ;
+ if "--sanitize" in $(.argv)
+ {
+ # clang only for now as we need the blacklist to remove Boost noise
+ if <toolset>clang in $(properties)
+ {
+ result += <define>BOOST_AFIO_THREAD_SANITIZING
+ <cxxflags>"-fsanitize=thread -fsanitize=undefined -fsanitize-blacklist=libs/afio/test/blacklist.supp -g -fno-omit-frame-pointer -fno-optimize-sibling-calls"
+ <linkflags>"-fsanitize=thread -fsanitize=undefined"
+ <testing.launcher>TSAN_OPTIONS="suppressions=libs/afio/test/tsan.supp history_size=7 external_symbolizer_path=/usr/bin/llvm-symbolizer-3.4" ;
+ }
+ }
+ return $(result) ;
+}
+
+# Some compilers can do enhanced static analysis
+rule analyse ( properties * )
+{
+ local result ;
+ if "--analyse" in $(.argv)
+ {
+ if <toolset>clang in $(properties)
+ {
+ result += <cxxflags>"--analyze" ;
+ }
+ if <toolset>msvc in $(properties)
+ {
+ result += <cxxflags>"/analyze /wd6246 /wd6993 /wd4603 /analyze:stacksize 131072" ;
+ }
+ }
+ return $(result) ;
+}
+
+project boost/afio/test
+ : requirements
+ [ requires cxx11_variadic_templates cxx11_template_aliases cxx11_noexcept cxx11_constexpr ]
+ <threading>multi
+ <include>.
+# <library>/boost/afio//boost_afio
+ <toolset>gcc:<cxxflags>"-fvisibility-inlines-hidden -fstrict-aliasing -Wstrict-aliasing -Wno-unused -fargument-noalias -fvisibility=hidden -fopenmp -fasynchronous-unwind-tables"
+ <toolset>gcc-mingw:<cxxflags>"-DWIN32 -D_UNICODE -DUNICODE -Wno-missing-braces"
+ <toolset>gcc-mingw:<library>/boost/afio//boost_afio
+ <toolset>gcc-mingw:<linkflags>"-lws2_32"
+ <toolset>gcc:<linkflags>"-lgomp"
+ <toolset>clang:<cxxflags>"-fvisibility-inlines-hidden -fstrict-aliasing -Wstrict-aliasing -Wno-unused -Wno-mismatched-tags -Wno-unknown-pragmas -fvisibility=hidden -fopenmp -fasynchronous-unwind-tables"
+ <toolset>msvc:<cxxflags>"/openmp /GF /Gy /bigobj /wd4456"
+ <toolset>msvc:<linkflags>"/LARGEADDRESSAWARE /DYNAMICBASE /NXCOMPAT" # /VERSION:1.00.0"
+ <toolset>msvc:<define>WIN32
+ <toolset>msvc:<define>_WINDOWS
+ <toolset>msvc:<define>UNICODE
+ <toolset>msvc:<define>_UNICODE
+ <target-os>linux:<linkflags>"-lpthread -ldl -lrt"
+ <target-os>freebsd:<linkflags>"-lpthread -lexecinfo"
+ <link>shared:<define>BOOST_AFIO_DYN_LINK=1
+ <library>../../system/build//boost_system
+ <library>../../filesystem/build//boost_filesystem
+ <library>../../atomic/build//boost_atomic
+ <library>../../thread/build//boost_thread
+ <define>BOOST_AFIO_USE_BOOST_UNIT_TEST=1
+ <library>../../test/build//boost_unit_test_framework
+ <conditional>@running-in-ci
+ <conditional>@fast-build
+ <conditional>@lto
+ <conditional>@sanitize
+ <conditional>@analyse
+ ;
+
+cpp-pch test_functions : test_functions.hpp : <include>. ;
+explicit test_functions ;
+cpp-pch afio_pch : afio_pch.hpp : <include>. ;
+explicit afio_pch ;
+obj spooky : ../detail/SpookyV2.cpp ;
+
+# look in the commandline args for "--valgrind=" and capture its contents in VALGRIND_ARGS
+local VALGRIND_ARGS = [ MATCH --valgrind=(.*) : $(.argv) ] ;
+
+
+# if we're doing a valgrind build, set up the test launcher
+if $(VALGRIND_ARGS)
+{
+ launcher = <testing.launcher>valgrind ;
+}
+
+#auto generate the test_all.cpp
+if [ os.name ] = "NT"
+{
+ local str = "for /f \"delims=\" %i in ('dir /b /a-d /s test_file_glob.bat') do \"%~fi\"" ;
+ SHELL $(str) ;
+}
+else
+{
+ SHELL [ SHELL "find -L libs/afio -name test_file_glob.sh" ] ;
+}
+
+# Let user limit test_files
+local test_files = [ MATCH ^--test=(.*)$ : $(.argv) ] ;
+local example_files = [ glob ../example/*.cpp ] ;
+if $(test_files)
+{
+ test_files_all = $(test_files) ;
+ single_test = true ;
+}
+else
+{
+ # Don't do any of the fsyncing tests on the CI, as it murders the poor hard drive
+ if "--running-in-ci" in $(.argv)
+ {
+ test_files = [ glob tests/*.cpp : tests/*sync_test.cpp tests/*autoflush_test.cpp ] ;
+ }
+ else
+ {
+ test_files = [ glob tests/*.cpp ] ;
+ }
+ test_files_all = $(test_files) test_all.cpp ;
+ single_test = false ;
+}
+
+# link tests (just compile and links targets)
+if "--link-test" in $(.argv)
+{
+ if $(single_test) != true
+ {
+ # Test that inline linkage works with variadic templates turned off
+ exe test_inline_linkage_master : test_inline_linkage_master.cpp test_inline_linkage1.cpp test_inline_linkage2.cpp : <define>BOOST_NO_CXX11_VARIADIC_TEMPLATES ;
+ }
+
+ for local x in $(test_files_all)
+ {
+ link $(x) test_functions spooky ;
+ }
+ if $(single_test) != true
+ {
+ for local x in $(example_files)
+ {
+ link $(x) afio_pch spooky ;
+ }
+ }
+}
+else # run actual tests
+{
+ if [ os.name ] = "NT"
+ {
+ SHELL "rmdir /S /Q results_tests" ;
+ SHELL "mkdir results_tests" ;
+ }
+ else
+ {
+ SHELL "rm -rf results_tests" ;
+ SHELL "mkdir results_tests" ;
+ }
+ if "--test-all" in $(.argv) # run the whole test suite at once
+ {
+ test-suite afio
+ : [ run test_all.cpp test_functions spooky : $(VALGRIND_ARGS) --log_format=XML --log_sink=results_all.xml --log_level=all --report_level=no : : $(launcher) ]
+ ;
+ PRECIOUS test_all ;
+ }
+ else if "--test-each" in $(.argv) # run just the individual tests
+ {
+ for local file in $(test_files)
+ {
+ run $(file) test_functions spooky : $(VALGRIND_ARGS) --log_format=XML --log_sink=results_$(file).xml --log_level=all --report_level=no : : $(launcher) ;
+ PRECIOUS $(file) ;
+ }
+ }
+ else # otherwise run individual tests and the whole lot at once as a separate test
+ {
+ for local file in $(test_files_all)
+ {
+ run $(file) test_functions spooky : $(VALGRIND_ARGS) --log_format=XML --log_sink=results_$(file).xml --log_level=all --report_level=no : : $(launcher) ;
+ PRECIOUS $(file) ;
+ }
+ }
+}
diff --git a/attic/test/afio_pch.hpp b/attic/test/afio_pch.hpp
new file mode 100644
index 00000000..9a908b9f
--- /dev/null
+++ b/attic/test/afio_pch.hpp
@@ -0,0 +1,7 @@
+#ifndef BOOST_AFIO_PCH_HPP
+# define BOOST_AFIO_PCH_HPP
+# define BOOST_AFIO_ENABLE_BENCHMARKING_COMPLETION
+# include "boost/afio/afio.hpp"
+# include <iostream>
+# include <fstream>
+#endif
diff --git a/attic/test/asan.supp b/attic/test/asan.supp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/attic/test/asan.supp
diff --git a/attic/test/blacklist.supp b/attic/test/blacklist.supp
new file mode 100644
index 00000000..08968f04
--- /dev/null
+++ b/attic/test/blacklist.supp
@@ -0,0 +1,39 @@
+# Remember that this blacklist file is GLOBAL to all sanitizers
+# Be therefore extremely careful when considering to add a sanitizer
+# filter here instead of using a runtime suppression
+#
+# Remember also that filters here quite literally completely
+# remove instrumentation altogether, so filtering here means
+# that sanitizers such as tsan will false positive on problems
+# introduced by code filtered here.
+#
+# The main use for this file is ubsan, as it's the only sanitizer
+# without a runtime suppression facility.
+#
+# Be ESPECIALLY careful when filtering out entire source files!
+# Try if at all possible to filter only functions using fun:regex
+# Remember you must use mangled symbol names with fun:regex
+
+
+#### Compile time filters for ubsan ####
+
+## The well known ubsan failure in libstdc++ extant for years :)
+# Line 96:24: runtime error: load of value 4294967221, which is not a valid value for type 'std::_Ios_Fmtflags'
+fun:*_Ios_Fmtflags*
+
+# boost/any.hpp:259:16: runtime error: downcast of address 0x000004392e70 which does not point to an object of type 'any::holder<int>'
+fun:*any_cast*
+
+# boost/lexical_cast.hpp:1625:43: runtime error: downcast of address 0x7fbb4fffbce8 which does not point to an object of type 'buffer_t' (aka 'parser_buf<std::basic_streambuf<char, char_traits<char> >, char>')
+fun:*shl_input_streamable*
+
+
+
+
+#### Compile time filters for asan ####
+
+
+#### Compile time filters for msan ####
+
+
+#### Compile time filters for tsan ####
diff --git a/attic/test/drd.supp b/attic/test/drd.supp
new file mode 100644
index 00000000..591863b8
--- /dev/null
+++ b/attic/test/drd.supp
@@ -0,0 +1,88 @@
+{
+ boostasio1
+ drd:CondRaceErr
+ fun:pthread_cond_signal@*
+ ...
+ fun:_ZN5boost4asio6detail15task_io_service4postISt5_BindIFZN9triplegit8async_io11thread_pool7enqueueIS4_IFSt8functionIFSt10shared_ptrINS6_6detail15async_io_handleEESD_EESD_EEEENS6_6futureINSt9result_ofIFT_vEE4typeEEESK_EUlSA_INS6_13packaged_taskIFSD_vEEEEE_SS_EEEEvSK_
+ ...
+}
+{
+ boostasio2
+ drd:CondRaceErr
+ fun:pthread_cond_signal@*
+ ...
+ fun:_ZN5boost4asio6detail15task_io_service3runERNS_6system10error_codeE
+ ...
+}
+{
+ boostasio3
+ drd:CondRaceErr
+ fun:pthread_cond_signal@*
+ ...
+ fun:_ZN5boost4asio10io_service4postISt5_BindIFZN9triplegit8async_io11thread_pool7enqueueIPFivEEENS5_6futureINSt9result_ofIFT_vEE4typeEEESC_EUlSt10shared_ptrINS5_13packaged_taskIS8_EEEE_SK_EEEEvOSC_
+ ...
+}
+{
+ boostasio4
+ drd:CondRaceErr
+ fun:pthread_cond_signal@*
+ ...
+ fun:_ZN5boost4asio6detail15task_io_service4postISt5_BindIFZN9triplegit8async_io11thread_pool7enqueueIPFivEEENS6_6futureINSt9result_ofIFT_vEE4typeEEESD_EUlSt10shared_ptrINS6_13packaged_taskIS9_EEEE_SL_EEEEvSD_
+ ...
+}
+{
+ boost1
+ drd:ConflictingAccess
+ ...
+ fun:_ZN5boost6detail12shared_countD1Ev
+ fun:_ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE2EED1Ev
+ ...
+}
+{
+ boostthread1
+ drd:ConflictingAccess
+ ...
+ fun:_ZN5boost7promiseISt6vectorISt10shared_ptrIN9triplegit8async_io6detail15async_io_handleEESaIS7_EEE9lazy_initEv
+ fun:_ZN9triplegit8async_io6detail24when_all_count_completedEmSt10shared_ptrINS1_15async_io_handleEES2_INS1_30when_all_count_completed_stateEEm
+ ...
+}
+{
+ boostthread2
+ drd:ConflictingAccess
+ ...
+ fun:_ZN5boost6detail15sp_counted_base7destroyEv
+ fun:_ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE2EED1Ev
+ ...
+}
+{
+ boostthread3
+ drd:ConflictingAccess
+ ...
+ fun:_ZN5boost6detail17sp_counted_impl_pINS0_11task_objectISt8functionIFSt10shared_ptrIN9triplegit8async_io6detail15async_io_handleEEvEES9_EEED0Ev
+ fun:_ZNSt14__shared_countILN9__gnu_cxx12_Lock_policyE2EED1Ev
+ ...
+}
+{
+ boostthread4
+ drd:ConflictingAccess
+ ...
+ fun:_ZN9triplegit8async_io6detail24when_all_count_completedEmSt10shared_ptrINS1_15async_io_handleEES2_INS1_30when_all_count_completed_stateEEm
+ fun:_ZNSt17_Function_handlerIFSt4pairIbSt10shared_ptrIN9triplegit8async_io6detail15async_io_handleEEEmS6_ESt5_BindIFPFS7_mS6_S1_INS4_30when_all_count_completed_stateEEmESt12_PlaceholderILi1EESE_ILi2EESB_mEEE9_M_invokeERKSt9_Any_datamS6_
+ ...
+}
+{
+ boostthread5
+ drd:ConflictingAccess
+ ...
+ fun:_ZN5boost13packaged_taskIiEclEv
+ fun:_ZZN9triplegit8async_io11thread_pool7enqueueIPFivEEENS0_6futureINSt9result_ofIFT_vEE4typeEEES7_ENKUlSt10shared_ptrINS0_13packaged_taskIS3_EEEE_clESF_
+ ...
+}
+{
+ boostthread6
+ drd:ConflictingAccess
+ ...
+ fun:_ZN5boost7promiseISt6vectorISt10shared_ptrIN9triplegit8async_io6detail15async_io_handleEESaIS7_EEE9set_valueERKS9_
+ fun:_ZN9triplegit8async_io6detail24when_all_count_completedEmSt10shared_ptrINS1_15async_io_handleEES2_INS1_30when_all_count_completed_stateEEm
+ ...
+}
diff --git a/attic/test/json_encode.py b/attic/test/json_encode.py
new file mode 100644
index 00000000..43aee112
--- /dev/null
+++ b/attic/test/json_encode.py
@@ -0,0 +1,3 @@
+#!/usr/bin/python
+import json,sys,codecs
+sys.stdout.write(json.dumps(codecs.getreader('ISO-8859-1')(sys.stdin).read()))
diff --git a/attic/test/memcheck.supp b/attic/test/memcheck.supp
new file mode 100644
index 00000000..42d18aaf
--- /dev/null
+++ b/attic/test/memcheck.supp
@@ -0,0 +1,81 @@
+{
+ libstdc1
+ Memcheck:Addr8
+ fun:wcscmp
+ fun:_ZNSt10moneypunctIwLb0EED1Ev
+ fun:_ZNSt10moneypunctIwLb0EED0Ev
+ fun:_ZNSt6locale5_ImplD1Ev
+ fun:_ZNSt6localeD1Ev
+ fun:__cxa_finalize
+ ...
+ fun:__run_exit_handlers
+ fun:exit
+ fun:(below main)
+}
+{
+ libstdc2
+ Memcheck:Addr8
+ fun:wcscmp
+ fun:_ZNSt10moneypunctIwLb1EED1Ev
+ fun:_ZNSt10moneypunctIwLb1EED0Ev
+ fun:_ZNSt6locale5_ImplD1Ev
+ fun:_ZNSt6localeD1Ev
+ fun:__cxa_finalize
+ ...
+ fun:__run_exit_handlers
+ fun:exit
+ fun:(below main)
+}
+{
+ boost1
+ Memcheck:Leak
+ fun:malloc
+ fun:_ZN5boost6detail25get_once_per_thread_epochEv
+ ...
+ fun:_ZN5boost6detail23get_current_thread_dataEv
+ ...
+}
+{
+ boosthread1
+ Memcheck:Leak
+ ...
+ fun:calloc
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.2.5
+ fun:_ZN5boost6thread21start_thread_noexceptEv
+ ...
+}
+{
+ libgomp1
+ Memcheck:Leak
+ ...
+ fun:realloc
+ obj:/usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0
+ ...
+}
+{
+ libgomp2
+ Memcheck:Leak
+ ...
+ fun:malloc
+ obj:/usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0
+ ...
+}
+{
+ libgomp3
+ Memcheck:Leak
+ ...
+ fun:pthread_create@@GLIBC_2.2.5
+ obj:/usr/lib/x86_64-linux-gnu/libgomp.so.1.0.0
+ ...
+}
+{
+ boosttest1
+ Memcheck:Leak
+ ...
+ fun:calloc
+ fun:allocate_dtv
+ fun:_dl_allocate_tls
+ fun:pthread_create@@GLIBC_2.1
+ ...
+}
diff --git a/attic/test/msan.supp b/attic/test/msan.supp
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/attic/test/msan.supp
diff --git a/attic/test/test_all.cpp b/attic/test/test_all.cpp
new file mode 100644
index 00000000..81c4aed0
--- /dev/null
+++ b/attic/test/test_all.cpp
@@ -0,0 +1,32 @@
+// Make sure we properly define the main for the test suite
+#define BOOST_AFIO_TEST_ALL
+
+#include "test_functions.hpp"
+
+BOOST_AUTO_TEST_SUITE(all)
+
+#include "test_cpps.txt"
+/*
+#include "async_io_threadpool_test.cpp"
+#include "async_io_works_1_prime_test.cpp"
+#include "async_io_works_1_test.cpp"
+#include "async_io_works_64_test.cpp"
+#include "async_io_works_1_sync_test.cpp"
+#include "async_io_works_64_sync_test.cpp"
+#include "async_io_works_1_autoflush_test.cpp"
+#include "async_io_works_64_autoflush_test.cpp"
+#include "async_io_works_1_direct_test.cpp"
+#include "async_io_works_64_direct_test.cpp"
+#include "async_io_works_1_directsync_test.cpp"
+#include "async_io_works_64_directsync_test.cpp"
+#include "async_io_barrier_test.cpp"
+#include "async_io_errors_test.cpp"
+#include "async_io_torture_test.cpp"
+#include "async_io_torture_sync_test.cpp"
+#include "async_io_torture_autoflush_test.cpp"
+#include "async_io_torture_direct_test.cpp"
+#include "async_io_torture_direct_sync_test.cpp"
+#include "async_io_sync_test.cpp" // */
+BOOST_AUTO_TEST_SUITE_END()
+
+
diff --git a/attic/test/test_all_multiabi.cpp b/attic/test/test_all_multiabi.cpp
new file mode 100644
index 00000000..2c3f1ee6
--- /dev/null
+++ b/attic/test/test_all_multiabi.cpp
@@ -0,0 +1,36 @@
+// Reduce unit testing
+#define BOOST_AFIO_RUNNING_IN_CI 1
+// Use the Boost.Test emulation in Boost.BindLib as Boost.Test isn't multi ABI capable.
+#define BOOST_AFIO_USE_BOOST_UNIT_TEST 0
+// Have Boost.Spinlock also use Boost.BindLib
+#define SPINLOCK_STANDALONE 1
+
+#define STRINGIZE2(a) #a
+#define STRINGIZE(a, b) STRINGIZE2(a ## b)
+
+// Make unit test names be different
+#define BOOST_CATCH_AUTO_TEST_CASE_NAME(name) STRINGIZE(1_, name)
+
+#if 1
+// A copy of AFIO + unit tests completely standalone apart from Boost.Filesystem
+#define BOOST_AFIO_USE_BOOST_THREAD 0
+#define BOOST_AFIO_USE_BOOST_FILESYSTEM 1
+#define ASIO_STANDALONE 1
+#include "test_all.cpp"
+#undef BOOST_AFIO_USE_BOOST_THREAD
+#undef BOOST_AFIO_USE_BOOST_FILESYSTEM
+#undef ASIO_STANDALONE
+#endif
+
+// Force unit test utilities to be reincluded
+#undef BOOST_AFIO_TEST_FUNCTIONS_HPP
+#undef BOOST_CATCH_AUTO_TEST_CASE_NAME
+#define BOOST_CATCH_AUTO_TEST_CASE_NAME(name) STRINGIZE(2_, name)
+
+#if 1
+// A copy of AFIO + unit tests using Boost.Thread, Boost.Filesystem and Boost.ASIO
+#define BOOST_AFIO_USE_BOOST_THREAD 1
+#define BOOST_AFIO_USE_BOOST_FILESYSTEM 1
+// ASIO_STANDALONE undefined
+#include "test_all.cpp"
+#endif
diff --git a/attic/test/test_file_glob.bat b/attic/test/test_file_glob.bat
new file mode 100644
index 00000000..d1377fde
--- /dev/null
+++ b/attic/test/test_file_glob.bat
@@ -0,0 +1,6 @@
+@ECHO OFF
+
+ECHO. 2> "%~dp0test_cpps.txt"
+
+FOR %%G IN ("%~dp0\tests\*.cpp") DO ECHO #include "tests/%%~nxG" >> "%~dp0test_cpps.txt"
+
diff --git a/attic/test/test_file_glob.sh b/attic/test/test_file_glob.sh
new file mode 100755
index 00000000..044a407e
--- /dev/null
+++ b/attic/test/test_file_glob.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+> ${0%/*}/test_cpps.txt
+
+for filename in ${0%/*}/tests/*.cpp
+do
+ echo "#include \"${filename##${0%/*}/}\"" >> ${0%/*}/test_cpps.txt
+done
diff --git a/attic/test/test_functions.hpp b/attic/test/test_functions.hpp
new file mode 100644
index 00000000..7606f6a8
--- /dev/null
+++ b/attic/test/test_functions.hpp
@@ -0,0 +1,926 @@
+/* Unit tests for TripleGit
+(C) 2013 Niall Douglas http://www.nedprod.com/
+Created: Feb 2013
+*/
+
+#ifndef BOOST_AFIO_TEST_FUNCTIONS_HPP
+#define BOOST_AFIO_TEST_FUNCTIONS_HPP
+
+// Uses a ton more memory and is many orders of magnitude slower, but no longer optional.
+#define DEBUG_TORTURE_TEST 1
+
+// Reduces CPU cores used to execute, which can be useful for certain kinds of race condition.
+//#define MAXIMUM_TEST_CPUS 1
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include "boost/afio/afio.hpp"
+
+#ifdef __MINGW32__
+#include <stdlib.h> // To pull in __MINGW64_VERSION_MAJOR
+#ifndef __MINGW64_VERSION_MAJOR
+// Mingw32 doesn't define putenv() needed by Boost.Test
+extern "C" int putenv(char*);
+#endif
+// Mingw doesn't define tzset() either
+extern "C" void tzset(void);
+#endif
+
+#include <utility>
+#include <sstream>
+#include <iostream>
+#include <algorithm>
+#include <deque>
+#include <set>
+#include <unordered_set>
+#include <random>
+#include <fstream>
+#include "../detail/SpookyV2.h"
+#include "Aligned_Allocator.hpp"
+#include "boost/afio/v2/detail/valgrind/valgrind.h"
+#include <time.h>
+
+#ifdef BOOST_AFIO_INCLUDE_SPOOKY_IMPL
+#include "../detail/SpookyV2.cpp"
+#endif
+
+#if defined(__has_feature)
+# if __has_feature(thread_sanitizer)
+# define RUNNING_ON_SANITIZER 1
+# endif
+#endif
+#ifndef RUNNING_ON_SANITIZER
+# define RUNNING_ON_SANITIZER 0
+#endif
+
+//if we're building the tests all together don't define the test main
+#ifndef BOOST_AFIO_TEST_ALL
+# define BOOST_TEST_MAIN //must be defined before unit_test.hpp is included
+#endif
+#ifndef BOOST_TEST_MODULE
+#define BOOST_TEST_MODULE Boost.AFIO
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(disable: 4535) // calling _set_se_translator() requires /EHa
+#endif
+
+#ifndef BOOST_AFIO_USE_BOOST_UNIT_TEST
+# define BOOST_AFIO_USE_BOOST_UNIT_TEST 0
+#endif
+#if BOOST_AFIO_USE_BOOST_UNIT_TEST
+# include "boost/test/unit_test.hpp"
+# include "boost/test/unit_test_monitor.hpp"
+
+//define a simple macro to check any exception using Boost.Test
+#define BOOST_AFIO_CHECK_THROWS(expr)\
+try{\
+ expr;\
+ BOOST_FAIL("Exception was not thrown");\
+}catch(...){/*BOOST_CHECK(true);*/}
+#define BOOST_AFIO_CHECK_NO_THROW(expr)\
+try{\
+ expr;\
+ /*BOOST_CHECK(true);*/ \
+}catch(...){BOOST_FAIL("Exception was thrown");}
+
+#else
+# include "../include/boost/afio/bindlib/include/boost/test/unit_test.hpp"
+# define BOOST_AFIO_CHECK_THROWS(expr) BOOST_CHECK_THROWS(expr)
+# define BOOST_AFIO_CHECK_NO_THROW(expr) BOOST_CHECK_NO_THROW(expr)
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+// Force maximum CPUs available to threads in this process, if available on this platform
+#ifdef MAXIMUM_TEST_CPUS
+static inline void set_maximum_cpus(size_t no=MAXIMUM_TEST_CPUS)
+{
+#ifdef WIN32
+ DWORD_PTR mask=0;
+ for(size_t n=0; n<no; n++)
+ mask|=(size_t)1<<n;
+ if(!SetProcessAffinityMask(GetCurrentProcess(), mask))
+ {
+ std::cerr << "ERROR: Failed to set process affinity mask due to reason " << GetLastError() << std::endl;
+ abort();
+ }
+#endif
+#ifdef __linux__
+ cpu_set_t mask;
+ CPU_ZERO(&mask);
+ for(size_t n=0; n<no; n++)
+ CPU_SET(n, &mask);
+ sched_setaffinity(getpid(), sizeof(mask), &mask);
+#endif
+}
+#else
+static inline void set_maximum_cpus(size_t no=0)
+{
+}
+#endif
+
+// Boost.Test uses alarm() to timeout tests, which is nearly useless. Hence do our own.
+static inline void watchdog_thread(size_t timeout, std::shared_ptr<std::pair<atomic<bool>, condition_variable>> cv)
+{
+ detail::set_threadname("watchdog_thread");
+ if(getenv("BOOST_AFIO_TEST_DISABLE_WATCHDOG_TIMER"))
+ return;
+ bool docountdown=timeout>10;
+ if(docountdown) timeout-=10;
+ chrono::duration<size_t, ratio<1, 1>> d(timeout);
+ mutex m;
+ unique_lock<mutex> lock(m);
+ if(!cv->second.wait_for(lock, d, [cv]{return !!cv->first;}))
+ {
+ if(docountdown)
+ {
+ std::cerr << "Test about to time out ...";
+ d=chrono::duration<size_t, ratio<1, 1>>(1);
+ for(size_t n=0; n<10; n++)
+ {
+ if(cv->second.wait_for(lock, d, [cv]{return !!cv->first;}))
+ return;
+ std::cerr << " " << 10-n;
+ }
+ std::cerr << " ... ";
+ }
+ BOOST_CHECK_MESSAGE(false, "Test timed out");
+ std::cerr << "Test timed out, aborting" << std::endl;
+ abort();
+ }
+}
+
+#define BOOST_AFIO_TRAP_EXCEPTIONS_IN_TEST(callable) \
+ try { callable; } \
+ catch(const BOOST_AFIO_V2_NAMESPACE::system_error &e) { std::cerr << "ERROR: unit test exits via system_error code " << e.code().value() << " (" << e.what() << ")" << std::endl; throw; } \
+ catch(const std::exception &e) { std::cerr << "ERROR: unit test exits via exception (" << e.what() << ")" << std::endl; throw; }
+#if BOOST_AFIO_USE_BOOST_UNIT_TEST
+template<class T> inline void wrap_test_method(T &t)
+{
+ BOOST_AFIO_TRAP_EXCEPTIONS_IN_TEST(t.test_method())
+ catch(const boost::execution_aborted &) { throw; }
+ catch(...) { std::cerr << "ERROR: unit test exits via unknown exception" << std::endl; throw; }
+}
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#if BOOST_AFIO_USE_BOOST_UNIT_TEST
+// Define a unit test description and timeout
+#ifdef BOOST_TEST_UNIT_TEST_SUITE_IMPL_HPP_071894GER
+#define BOOST_AFIO_AUTO_TEST_CASE_REGISTRAR(test_name) \
+BOOST_AUTO_TU_REGISTRAR( test_name )( \
+ boost::unit_test::make_test_case( \
+ &BOOST_AUTO_TC_INVOKER( test_name ), #test_name ), \
+ boost::unit_test::ut_detail::auto_tc_exp_fail< \
+ BOOST_AUTO_TC_UNIQUE_ID( test_name )>::instance()->value() ); \
+ \
+void test_name::test_method() \
+
+#else
+#define BOOST_AFIO_AUTO_TEST_CASE_REGISTRAR(test_name) \
+BOOST_AUTO_TU_REGISTRAR( test_name )( \
+ boost::unit_test::make_test_case( \
+ &BOOST_AUTO_TC_INVOKER( test_name ), \
+ #test_name, __FILE__, __LINE__ ), \
+ boost::unit_test::decorator::collector::instance() ); \
+ \
+void test_name::test_method() \
+
+#endif
+
+#define BOOST_AFIO_AUTO_TEST_CASE(test_name, desc, _timeout) \
+struct test_name : public BOOST_AUTO_TEST_CASE_FIXTURE { void test_method(); }; \
+ \
+static void BOOST_AUTO_TC_INVOKER( test_name )() \
+{ \
+ test_name t; \
+ size_t timeout=_timeout; \
+ if(RUNNING_ON_VALGRIND) { \
+ VALGRIND_PRINTF("BOOST.AFIO TEST INVOKER: Unit test running in valgrind so tripling timeout\n"); \
+ timeout*=3; \
+ } \
+ if(RUNNING_ON_SANITIZER) { \
+ BOOST_TEST_MESSAGE("BOOST.AFIO TEST INVOKER: Unit test running in thread sanitiser so tripling timeout"); \
+ timeout*=3; \
+ } \
+ /*boost::unit_test::unit_test_monitor_t::instance().p_timeout.set(timeout);*/ \
+ BOOST_TEST_MESSAGE(desc); \
+ std::cout << std::endl << desc << std::endl; \
+ try { BOOST_AFIO_V2_NAMESPACE::filesystem::remove_all("testdir"); } catch(...) { } \
+ BOOST_AFIO_V2_NAMESPACE::set_maximum_cpus(); \
+ auto cv=std::make_shared<std::pair<BOOST_AFIO_V2_NAMESPACE::atomic<bool>, BOOST_AFIO_V2_NAMESPACE::condition_variable>>(); \
+ cv->first=false; \
+ BOOST_AFIO_V2_NAMESPACE::thread watchdog(BOOST_AFIO_V2_NAMESPACE::watchdog_thread, timeout, cv); \
+ boost::unit_test::unit_test_monitor_t::instance().execute([&]() -> int { BOOST_AFIO_V2_NAMESPACE::wrap_test_method(t); cv->first=true; cv->second.notify_all(); watchdog.join(); return 0; }); \
+} \
+ \
+struct BOOST_AUTO_TC_UNIQUE_ID( test_name ) {}; \
+ \
+BOOST_AFIO_AUTO_TEST_CASE_REGISTRAR ( test_name )
+
+#else
+
+#define BOOST_AFIO_AUTO_TEST_CASE(__test_name, __desc, __timeout) \
+static void __test_name ## _impl(); \
+CATCH_TEST_CASE(BOOST_CATCH_AUTO_TEST_CASE_NAME(__test_name), __desc) \
+{ \
+ size_t timeout=__timeout; \
+ if(RUNNING_ON_VALGRIND) { \
+ VALGRIND_PRINTF("BOOST.AFIO TEST INVOKER: Unit test running in valgrind so tripling timeout\n"); \
+ timeout*=3; \
+ } \
+ if(RUNNING_ON_SANITIZER) { \
+ BOOST_TEST_MESSAGE("BOOST.AFIO TEST INVOKER: Unit test running in thread sanitiser so tripling timeout"); \
+ timeout*=3; \
+ } \
+ BOOST_TEST_MESSAGE(__desc); \
+ std::cout << std::endl << __desc << std::endl; \
+ try { BOOST_AFIO_V2_NAMESPACE::filesystem::remove_all("testdir"); } catch(...) { } \
+ BOOST_AFIO_V2_NAMESPACE::set_maximum_cpus(); \
+ auto cv=std::make_shared<std::pair<BOOST_AFIO_V2_NAMESPACE::atomic<bool>, BOOST_AFIO_V2_NAMESPACE::condition_variable>>(); \
+ cv->first=false; \
+ struct __deleter_t { \
+ std::shared_ptr<std::pair<BOOST_AFIO_V2_NAMESPACE::atomic<bool>, BOOST_AFIO_V2_NAMESPACE::condition_variable>> cv; \
+ BOOST_AFIO_V2_NAMESPACE::thread watchdog; \
+ __deleter_t(std::shared_ptr<std::pair<BOOST_AFIO_V2_NAMESPACE::atomic<bool>, BOOST_AFIO_V2_NAMESPACE::condition_variable>> _cv, \
+ BOOST_AFIO_V2_NAMESPACE::thread &&_watchdog) : cv(std::move(_cv)), watchdog(std::move(_watchdog)) { } \
+ ~__deleter_t() { cv->first=true; cv->second.notify_all(); watchdog.join(); } \
+ } __deleter(cv, BOOST_AFIO_V2_NAMESPACE::thread(BOOST_AFIO_V2_NAMESPACE::watchdog_thread, timeout, cv)); \
+ BOOST_AFIO_TRAP_EXCEPTIONS_IN_TEST(__test_name ## _impl()) \
+ catch(...) { std::cerr << "ERROR: unit test exits via unknown exception" << std::endl; throw; } \
+} \
+static void __test_name ## _impl() \
+
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+// From http://burtleburtle.net/bob/rand/smallprng.html
+typedef unsigned int u4;
+typedef struct ranctx { u4 a; u4 b; u4 c; u4 d; } ranctx;
+
+#define rot(x,k) (((x)<<(k))|((x)>>(32-(k))))
+static u4 ranval(ranctx *x) {
+ u4 e = x->a - rot(x->b, 27);
+ x->a = x->b ^ rot(x->c, 17);
+ x->b = x->c + x->d;
+ x->c = x->d + e;
+ x->d = e + x->a;
+ return x->d;
+}
+
+static void raninit(ranctx *x, u4 seed) {
+ u4 i;
+ x->a = 0xf1ea5eed, x->b = x->c = x->d = seed;
+ for (i = 0; i < 20; ++i) {
+ (void) ranval(x);
+ }
+}
+
+static void dofilter(atomic<size_t> *callcount, detail::OpType, future<> &) { ++*callcount; }
+static void checkwrite(detail::OpType, handle *h, const detail::io_req_impl<true> &req, off_t offset, size_t idx, size_t no, const error_code &, size_t transferred)
+{
+ size_t amount=0;
+ for(auto &i: req.buffers)
+ amount+=asio::buffer_size(i);
+ if(offset!=0)
+ BOOST_CHECK(offset==0);
+ if(transferred!=amount)
+ BOOST_CHECK(transferred==amount);
+}
+static int donothing(atomic<size_t> *callcount, int i) { ++*callcount; return i; }
+static void _1000_open_write_close_deletes(dispatcher_ptr dispatcher, size_t bytes)
+{
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ std::vector<char, detail::aligned_allocator<char, 4096>> towrite(bytes, 'N');
+ assert(!(((size_t) &towrite.front()) & 4095));
+
+ // Wait for six seconds to let filing system recover and prime SpeedStep
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<6);
+
+ // Install a file open filter
+ dispatcher->post_op_filter_clear();
+ atomic<size_t> filtercount(0);
+ std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_t>>> filters;
+ filters.push_back(std::make_pair<detail::OpType, std::function<dispatcher::filter_t>>(detail::OpType::file, std::bind(dofilter, &filtercount, std::placeholders::_1, std::placeholders::_2)));
+ dispatcher->post_op_filter(filters);
+
+ // Install a write filter
+ std::vector<std::pair<detail::OpType, std::function<dispatcher::filter_readwrite_t>>> rwfilters;
+ rwfilters.push_back(std::make_pair(detail::OpType::Unknown, std::function<dispatcher::filter_readwrite_t>(checkwrite)));
+ dispatcher->post_readwrite_filter(rwfilters);
+
+ // Start opening 1000 files
+ begin=chrono::high_resolution_clock::now();
+ std::vector<path_req> manyfilereqs;
+ manyfilereqs.reserve(1000);
+ for(size_t n=0; n<1000; n++)
+ manyfilereqs.push_back(path_req::relative(mkdir, to_string(n), file_flags::create|file_flags::write));
+ auto manyopenfiles(dispatcher->file(manyfilereqs));
+
+ // Write to each of those 1000 files as they are opened
+ std::vector<io_req<const char>> manyfilewrites;
+ manyfilewrites.reserve(manyfilereqs.size());
+ auto openit=manyopenfiles.begin();
+ for(size_t n=0; n<manyfilereqs.size(); n++)
+ manyfilewrites.push_back(io_req<const char>(*openit++, &towrite.front(), towrite.size(), 0));
+ auto manywrittenfiles(dispatcher->write(manyfilewrites));
+
+ // Close each of those 1000 files once one byte has been written
+ auto manyclosedfiles(dispatcher->close(manywrittenfiles));
+
+ // Delete each of those 1000 files once they are closed
+ auto it(manyclosedfiles.begin());
+ for(auto &i: manyfilereqs)
+ i.precondition=dispatcher->depends(*it++, mkdir);
+ auto manydeletedfiles(dispatcher->rmfile(manyfilereqs));
+
+ // As a test of call() which involves significant template metaprogramming, have a do nothing callback
+ atomic<size_t> callcount(0);
+ typedef int (*callable_type)(atomic<size_t> *, int);
+ callable_type callable=donothing;
+ std::vector<std::function<int()>> callables;
+ callables.reserve(1000);
+ for(size_t n=0; n<1000; n++)
+ callables.push_back(std::bind(callable, &callcount, 78));
+ auto manycallbacks(dispatcher->call(manydeletedfiles, std::move(callables)));
+ auto dispatched=chrono::high_resolution_clock::now();
+ std::cout << "There are now " << std::dec << dispatcher->fd_count() << " handles open with a queue depth of " << dispatcher->wait_queue_depth() << std::endl;
+
+ // Wait for all files to open
+ when_all_p(manyopenfiles.begin(), manyopenfiles.end()).get();
+ auto openedsync=chrono::high_resolution_clock::now();
+ // Wait for all files to write
+ when_all_p(manywrittenfiles.begin(), manywrittenfiles.end()).get();
+ auto writtensync=chrono::high_resolution_clock::now();
+ // Wait for all files to close
+ when_all_p(manyclosedfiles.begin(), manyclosedfiles.end()).get();
+ auto closedsync=chrono::high_resolution_clock::now();
+ // Wait for all files to delete
+ when_all_p(manydeletedfiles.begin(), manydeletedfiles.end()).get();
+ auto deletedsync=chrono::high_resolution_clock::now();
+ // Wait for all callbacks
+ when_all_p(manycallbacks.begin(), manycallbacks.end()).get();
+
+ auto end=deletedsync;
+ auto rmdir(dispatcher->rmdir(path_req("testdir")));
+
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to do all operations" << std::endl;
+ diff=chrono::duration_cast<secs_type>(dispatched-begin);
+ std::cout << " It took " << diff.count() << " secs to dispatch all operations" << std::endl;
+ diff=chrono::duration_cast<secs_type>(end-dispatched);
+ std::cout << " It took " << diff.count() << " secs to finish all operations" << std::endl << std::endl;
+
+ diff=chrono::duration_cast<secs_type>(openedsync-begin);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file opens per sec" << std::endl;
+ diff=chrono::duration_cast<secs_type>(writtensync-openedsync);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file writes per sec" << std::endl;
+ diff=chrono::duration_cast<secs_type>(closedsync-writtensync);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file closes per sec" << std::endl;
+ diff=chrono::duration_cast<secs_type>(deletedsync-closedsync);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file deletions per sec" << std::endl;
+
+ // Fetch any outstanding error
+ when_all_p(rmdir).wait();
+ BOOST_CHECK((callcount==1000U));
+ BOOST_CHECK((filtercount==1000U));
+}
+
+#ifdef DEBUG_TORTURE_TEST
+ struct Op
+ {
+ bool write;
+ std::vector<char *> data;
+ io_req<char> req;
+ };
+#else
+ struct Op
+ {
+ Hash256 hash; // Only used for reading
+ bool write;
+ ranctx seed;
+ io_req<char> req;
+ };
+#endif
+
+#ifdef DEBUG_TORTURE_TEST
+static u4 mkfill() { static char ret='0'; if(ret+1>'z') ret='0'; return ret++; }
+#endif
+
+static void evil_random_io(dispatcher_ptr dispatcher, size_t no, size_t bytes, size_t alignment=0)
+{
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+
+ detail::aligned_allocator<char, 4096> aligned_allocator;
+ std::vector<std::vector<char, detail::aligned_allocator<char, 4096>>> towrite(no);
+ std::vector<char *> towriteptrs(no);
+ std::vector<size_t> towritesizes(no);
+
+#ifdef DEBUG_TORTURE_TEST
+ std::vector<std::vector<Op>> todo(no);
+#else
+ static_assert(!(sizeof(PadSizeToMultipleOf<Op, 32>)&31), "Op's stored size must be a multiple of 32 bytes");
+ std::vector<std::vector<PadSizeToMultipleOf<Op, 32>, detail::aligned_allocator<Op, 32>>> todo(no);
+#endif
+
+ for(size_t n=0; n<no; n++)
+ {
+ towrite[n].reserve(bytes);
+ towrite[n].resize(bytes);
+ assert(!(((size_t) &towrite[n].front()) & 4095));
+ towriteptrs[n]=&towrite[n].front();
+ towritesizes[n]=bytes;
+ }
+ // We create no lots of random writes and reads representing about 100% of bytes
+ // We simulate what we _ought_ to see appear in storage during the test and
+ // SHA256 out the results
+ // We then replay the same with real storage to see if it matches
+ auto begin=chrono::high_resolution_clock::now();
+#pragma omp parallel for
+ for(ptrdiff_t n=0; n<(ptrdiff_t) no; n++)
+ {
+ ranctx gen;
+ raninit(&gen, 0x78adbcff^(u4) n);
+ for(off_t bytessofar=0; bytessofar<bytes;)
+ {
+ Op op;
+ u4 r=ranval(&gen), toissue=(r>>24) & 15;
+ size_t thisbytes=0, m;
+ if(!toissue) toissue=1;
+ op.write=bytessofar<bytes/4 ? true : !(r&(1<<31));
+ op.req.where=r & (bytes-1);
+ if(op.req.where>bytes-1024*1024) op.req.where=bytes-1024*1024;
+ if(alignment)
+ op.req.where&=~(alignment-1);
+#ifdef DEBUG_TORTURE_TEST
+ u4 fillvalue=mkfill();
+ fillvalue|=fillvalue<<8;
+ fillvalue|=fillvalue<<16;
+#else
+ ranctx writeseed=op.seed=gen;
+#endif
+ for(m=0; m<toissue; m++)
+ {
+ u4 s=ranval(&gen) & ((256*1024-1)&~63); // Must be a multiple of 64 bytes for SHA256
+ if(s<64) s=64;
+ if(alignment)
+ s=(s+4095)&~(alignment-1);
+ if(thisbytes+s>1024*1024) break;
+ char *buffertouse=(char *)((size_t)towriteptrs[n]+(size_t) op.req.where+thisbytes);
+#ifdef DEBUG_TORTURE_TEST
+ op.data.push_back(aligned_allocator.allocate(s));
+ char *buffer=op.data.back();
+#endif
+ if(op.write)
+ {
+ for(size_t x=0; x<s; x+=4)
+#ifndef DEBUG_TORTURE_TEST
+ *(u4 *)(buffer+thisbytes+x)=ranval(&writeseed);
+#else
+ *(u4 *)(buffer+x)=fillvalue;
+ memcpy((char *)((size_t)towriteptrs[n]+(size_t) op.req.where+thisbytes), buffer, s);
+ buffertouse=buffer;
+#endif
+ }
+#ifdef DEBUG_TORTURE_TEST
+ else
+ memcpy(buffer, (char *)((size_t)towriteptrs[n]+(size_t) op.req.where+thisbytes), s);
+#endif
+ thisbytes+=s;
+ op.req.buffers.push_back(asio::mutable_buffer(buffertouse, s));
+ }
+#ifndef DEBUG_TORTURE_TEST
+ if(!op.write)
+ {
+ op.hash=Hash256();
+ op.hash.AddSHA256To((const char *)(((size_t)towriteptrs[n]+(size_t) op.req.where)), thisbytes);
+ //__sha256_osol((const char *)(((size_t)towriteptrs[n]+(size_t) op.req.where)), thisbytes);
+#ifdef _DEBUG
+ std::cout << "<=SHA256 of " << thisbytes << " bytes at " << op.req.where << " is " << op.hash.asHexString() << std::endl;
+#endif
+ }
+#endif
+#ifdef _DEBUG
+ // Quickly make sure none of these exceed 10Mb
+ off_t end=op.req.where;
+ for(auto &b: op.req.buffers)
+ end+=asio::buffer_size(b);
+ assert(end<=bytes);
+#endif
+ todo[n].push_back(std::move(op));
+ bytessofar+=thisbytes;
+ }
+ }
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to simulate torture test in RAM" << std::endl;
+ begin=chrono::high_resolution_clock::now(); // start timer for hashes
+
+ // a vector to hold the hash values from SpookyHash
+ //SpookyHash returns 2 64bit integers for a 128 bit hash, so we store them as a pair
+ std::vector<std::pair<uint64, uint64>> memhashes(no);
+ // variables to seed and return the hashed values
+ uint64 hash1, hash2, seed;
+ seed = 1; //initialize the seed value. Completely arbitrary, but it needs to remain consistent
+
+ for(size_t i = 0; i < no; ++i)
+ {
+ // set up seeds and a variables to store hash values
+ hash1 = seed;
+ hash2 = seed;
+
+ //hash the data from towriteptrs
+ SpookyHash::Hash128(towriteptrs[i], towritesizes[i], &hash1, &hash2);
+
+ // store the hash values for this data in memhashes for later comparison
+ memhashes[i]= std::make_pair(hash1, hash2);
+
+ }
+
+ end=chrono::high_resolution_clock::now(); // end timer for hashes
+ diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to hash the results" << std::endl;
+ for(size_t n=0; n<no; n++)
+ memset(towriteptrs[n], 0, towritesizes[n]);
+
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ // Wait for three seconds to let filing system recover and prime SpeedStep
+ //begin=chrono::high_resolution_clock::now();
+ //while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<3);
+
+ // Open our test files
+ begin=chrono::high_resolution_clock::now();
+ std::vector<path_req> manyfilereqs;
+ manyfilereqs.reserve(no);
+ for(size_t n=0; n<no; n++)
+ manyfilereqs.push_back(path_req::relative(mkdir, to_string(n), file_flags::create|file_flags::read_write));
+ auto manyopenfiles(dispatcher->file(manyfilereqs));
+ std::vector<off_t> sizes(no, bytes);
+ auto manywrittenfiles(dispatcher->truncate(manyopenfiles, sizes));
+#if defined(_DEBUG) && 0
+ for(size_t n=0; n<manywrittenfiles.size(); n++)
+ std::cout << n << ": " << manywrittenfiles[n].id << " (" << when_all_p(manywrittenfiles[n]).get().front()->path() << ") " << std::endl;
+#endif
+ // Schedule a replay of our in-RAM simulation
+
+ spinlock<size_t> failureslock;
+ std::deque<std::pair<const Op *, size_t>> failures;
+ auto checkHash=[&failureslock, &failures](Op &op, char *base, size_t, future<> _h) -> std::pair<bool, handle_ptr> {
+ const char *data=(const char *)(((size_t) base+(size_t) op.req.where));
+ size_t idxoffset=0;
+
+ for(size_t m=0; m<op.req.buffers.size(); m++)
+ {
+ const char *buffer=op.data[m];
+ size_t idx;
+ for(idx=0; idx < asio::buffer_size(op.req.buffers[m]); idx++)
+ {
+ if(data[idx]!=buffer[idx])
+ {
+ {
+ lock_guard<decltype(failureslock)> h(failureslock);
+ failures.push_back(std::make_pair(&op, idxoffset+idx));
+ }
+#ifdef _DEBUG
+ std::string contents(data, 8), shouldbe(buffer, 8);
+ std::cout << "Contents of file at " << op.req.where << " +" << idx << " contains " << contents << " instead of " << shouldbe << std::endl;
+#endif
+ break;
+ }
+ }
+ if(idx!=asio::buffer_size(op.req.buffers[m])) break;
+ data+=asio::buffer_size(op.req.buffers[m]);
+ idxoffset+=asio::buffer_size(op.req.buffers[m]);
+ }
+ return std::make_pair(true, _h.get_handle());
+ };
+#pragma omp parallel for
+ for(ptrdiff_t n=0; n<(ptrdiff_t) no; n++)
+ {
+ for(Op &op: todo[n])
+ {
+ op.req.precondition=manywrittenfiles[n];
+ if(op.write)
+ {
+ manywrittenfiles[n]=dispatcher->write(op.req);
+ }
+ else
+ manywrittenfiles[n]=dispatcher->completion(dispatcher->read(op.req), std::pair<async_op_flags, std::function<dispatcher::completion_t>>(async_op_flags::none, std::bind(checkHash, std::ref(op), towriteptrs[n], std::placeholders::_1, std::placeholders::_2)));
+ }
+ // After replay, read the entire file into memory
+ manywrittenfiles[n]=dispatcher->read(io_req<char>(manywrittenfiles[n], towriteptrs[n], towritesizes[n], 0));
+ }
+
+ // Close each of those files
+ auto manyclosedfiles(dispatcher->close(manywrittenfiles));
+ auto dispatched=chrono::high_resolution_clock::now();
+ std::cout << "There are now " << std::dec << dispatcher->fd_count() << " handles open with a queue depth of " << dispatcher->wait_queue_depth() << std::endl;
+
+ // Wait for all files to open
+ when_all_p(manyopenfiles.begin(), manyopenfiles.end()).get();
+ auto openedsync=chrono::high_resolution_clock::now();
+ // Wait for all files to write
+ when_all_p(manywrittenfiles.begin(), manywrittenfiles.end()).get();
+ auto writtensync=chrono::high_resolution_clock::now();
+ // Wait for all files to close
+ when_all_p(manyclosedfiles.begin(), manyclosedfiles.end()).get();
+ auto closedsync=chrono::high_resolution_clock::now();
+ end=closedsync;
+
+ diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "It took " << diff.count() << " secs to do all operations" << std::endl;
+ diff=chrono::duration_cast<secs_type>(dispatched-begin);
+ std::cout << " It took " << diff.count() << " secs to dispatch all operations" << std::endl;
+ diff=chrono::duration_cast<secs_type>(end-dispatched);
+ std::cout << " It took " << diff.count() << " secs to finish all operations" << std::endl << std::endl;
+ off_t readed=0, written=0;
+ size_t ops=0;
+ diff=chrono::duration_cast<secs_type>(end-begin);
+ for(auto &i: manyclosedfiles)
+ {
+ readed+=when_all_p(i).get().front()->read_count();
+ written+=when_all_p(i).get().front()->write_count();
+ }
+ for(ptrdiff_t n=0; n<(ptrdiff_t) no; n++)
+ ops+=todo[n].size();
+ std::cout << "We read " << readed << " bytes and wrote " << written << " bytes during " << ops << " operations." << std::endl;
+ std::cout << " That makes " << (readed+written)/diff.count()/1024/1024 << " Mb/sec" << std::endl;
+
+ diff=chrono::duration_cast<secs_type>(openedsync-begin);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file opens per sec" << std::endl;
+ diff=chrono::duration_cast<secs_type>(writtensync-openedsync);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file reads and writes per sec" << std::endl;
+ diff=chrono::duration_cast<secs_type>(closedsync-writtensync);
+ std::cout << "It took " << diff.count() << " secs to do " << manyfilereqs.size()/diff.count() << " file closes per sec" << std::endl;
+
+ BOOST_CHECK(failures.empty());
+ if(!failures.empty())
+ {
+ std::cerr << "The following hash failures occurred:" << std::endl;
+ while(!failures.empty())
+ {
+ std::pair<const Op *, size_t> &failedop=failures.front();
+ size_t _bytes=0;
+ for(auto &b: failedop.first->req.buffers)
+ _bytes+=asio::buffer_size(b);
+ std::cerr << " " << (failedop.first->write ? "Write to" : "Read from") << " " << to_string(failedop.first->req.where) << " at offset " << failedop.second << " into bytes " << _bytes << std::endl;
+ failures.pop_front();
+ }
+ }
+ BOOST_TEST_MESSAGE("Checking if the final files have exactly the right contents ... this may take a bit ...");
+ std::cout << "Checking if the final files have exactly the right contents ... this may take a bit ..." << std::endl;
+ {
+ // a vector for holding hash results from SpookyHash
+ //SpookyHash returns 2 64bit integers for a 128 bit hash, so we store them as a pair
+ std::vector<std::pair<uint64, uint64>> filehashes(no);
+
+ for(size_t i = 0; i < no; ++i)
+ {
+ // set up seeds and a variables to store hash values
+ hash1 = seed;
+ hash2 = seed;
+
+ // hash the data from towriteptrs
+ SpookyHash::Hash128(towriteptrs[i], towritesizes[i], &hash1, &hash2);
+
+ // store the hash values for this data in filehashes for a later comparison
+ filehashes[i]= std::make_pair(hash1, hash2);
+
+ }
+ for(size_t n=0; n<no; n++)
+ if(memhashes[n]!=filehashes[n]) // compare hash values from ram and actual IO
+ {
+ std::string failmsg("File "+to_string(n)+" contents were not what they were supposed to be!");
+ BOOST_TEST_MESSAGE(failmsg.c_str());
+ std::cerr << failmsg << std::endl;
+ }
+ }
+#ifdef DEBUG_TORTURE_TEST
+ for(ptrdiff_t n=0; n<(ptrdiff_t) no; n++)
+ {
+ for(Op &op: todo[n])
+ {
+ for(auto &i: op.data)
+ aligned_allocator.deallocate(i, 0);
+ }
+ }
+#endif
+ // Delete each of those files once they are closed
+ auto it(manyclosedfiles.begin());
+ for(auto &i: manyfilereqs)
+ i.precondition=dispatcher->depends(*it++, mkdir);
+ auto manydeletedfiles(dispatcher->rmfile(manyfilereqs));
+ // Wait for all files to delete
+ when_all_p(manydeletedfiles.begin(), manydeletedfiles.end()).get();
+ auto rmdir(dispatcher->rmdir(path_req("testdir")));
+ // Fetch any outstanding error
+ when_all_p(rmdir).get();
+}
+
+
+static std::ostream &operator<<(std::ostream &s, const chrono::system_clock::time_point &ts)
+{
+ char buf[64];
+ struct tm *t;
+ size_t len=sizeof(buf);
+ size_t ret;
+ time_t v=chrono::system_clock::to_time_t(ts);
+ chrono::system_clock::duration remainder(ts-chrono::system_clock::from_time_t(v));
+
+#ifdef _MSC_VER
+ _tzset();
+#else
+ tzset();
+#endif
+ if ((t=localtime(&v)) == NULL)
+ {
+ s << "<bad timespec>";
+ return s;
+ }
+
+ ret = strftime(buf, len, "%Y-%m-%d %H:%M:%S", t);
+ if (ret == 0)
+ {
+ s << "<bad timespec>";
+ return s;
+ }
+ //len -= ret - 1;
+
+ size_t end=strlen(buf);
+ sprintf(&buf[end], "%f", remainder.count()/((double) chrono::system_clock::period::den / chrono::system_clock::period::num));
+ memmove(&buf[end], &buf[end+1], strlen(&buf[end]));
+ s << buf;
+
+ return s;
+}
+
+static stat_t print_stat(handle_ptr h)
+{
+ using namespace boost::afio;
+ auto entry=h->lstat(metadata_flags::All);
+ std::cout << "Entry " << h->path(true) << " is a ";
+ switch(entry.st_type)
+ {
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ case filesystem::file_type::symlink_file:
+ std::cout << "link";
+ break;
+ case filesystem::file_type::directory_file:
+ std::cout << "directory";
+ break;
+ case filesystem::file_type::regular_file:
+ std::cout << "file";
+ break;
+ default:
+ std::cout << "unknown";
+ break;
+#else
+ case filesystem::file_type::symlink:
+ std::cout << "link";
+ break;
+ case filesystem::file_type::directory:
+ std::cout << "directory";
+ break;
+ case filesystem::file_type::regular:
+ std::cout << "file";
+ break;
+ default:
+ std::cout << "unknown";
+ break;
+#endif
+ }
+ std::cout << " and it has the following information:" << std::endl;
+ if(!h->path().empty())
+ {
+ std::cout << " Normalised path: " << normalise_path(h->path()) << std::endl;
+ std::cout << " Normalised path with volume GUID: " << normalise_path(h->path(), path_normalise::guid_volume) << std::endl;
+ std::cout << " Normalised path as GUID: ";
+ try
+ {
+ std::cout << normalise_path(h->path(), path_normalise::guid_all) << std::endl;
+ }
+ catch (...)
+ {
+ std::cout << "EXCEPTION THROWN!" << std::endl;
+ }
+ }
+ if(
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ filesystem::file_type::symlink_file
+#else
+ filesystem::file_type::symlink
+#endif
+ ==entry.st_type)
+ {
+ std::cout << " Target=" << h->target() << std::endl;
+ }
+#define PRINT_FIELD(field, ...) \
+ std::cout << " st_" #field ": "; if(!!(directory_entry::metadata_supported()&metadata_flags::field)) std::cout << __VA_ARGS__ entry.st_##field; else std::cout << "unknown"; std::cout << std::endl
+#ifndef WIN32
+ PRINT_FIELD(dev);
+#endif
+ PRINT_FIELD(ino);
+ PRINT_FIELD(type, (int));
+#ifndef WIN32
+ PRINT_FIELD(perms);
+#endif
+ PRINT_FIELD(nlink);
+#ifndef WIN32
+ PRINT_FIELD(uid);
+ PRINT_FIELD(gid);
+ PRINT_FIELD(rdev);
+#endif
+ PRINT_FIELD(atim);
+ PRINT_FIELD(mtim);
+ PRINT_FIELD(ctim);
+ PRINT_FIELD(size);
+ PRINT_FIELD(allocated);
+ PRINT_FIELD(blocks);
+ PRINT_FIELD(blksize);
+ PRINT_FIELD(flags);
+ PRINT_FIELD(gen);
+ PRINT_FIELD(birthtim);
+ PRINT_FIELD(sparse);
+ PRINT_FIELD(compressed);
+ PRINT_FIELD(reparse_point);
+#undef PRINT_FIELD
+ return entry;
+}
+
+static void print_stat(handle_ptr dirh, directory_entry direntry)
+{
+ using namespace boost::afio;
+ std::cout << "Entry " << direntry.name() << " is a ";
+ auto entry=direntry.fetch_lstat(dirh);
+ switch(entry.st_type)
+ {
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ case filesystem::file_type::symlink_file:
+ std::cout << "link";
+ break;
+ case filesystem::file_type::directory_file:
+ std::cout << "directory";
+ break;
+ case filesystem::file_type::regular_file:
+ std::cout << "file";
+ break;
+ default:
+ std::cout << "unknown";
+ break;
+#else
+ case filesystem::file_type::symlink:
+ std::cout << "link";
+ break;
+ case filesystem::file_type::directory:
+ std::cout << "directory";
+ break;
+ case filesystem::file_type::regular:
+ std::cout << "file";
+ break;
+ default:
+ std::cout << "unknown";
+ break;
+#endif
+ }
+ std::cout << " and it has the following information:" << std::endl;
+
+#define PRINT_FIELD(field, ...) \
+ std::cout << " st_" #field ": "; if(!!(direntry.metadata_ready()&metadata_flags::field)) std::cout << __VA_ARGS__ entry.st_##field; else std::cout << "unknown"; std::cout << std::endl
+#ifndef WIN32
+ PRINT_FIELD(dev);
+#endif
+ PRINT_FIELD(ino);
+ PRINT_FIELD(type, (int));
+#ifndef WIN32
+ PRINT_FIELD(perms);
+#endif
+ PRINT_FIELD(nlink);
+#ifndef WIN32
+ PRINT_FIELD(uid);
+ PRINT_FIELD(gid);
+ PRINT_FIELD(rdev);
+#endif
+ PRINT_FIELD(atim);
+ PRINT_FIELD(mtim);
+ PRINT_FIELD(ctim);
+ PRINT_FIELD(size);
+ PRINT_FIELD(allocated);
+ PRINT_FIELD(blocks);
+ PRINT_FIELD(blksize);
+ PRINT_FIELD(flags);
+ PRINT_FIELD(gen);
+ PRINT_FIELD(birthtim);
+ PRINT_FIELD(sparse);
+ PRINT_FIELD(compressed);
+ PRINT_FIELD(reparse_point);
+#undef PRINT_FIELD
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#endif
diff --git a/attic/test/test_inline_linkage1.cpp b/attic/test/test_inline_linkage1.cpp
new file mode 100644
index 00000000..e5cb8fad
--- /dev/null
+++ b/attic/test/test_inline_linkage1.cpp
@@ -0,0 +1,33 @@
+#include "boost/afio/afio.hpp"
+
+void test_inline_linkage1()
+{
+ using namespace boost::afio;
+ using namespace std;
+ vector<char> buffer(64, 'n');
+ auto dispatcher = boost::afio::make_dispatcher("file:///", boost::afio::file_flags::always_sync).get();
+ std::cout << "\n\nTesting synchronous directory and file creation:\n";
+ {
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ auto mkfile(dispatcher->file(path_req::relative(mkdir, "foo", file_flags::create | file_flags::read_write)));
+ auto writefile1(dispatcher->write(io_req < vector < char >>(mkfile, buffer, 0)));
+ auto sync1(dispatcher->sync(writefile1));
+ auto writefile2(dispatcher->write(io_req < vector < char >>(sync1, buffer, 0)));
+ auto closefile1(dispatcher->close(writefile2));
+ auto openfile(dispatcher->file(path_req::relative(closefile1, file_flags::read)));
+ char b[64];
+ auto readfile(dispatcher->read(make_io_req(openfile, b, 0)));
+ auto delfile(dispatcher->close(dispatcher->rmfile(readfile)));
+ auto deldir(dispatcher->close(dispatcher->rmdir(delfile)));
+ when_all_p(mkdir).wait();
+ when_all_p(mkfile).wait();
+ when_all_p(writefile1).wait();
+ when_all_p(sync1).wait();
+ when_all_p(writefile2).wait();
+ when_all_p(closefile1).wait();
+ when_all_p(openfile).wait();
+ when_all_p(readfile).wait();
+ when_all_p(delfile).wait();
+ when_all_p(deldir).wait();
+ }
+} \ No newline at end of file
diff --git a/attic/test/test_inline_linkage2.cpp b/attic/test/test_inline_linkage2.cpp
new file mode 100644
index 00000000..c627fb1f
--- /dev/null
+++ b/attic/test/test_inline_linkage2.cpp
@@ -0,0 +1,33 @@
+#include "boost/afio/afio.hpp"
+
+void test_inline_linkage2()
+{
+ using namespace boost::afio;
+ using namespace std;
+ vector<char> buffer(64, 'n');
+ auto dispatcher = boost::afio::make_dispatcher("file:///", boost::afio::file_flags::always_sync).get();
+ std::cout << "\n\nTesting synchronous directory and file creation:\n";
+ {
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ auto mkfile(dispatcher->file(path_req::relative(mkdir, "foo", file_flags::create | file_flags::read_write)));
+ auto writefile1(dispatcher->write(io_req < vector < char >> (mkfile, buffer, 0)));
+ auto sync1(dispatcher->sync(writefile1));
+ auto writefile2(dispatcher->write(io_req < vector < char >> (sync1, buffer, 0)));
+ auto closefile1(dispatcher->close(writefile2));
+ auto openfile(dispatcher->file(path_req::relative(closefile1, file_flags::read)));
+ char b[64];
+ auto readfile(dispatcher->read(make_io_req(openfile, b, 0)));
+ auto delfile(dispatcher->close(dispatcher->rmfile(readfile)));
+ auto deldir(dispatcher->close(dispatcher->rmdir(delfile)));
+ when_all_p(mkdir).wait();
+ when_all_p(mkfile).wait();
+ when_all_p(writefile1).wait();
+ when_all_p(sync1).wait();
+ when_all_p(writefile2).wait();
+ when_all_p(closefile1).wait();
+ when_all_p(openfile).wait();
+ when_all_p(readfile).wait();
+ when_all_p(delfile).wait();
+ when_all_p(deldir).wait();
+ }
+} \ No newline at end of file
diff --git a/attic/test/test_inline_linkage_master.cpp b/attic/test/test_inline_linkage_master.cpp
new file mode 100644
index 00000000..8f3205d6
--- /dev/null
+++ b/attic/test/test_inline_linkage_master.cpp
@@ -0,0 +1,9 @@
+extern void test_inline_linkage1();
+extern void test_inline_linkage2();
+
+int main(void)
+{
+ test_inline_linkage1();
+ test_inline_linkage2();
+ return 0;
+} \ No newline at end of file
diff --git a/attic/test/tests/api_error_check.cpp b/attic/test/tests/api_error_check.cpp
new file mode 100644
index 00000000..3800cb41
--- /dev/null
+++ b/attic/test/tests/api_error_check.cpp
@@ -0,0 +1,39 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(api_error_check, "Tests that every API returns errors as it is supposed to", 20)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ auto dispatcher = make_dispatcher().get();
+#define BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(call, errcode) try { BOOST_TEST_MESSAGE("Testing " #call); call; BOOST_FAIL("Exception not thrown by " #call); } \
+ catch(const system_error &e) { BOOST_CHECK(e.code().value()==errcode); std::cout << "\nsystem_error message from " #call " was: " << e.what() << std::endl; } \
+ catch(...) { BOOST_FAIL("Exception thrown by " #call " was not a system_error"); }
+#ifdef WIN32
+# define BOOST_AFIO_FILE_NOT_FOUND_ERRCODE ERROR_FILE_NOT_FOUND
+# define BOOST_AFIO_BAD_FD ERROR_INVALID_HANDLE
+#else
+# define BOOST_AFIO_FILE_NOT_FOUND_ERRCODE ENOENT
+# define BOOST_AFIO_BAD_FD EBADF
+#endif
+
+ // Create a bad handle
+ future<> op=dispatcher->file(path_req("testfile", file_flags::create|file_flags::read_write));
+ auto h=op.get_handle();
+ dispatcher->close(dispatcher->rmfile(op)).get();
+ char buffer[32];
+
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->dir("should not exist this").get(), BOOST_AFIO_FILE_NOT_FOUND_ERRCODE);
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->file("should not exist this").get(), BOOST_AFIO_FILE_NOT_FOUND_ERRCODE);
+ // No point checking sync(), he won't call if no bytes have been written
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->zero(op, {{0, 0}}).get(), BOOST_AFIO_BAD_FD);
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->read(make_io_req(op, buffer, 32)).get(), BOOST_AFIO_BAD_FD);
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->write(make_io_req(op, buffer, 32)).get(), BOOST_AFIO_BAD_FD);
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->truncate(op, 32).get(), BOOST_AFIO_BAD_FD);
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->enumerate(op).get_handle(), BOOST_AFIO_BAD_FD);
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->enumerate(op).get(), BOOST_AFIO_BAD_FD);
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->extents(op).get_handle(), BOOST_AFIO_BAD_FD);
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->extents(op).get(), BOOST_AFIO_BAD_FD);
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->statfs(op, fs_metadata_flags::All).get_handle(), BOOST_AFIO_BAD_FD);
+ BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE(dispatcher->statfs(op, fs_metadata_flags::All).get(), BOOST_AFIO_BAD_FD);
+
+#undef BOOST_AFIO_CHECK_SYSTEM_ERROR_CODE
+}
diff --git a/attic/test/tests/async_data_op_req_compilation.cpp b/attic/test/tests/async_data_op_req_compilation.cpp
new file mode 100644
index 00000000..d34103e8
--- /dev/null
+++ b/attic/test/tests/async_data_op_req_compilation.cpp
@@ -0,0 +1,449 @@
+#include "test_functions.hpp"
+
+//#define BOOST_AFIO_TEST_ASYNC_DATA_OP_REQ_FAILURE_TO_COMPILE
+
+BOOST_AFIO_AUTO_TEST_CASE(io_req_compilation, "Tests that all the use cases for io_req compile", 10)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ // Note that this test is mainly for testing metaprogramming compilation, it doesn't really do much
+ {
+ auto dispatcher=make_dispatcher().get();
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ mkdir.get();
+ auto mkfile(dispatcher->file(path_req::relative(mkdir, "foo", file_flags::create|file_flags::read_write)));
+ mkfile.get();
+ auto last(dispatcher->truncate(mkfile, 1024));
+ last.get();
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ size_t length=sizeof(buffer);
+
+ static_assert(detail::is_container<std::array<char, 2>>::value, "detail::is_container<std::array> isn't detecting a container");
+ static_assert(!std::is_const<detail::is_container<std::array<char, 2>>::type>::value, "detail::is_container<std::array> thinks the container iterator const");
+ static_assert(std::is_const<detail::is_container<const std::array<char, 2>>::type>::value, "detail::is_container<const std::array> thinks the container iterator not const");
+ static_assert(detail::is_container<std::string>::value, "detail::is_container<std::string> isn't detecting a container");
+ static_assert(!std::is_const<detail::is_container<std::string>::type>::value, "detail::is_container<std::string> thinks the container iterator const");
+ static_assert(std::is_const<detail::is_container<const std::string>::type>::value, "detail::is_container<const std::string> thinks the container iterator not const");
+
+ // ***************************** Static ASIO buffers composure checking *******************************
+ // void *
+ {
+ typedef void type;
+ type *buffer=(type *)(size_t)0xdeadbeef;
+ size_t length=78;
+ auto req(make_io_req(last, buffer, length, 0));
+ BOOST_CHECK(typeid(req)==typeid(io_req<type>));
+ BOOST_CHECK(typeid(req.buffers.front())==typeid(asio::mutable_buffer));
+ BOOST_CHECK(req.buffers.size()==1);
+ if(!req.buffers.empty())
+ {
+ BOOST_CHECK(asio::buffer_cast<type *>(req.buffers.front())==buffer);
+ BOOST_CHECK(asio::buffer_size(req.buffers.front())==length*1);
+ }
+ }
+ // const double *
+ {
+ typedef const double type;
+ type *buffer=(type *)(size_t)0xdeadbeef;
+ size_t length=78;
+ auto req(make_io_req(last, buffer, length, 0));
+ BOOST_CHECK(typeid(req)==typeid(io_req<type>));
+ BOOST_CHECK(typeid(req.buffers.front())==typeid(asio::const_buffer));
+ BOOST_CHECK(req.buffers.size()==1);
+ if(!req.buffers.empty())
+ {
+ BOOST_CHECK(asio::buffer_cast<type *>(req.buffers.front())==buffer);
+ BOOST_CHECK(asio::buffer_size(req.buffers.front())==length*sizeof(type));
+ }
+ }
+ // double[]
+ {
+ typedef double type[78];
+ double buffer[78];
+ auto req(make_io_req(last, buffer, 0));
+ BOOST_CHECK(typeid(req)==typeid(io_req<double>));
+ BOOST_CHECK(typeid(req.buffers.front())==typeid(asio::mutable_buffer));
+ BOOST_CHECK(req.buffers.size()==1);
+ if(!req.buffers.empty())
+ {
+ BOOST_CHECK(asio::buffer_cast<double *>(req.buffers.front())==buffer);
+ BOOST_CHECK(asio::buffer_size(req.buffers.front())==sizeof(buffer));
+ }
+ }
+ // asio::mutable_buffer
+ {
+ typedef asio::mutable_buffer type;
+ type b(buffer, length);
+ auto req(make_io_req(last, b, 0));
+ BOOST_CHECK(typeid(req)==typeid(io_req<type>));
+ BOOST_CHECK(typeid(req.buffers.front())==typeid(asio::mutable_buffer));
+ BOOST_CHECK(req.buffers.size()==1);
+ if(!req.buffers.empty())
+ {
+ BOOST_CHECK(asio::buffer_cast<void *>(req.buffers.front())==buffer);
+ BOOST_CHECK(asio::buffer_size(req.buffers.front())==length);
+ }
+ }
+ // vector<asio::mutable_buffer>
+ {
+ typedef std::vector<asio::mutable_buffer> type;
+ type b(4, type::value_type(buffer, length));
+ auto req(make_io_req(last, b, 0));
+ BOOST_CHECK(typeid(req)==typeid(io_req<type>));
+ BOOST_CHECK(typeid(req.buffers.front())==typeid(asio::mutable_buffer));
+ BOOST_CHECK(req.buffers.size()==b.size());
+ for(auto &i : req.buffers)
+ {
+ BOOST_CHECK(asio::buffer_cast<void *>(i)==buffer);
+ BOOST_CHECK(asio::buffer_size(i)==length);
+ }
+ }
+ // asio::mutable_buffer[]
+ {
+ typedef asio::mutable_buffer type[2];
+ type b={asio::mutable_buffer(buffer, length), asio::mutable_buffer(buffer, length)};
+ auto req(make_io_req(last, b, 0));
+ // Sequences of asio buffers are passed through
+ BOOST_CHECK(typeid(req)==typeid(io_req<asio::mutable_buffer>));
+ BOOST_CHECK(typeid(req.buffers.front())==typeid(asio::mutable_buffer));
+ BOOST_CHECK(req.buffers.size()==2);
+ for(auto &i : req.buffers)
+ {
+ BOOST_CHECK(asio::buffer_cast<void *>(i)==buffer);
+ BOOST_CHECK(asio::buffer_size(i)==length);
+ }
+ }
+ // std::list<std::list<std::string>>
+ {
+ typedef std::list<std::list<std::string>> type;
+ type b={
+ { "Niall", "Douglas"},
+ { "was", "here" }
+ };
+ auto req(make_io_req(last, b, 0));
+ BOOST_CHECK(typeid(req)==typeid(io_req<type>));
+ BOOST_CHECK(typeid(req.buffers.front())==typeid(asio::mutable_buffer));
+ BOOST_CHECK(req.buffers.size()==4);
+ BOOST_CHECK(asio::buffer_cast<void *>(req.buffers[0])==b.front().front().c_str());
+ BOOST_CHECK(asio::buffer_size(req.buffers[0])==b.front().front().size());
+ BOOST_CHECK(asio::buffer_cast<void *>(req.buffers[1])==b.front().back().c_str());
+ BOOST_CHECK(asio::buffer_size(req.buffers[1])==b.front().back().size());
+ BOOST_CHECK(asio::buffer_cast<void *>(req.buffers[2])==b.back().front().c_str());
+ BOOST_CHECK(asio::buffer_size(req.buffers[2])==b.back().front().size());
+ BOOST_CHECK(asio::buffer_cast<void *>(req.buffers[3])==b.back().back().c_str());
+ BOOST_CHECK(asio::buffer_size(req.buffers[3])==b.back().back().size());
+ }
+ // std::vector<std::vector<unsigned long>>
+ {
+ typedef std::vector<std::vector<unsigned long>> type;
+ type b;
+ b.push_back(std::vector<unsigned long>(5));
+ b.push_back(std::vector<unsigned long>(6));
+ b.push_back(std::vector<unsigned long>(7));
+ b.push_back(std::vector<unsigned long>(8));
+ auto req(make_io_req(last, b, 0));
+ BOOST_CHECK(typeid(req)==typeid(io_req<type>));
+ BOOST_CHECK(typeid(req.buffers.front())==typeid(asio::mutable_buffer));
+ BOOST_CHECK(req.buffers.size()==4);
+ for(size_t n=0; n<b.size(); n++)
+ {
+ BOOST_CHECK(asio::buffer_cast<void *>(req.buffers[n])==b[n].data());
+ BOOST_CHECK(asio::buffer_size(req.buffers[n])==b[n].size()*sizeof(unsigned long));
+ }
+ }
+ // std::unordered_set<std::string>
+ {
+ typedef std::unordered_set<std::string> type;
+ type b;
+ b.insert("Niall");
+ auto req(make_io_req(last, b, 0));
+ // unordered_set only provides const access to its iterators, so this needs to be a const buffer
+ BOOST_CHECK(typeid(req)==typeid(io_req<const type>));
+ BOOST_CHECK(typeid(req.buffers.front())==typeid(asio::const_buffer));
+ BOOST_CHECK(req.buffers.size()==1);
+ BOOST_CHECK(asio::buffer_cast<const void *>(req.buffers.front())==b.begin()->data());
+ BOOST_CHECK(asio::buffer_size(req.buffers.front())==b.begin()->size());
+ }
+#if 0
+ // std::initializer_list<const char *>
+ {
+ auto b={"Ni", "al", "l "};
+ typedef decltype(b) type;
+ auto req(make_io_req(last, b, 0));
+ BOOST_CHECK(typeid(req)==typeid(io_req<type>));
+ BOOST_CHECK(typeid(req.buffers.front())==typeid(asio::const_buffer));
+ BOOST_CHECK(req.buffers.size()==3);
+ auto it(b.begin());
+ for(size_t n=0; n<b.size(); n++, ++it)
+ {
+ BOOST_CHECK(asio::buffer_cast<const void *>(req.buffers[n])==*it);
+ BOOST_CHECK(asio::buffer_size(req.buffers[n])==2);
+ }
+ }
+#endif
+
+ // ***************************** Buffers into read/write *******************************
+ // Base void * specialisation
+ {
+ typedef void type;
+ typedef const type const_type;
+
+ type *out=buffer;
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, length, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, length, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, length, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, length, 0));
+ last.get();
+ }
+ // char * specialisation
+ {
+ typedef char type;
+ typedef const type const_type;
+
+ type *out=buffer;
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, length, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, length, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, length, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, length, 0));
+ last.get();
+ }
+ // char array specialisation
+ {
+ typedef char type;
+ typedef const type const_type;
+
+ auto &out=buffer;
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ // Arbitrary integral type array specialisation
+ {
+ typedef wchar_t type;
+ typedef const type const_type;
+
+ wchar_t out[sizeof(buffer)/sizeof(wchar_t)];
+ memset(out, 0, sizeof(out));
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ // string specialisation
+ {
+ typedef std::string type;
+ typedef const type const_type;
+
+ type out(sizeof(buffer)/sizeof(type::value_type), ' ');
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ // asio::mutable_buffer specialisation
+ {
+ typedef asio::mutable_buffer type;
+ typedef asio::const_buffer const_type;
+
+ unsigned word=0xdeadbeef;
+ type out(&word, 1);
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ // vector specialisation
+ {
+ typedef std::vector<char> type;
+ typedef const type const_type;
+
+ type out(sizeof(buffer)/sizeof(type::value_type), ' ');
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ // vector of asio::mutable_buffer specialisation
+ {
+ typedef std::vector<asio::mutable_buffer> type;
+ typedef std::vector<asio::const_buffer> const_type;
+
+ unsigned word=0xdeadbeef;
+ type out(1, asio::mutable_buffer(&word, 1));
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ // vector of string specialisation
+ {
+ typedef std::vector<std::string> type;
+ typedef const type const_type;
+
+ type out(sizeof(buffer)/sizeof(type::value_type), " ");
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ // array specialisation
+ {
+ typedef std::array<char, sizeof(buffer)> type;
+ typedef const type const_type;
+
+ type out;
+ out.fill(' ');
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ // array of asio::mutable_buffer specialisation
+ {
+ typedef std::array<asio::mutable_buffer, 1> type;
+ typedef std::array<asio::const_buffer, 1> const_type;
+
+ unsigned word=0xdeadbeef;
+ type out;
+ out[0]=asio::mutable_buffer(&word, 1);
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ // array of string specialisation
+ {
+ typedef std::array<std::string, sizeof(buffer)/sizeof(std::string::value_type)> type;
+ typedef const type const_type;
+
+ type out;
+ out.fill(" ");
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ // Test read-only container
+ {
+ typedef std::unordered_set<char> type;
+ typedef const type const_type;
+
+ type out; out.insert(32);
+ // works
+ last=dispatcher->write(io_req<const_type>(last, out, 0));
+ last.get();
+#ifdef BOOST_AFIO_TEST_ASYNC_DATA_OP_REQ_FAILURE_TO_COMPILE
+ // auto-consts
+ last=dispatcher->write(io_req<type>(last, out, 0));
+ last.get();
+ // works
+ last=dispatcher->read(io_req<type>(last, out, 0));
+ last.get();
+#endif
+ // deduces
+ last=dispatcher->write(make_io_req(last, out, 0));
+ last.get();
+ }
+ last=dispatcher->rmfile(last);
+ last.get();
+ last=dispatcher->close(last);
+ last.get();
+ }
+ try { filesystem::remove_all("testdir"); } catch(...) {}
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_adopt_test.cpp b/attic/test/tests/async_io_adopt_test.cpp
new file mode 100644
index 00000000..a9bcdaf2
--- /dev/null
+++ b/attic/test/tests/async_io_adopt_test.cpp
@@ -0,0 +1,57 @@
+#include "test_functions.hpp"
+
+using namespace BOOST_AFIO_V2_NAMESPACE;
+namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+namespace afio = BOOST_AFIO_V2_NAMESPACE;
+
+struct test_handle : handle
+{
+ test_handle(dispatcher *parent) : handle(parent, file_flags::none) {}
+ virtual void close() override final
+ {
+ // Do nothing
+ }
+ virtual handle::open_states is_open() const override final
+ {
+ return handle::open_states::open;
+ }
+ virtual void *native_handle() const override final
+ {
+ return nullptr;
+ }
+ using handle::path;
+ virtual afio::path path(bool refresh=false) override final
+ {
+ return "foo";
+ }
+ virtual afio::path path() const override final
+ {
+ return "foo";
+ }
+ virtual directory_entry direntry(metadata_flags wanted=directory_entry::metadata_fastpath()) override final
+ {
+ return directory_entry();
+ }
+ virtual afio::path target() override final
+ {
+ return afio::path();
+ }
+ virtual void link(const path_req &req) override final
+ {
+ }
+ virtual void unlink() override final
+ {
+ }
+ virtual void atomic_relink(const path_req &req) override final
+ {
+ }
+};
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_adopt, "Tests foreign fd adoption", 5)
+{
+ auto dispatcher = make_dispatcher().get();
+ std::cout << "\n\nTesting foreign fd adoption:\n";
+ auto h=std::make_shared<test_handle>(dispatcher.get());
+ auto adopted=dispatcher->adopt(h);
+ BOOST_CHECK_NO_THROW(when_all_p(adopted).get());
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_barrier_test.cpp b/attic/test/tests/async_io_barrier_test.cpp
new file mode 100644
index 00000000..38b3af70
--- /dev/null
+++ b/attic/test/tests/async_io_barrier_test.cpp
@@ -0,0 +1,98 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_barrier, "Tests that the async i/o barrier works correctly under load", 180)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+ std::vector<std::pair<size_t, int>> groups;
+ // Generate 500,000 sorted random numbers between 0-10000
+ static const size_t numbers=
+#if defined(BOOST_AFIO_RUNNING_IN_CI) || defined(BOOST_AFIO_COMPILING_FOR_GCOV)
+ 1600
+#else
+ 160000
+#endif
+ ;
+ {
+ ranctx gen;
+ raninit(&gen, 0x78adbcff);
+ std::vector<int> manynumbers;
+ manynumbers.reserve(numbers);
+ for (size_t n = 0; n < numbers; n++)
+ manynumbers.push_back(ranval(&gen) % 10000);
+ std::sort(manynumbers.begin(), manynumbers.end());
+
+ // Collapse into a collection of runs of the same number
+ int lastnumber = -1;
+ for(auto &i: manynumbers)
+ {
+ if (i != lastnumber)
+ groups.push_back(std::make_pair(0, i));
+ groups.back().first++;
+ lastnumber = i;
+ }
+ }
+ atomic<size_t> callcount[10000];
+ memset(&callcount, 0, sizeof(callcount));
+ std::vector<future<bool>> verifies;
+ verifies.reserve(groups.size());
+ auto inccount = [](atomic<size_t> *count){ /*for (volatile size_t n = 0; n < 10000; n++);*/ (*count)++; };
+ auto verifybarrier = [](atomic<size_t> *count, size_t shouldbe)
+ {
+ if (*count != shouldbe)
+ {
+ BOOST_CHECK((*count == shouldbe));
+ throw std::runtime_error("Count was not what it should have been!");
+ }
+ return true;
+ };
+ // For each of those runs, dispatch ops and a barrier for them
+ auto dispatcher = make_dispatcher().get();
+ auto begin = chrono::high_resolution_clock::now();
+ size_t opscount = 0;
+ future<> next;
+ bool isfirst = true;
+ for(auto &run: groups)
+ {
+ assert(run.first>0);
+ std::vector<std::function<void()>> thisgroupcalls(run.first, std::bind(inccount, &callcount[run.second]));
+ std::vector<future<>> thisgroupcallops;
+ if (isfirst)
+ {
+ thisgroupcallops = dispatcher->call(thisgroupcalls);
+ isfirst = false;
+ }
+ else
+ {
+ std::vector<future<>> dependency(run.first, next);
+ thisgroupcallops = dispatcher->call(dependency, thisgroupcalls);
+ }
+ auto thisgroupbarriered = dispatcher->barrier(thisgroupcallops);
+ auto verify = dispatcher->call(thisgroupbarriered.front(), std::function<bool()>(std::bind(verifybarrier, &callcount[run.second], run.first)));
+ next = verify;
+ verifies.push_back(std::move(verify));
+ // barrier() adds an immediate op per op
+ opscount += run.first*2 + 1;
+ }
+ auto dispatched = chrono::high_resolution_clock::now();
+ std::cout << "There are now " << std::dec << dispatcher->fd_count() << " handles open with a queue depth of " << dispatcher->wait_queue_depth() << std::endl;
+ BOOST_AFIO_CHECK_NO_THROW(when_all_p(next).get());
+ std::cout << "There are now " << std::dec << dispatcher->fd_count() << " handles open with a queue depth of " << dispatcher->wait_queue_depth() << std::endl;
+ // Retrieve any errors
+ for(auto &i: verifies)
+ {
+ BOOST_AFIO_CHECK_NO_THROW(i.get());
+ }
+ auto end = chrono::high_resolution_clock::now();
+ auto diff = chrono::duration_cast<secs_type>(end - begin);
+ std::cout << "It took " << diff.count() << " secs to do " << opscount << " operations" << std::endl;
+ diff = chrono::duration_cast<secs_type>(dispatched - begin);
+ std::cout << " It took " << diff.count() << " secs to dispatch all operations" << std::endl;
+ diff = chrono::duration_cast<secs_type>(end - dispatched);
+ std::cout << " It took " << diff.count() << " secs to finish all operations" << std::endl << std::endl;
+ diff = chrono::duration_cast<secs_type>(end - begin);
+ std::cout << "That's a throughput of " << opscount / diff.count() << " ops/sec" << std::endl;
+ // Add a single output to validate the test
+ BOOST_CHECK(true);
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_enumerate_works.cpp b/attic/test/tests/async_io_enumerate_works.cpp
new file mode 100644
index 00000000..069136af
--- /dev/null
+++ b/attic/test/tests/async_io_enumerate_works.cpp
@@ -0,0 +1,62 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_enumerate, "Tests that async i/o enumerate() works", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ auto dispatcher=make_dispatcher().get();
+ std::cout << "Opening root directory for enumeration" << std::endl;
+ auto rootdir(dispatcher->dir(path_req("/", file_flags::read)));
+ auto rootdir2(dispatcher->dir(path_req("/", file_flags::read)));
+ when_all_p(rootdir).wait();
+ when_all_p(rootdir2).wait();
+ // Make sure directory cache is working
+ BOOST_CHECK(rootdir.get_handle()->native_handle()==rootdir2.get_handle()->native_handle());
+
+ std::cout << "The root directory contains the following items:" << std::endl << std::endl;
+ std::vector<directory_entry> rootdircontents1, rootdircontents2;
+ std::unordered_set<directory_entry> rootdircontents1a, rootdircontents2a;
+ // Read everything in one go
+ std::pair<std::vector<directory_entry>, bool> list;
+ bool first=true;
+ do
+ {
+ auto enumeration(dispatcher->enumerate(enumerate_req(rootdir, directory_entry::compatibility_maximum(), first, path(), directory_entry::metadata_fastpath())));
+ first=false;
+ list=enumeration.get();
+ if(!list.first.empty()) BOOST_CHECK((list.first.front().metadata_ready()&directory_entry::metadata_fastpath())== directory_entry::metadata_fastpath());
+ for(auto &i: list.first)
+ {
+ print_stat(rootdir.get_handle(), i);
+ }
+ rootdircontents1a.insert(list.first.begin(), list.first.end());
+ rootdircontents1.insert(rootdircontents1.end(), std::make_move_iterator(list.first.begin()), std::make_move_iterator(list.first.end()));
+ } while(list.second);
+ // Now read everything one at a time
+ first=true;
+ do
+ {
+ auto enumeration(dispatcher->enumerate(enumerate_req(rootdir, 1, first, path(), directory_entry::metadata_fastpath())));
+ first=false;
+ std::cout << ".";
+ list=enumeration.get();
+ if(!list.first.empty()) BOOST_CHECK((list.first.front().metadata_ready()&directory_entry::metadata_fastpath())== directory_entry::metadata_fastpath());
+ rootdircontents2a.insert(list.first.begin(), list.first.end());
+ rootdircontents2.insert(rootdircontents2.end(), std::make_move_iterator(list.first.begin()), std::make_move_iterator(list.first.end()));
+ } while(list.second);
+ std::cout << "rootdircontents1 has " << rootdircontents1.size() << " items and rootdircontents2 has " << rootdircontents2.size() << " items." << std::endl;
+ BOOST_CHECK(rootdircontents1.size()==rootdircontents2.size());
+ BOOST_CHECK(rootdircontents1.size()==rootdircontents1a.size());
+ BOOST_CHECK(rootdircontents2.size()==rootdircontents2a.size());
+
+ // Make sure unwildcarded single glob works (it has a fastpath on POSIX)
+ auto enumeration1(dispatcher->enumerate(enumerate_req(rootdir)));
+ auto &&direntries1=enumeration1.get().first;
+ BOOST_REQUIRE(direntries1.size()>0);
+ auto direntry1(direntries1.front());
+ auto enumeration2(dispatcher->enumerate(enumerate_req(rootdir, path(direntry1.name()))));
+ auto &&direntries2=enumeration2.get().first;
+ BOOST_REQUIRE(direntries2.size()>0);
+ auto direntry2(direntries2.front());
+ BOOST_CHECK(direntry1==direntry2);
+}
diff --git a/attic/test/tests/async_io_errors_test.cpp b/attic/test/tests/async_io_errors_test.cpp
new file mode 100644
index 00000000..7bfa3a91
--- /dev/null
+++ b/attic/test/tests/async_io_errors_test.cpp
@@ -0,0 +1,167 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_errors, "Tests that the async i/o error handling works", 300)
+{
+#ifndef BOOST_AFIO_THREAD_SANITIZING
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+
+ // Oh Windows, oh Windows, how strange you are ...
+ for (size_t n = 0; n < 10; n++)
+ {
+ try
+ {
+ if (filesystem::exists("testdir/a"))
+ filesystem::remove("testdir/a");
+ if (filesystem::exists("testdir"))
+ filesystem::remove("testdir");
+ break;
+ }
+ catch (...)
+ {
+ this_thread::sleep_for(chrono::milliseconds(10));
+ }
+ }
+ {
+ int hasErrorDirectly, hasErrorFromBarrier;
+ auto dispatcher = make_dispatcher().get();
+ dispatcher->testing_flags(detail::unit_testing_flags::no_symbol_lookup);
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ std::vector<path_req> filereqs;
+
+ /* There was once a rare race condition in barrier() which took many, many days
+ * to discover and solve, some of which involved much painful refactoring (which
+ * was for the good anyway, but still painful). So, let's really hammer this API
+ * such that it never, ever slightly fails to function ever again!
+ */
+#if defined(BOOST_AFIO_RUNNING_IN_CI) || defined(BOOST_AFIO_COMPILING_FOR_GCOV)
+#if defined(WIN32)
+ for(size_t n=0; n<200; n++)
+#else
+ for(size_t n=0; n<500; n++)
+#endif
+#else
+ for(size_t n=0; n<50000; n++)
+#endif
+ {
+ // The following is a fundamentally unstable unit test - if manyfilecreates completes before
+ // sync1, sync1 will throw on the spot
+ // Similarly, if either manyfilecreates or sync1 completes before the first when_all_p(),
+ // the nothrow_t when_all_p() will throw on the spot :)
+ do
+ {
+ filereqs.clear();
+ filereqs.push_back(path_req::relative(mkdir, "a", file_flags::create_only_if_not_exist));
+ filereqs.push_back(path_req::relative(mkdir, "a", file_flags::create_only_if_not_exist));
+ // Windows won't let you delete a file still open
+ while(dispatcher->fd_count()>1)
+ this_thread::sleep_for(chrono::milliseconds(1));
+ // Unfortunately Windows does not behave here, and deleting the file if a handle is still
+ // open to it appears to no op
+ for(size_t n=0; n<100; n++)
+ {
+ try
+ {
+ if (!filesystem::exists("testdir/a"))
+ break;
+ filesystem::remove("testdir/a");
+ } catch(...) { }
+ if(n>10) this_thread::sleep_for(chrono::milliseconds(1));
+ }
+ if(filesystem::exists("testdir/a"))
+ {
+ std::cerr << "FATAL: Something weird is happening, I can't delete my test file!" << std::endl;
+ abort();
+ }
+ try
+ {
+ auto manyfilecreates = dispatcher->file(filereqs); // One or both of these will error
+ auto sync1 = dispatcher->barrier(manyfilecreates); // If barrier() doesn't throw due to errored input, barrier() will replicate errors for you
+ auto future1 = when_all_p(std::nothrow_t(), sync1.begin(), sync1.end());
+ auto future1e = when_all_p(sync1.begin(), sync1.end());
+ auto future2 = when_all_p(std::nothrow_t(), sync1.begin(), sync1.end());
+ auto future2e = when_all_p(sync1.begin(), sync1.end());
+ // If any of the above threw due to context switches, they'll repeat
+
+ BOOST_AFIO_CHECK_NO_THROW(future1.get()); // nothrow variant must never throw
+ BOOST_AFIO_CHECK_THROWS(future1e.get()); // throw variant must always throw
+ BOOST_AFIO_CHECK_NO_THROW(future2.get()); // nothrow variant must never throw
+ BOOST_AFIO_CHECK_THROWS(future2e.get()); // throw variant must always throw
+ hasErrorDirectly = 0;
+ for (auto &i : manyfilecreates)
+ {
+ // If we ask for has_exception() before the async thread has exited its packaged_task
+ // this will fail, so no choice but to try { wait(); } catch { success }
+ try
+ {
+ i.get();
+ }
+#ifdef WIN32
+ // Sometimes Windows gets a bit stuck on slower CPUs when deleting files and occasionally
+ // returns this error. It's rare, but it confounds the test results :(
+ catch(const std::exception &e)
+ {
+ if(!strncmp(e.what(), "A non close operation has been requested of a file object with a delete pending", 79))
+ throw; // reset and retry
+ hasErrorDirectly++;
+ }
+#endif
+ catch (...)
+ {
+ hasErrorDirectly++;
+ }
+ }
+ if(hasErrorDirectly != 1)
+ {
+ std::cout << "hasErrorDirectly = " << hasErrorDirectly << std::endl;
+ BOOST_CHECK(hasErrorDirectly == 1);
+ for (auto &i : manyfilecreates)
+ {
+ try { i.get(); } catch(const std::runtime_error &e) { std::cerr << "Error was " << e.what() << std::endl; } catch(...) { std::cerr << "Error was unknown type" << std::endl; }
+ }
+ }
+ hasErrorFromBarrier = 0;
+ for (auto &i : sync1)
+ {
+ try
+ {
+ i.get();
+ }
+ catch (...)
+ {
+ hasErrorFromBarrier++;
+ }
+ }
+ if(hasErrorFromBarrier != 1)
+ {
+ std::cout << "hasErrorFromBarrier = " << hasErrorFromBarrier << std::endl;
+ BOOST_CHECK(hasErrorFromBarrier == 1);
+ }
+ }
+ catch(...)
+ {
+ // Restore state and repeat until it works
+ continue;
+ }
+ } while(false);
+ }
+ }
+ for (size_t n = 0; n < 10; n++)
+ {
+ try
+ {
+ if (filesystem::exists("testdir/a"))
+ filesystem::remove("testdir/a");
+ if (filesystem::exists("testdir"))
+ filesystem::remove("testdir");
+ break;
+ }
+ catch (...)
+ {
+ this_thread::sleep_for(chrono::milliseconds(10));
+ }
+ }
+#endif
+ // Add a single output to validate the test
+ BOOST_CHECK(true);
+}
diff --git a/attic/test/tests/async_io_lstat_works.cpp b/attic/test/tests/async_io_lstat_works.cpp
new file mode 100644
index 00000000..03a275c3
--- /dev/null
+++ b/attic/test/tests/async_io_lstat_works.cpp
@@ -0,0 +1,81 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_lstat_works, "Tests that async i/o lstat() works", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ // Oh Windows, oh Windows, how strange you are ...
+ for (size_t n = 0; n < 10; n++)
+ {
+ try
+ {
+ if (filesystem::exists("testdir"))
+ filesystem::remove("testdir");
+ break;
+ }
+ catch (...)
+ {
+ this_thread::sleep_for(chrono::milliseconds(10));
+ }
+ }
+
+ auto dispatcher=make_dispatcher().get();
+ auto test(dispatcher->dir(path_req("testdir", file_flags::create|file_flags::write)));
+ {
+ auto mkdir(dispatcher->dir(path_req::relative(test, "dir", file_flags::create|file_flags::write)));
+ auto mkfile(dispatcher->file(path_req::relative(mkdir, "file", file_flags::create|file_flags::write)));
+ auto mklink(dispatcher->symlink(path_req::absolute(mkdir, "testdir/linktodir", file_flags::create|file_flags::write)));
+
+ auto mkdirstat=print_stat(when_all_p(mkdir).get().front());
+ auto mkfilestat=print_stat(when_all_p(mkfile).get().front());
+ auto mklinkstat=print_stat(when_all_p(mklink).get().front());
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ BOOST_CHECK(mkdirstat.st_type==filesystem::file_type::directory_file);
+ BOOST_CHECK(mkfilestat.st_type==filesystem::file_type::regular_file);
+ BOOST_CHECK(mklinkstat.st_type==filesystem::file_type::symlink_file);
+#else
+ BOOST_CHECK(mkdirstat.st_type==filesystem::file_type::directory);
+ BOOST_CHECK(mkfilestat.st_type==filesystem::file_type::regular);
+ BOOST_CHECK(mklinkstat.st_type==filesystem::file_type::symlink);
+#endif
+
+ // Some sanity stuff
+ BOOST_CHECK(mkdirstat.st_ino!=mkfilestat.st_ino);
+ BOOST_CHECK(mkfilestat.st_ino!=mklinkstat.st_ino);
+ BOOST_CHECK(mkdirstat.st_ino!=mklinkstat.st_ino);
+ BOOST_CHECK(mklink.get_handle()->target()==mkdir.get_handle()->path());
+// BOOST_CHECK(mkdir.get()->container()->native_handle()==test.get()->native_handle());
+
+ // Enumerate the directory and make sure it returns the correct metadata too
+ auto items = enumerate(test, 10).first;
+ BOOST_CHECK(items.size() == 2);
+ for (auto &item : items)
+ {
+ if (item.name() == "dir")
+ {
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ BOOST_CHECK(item.fetch_lstat(test.get_handle()).st_type == filesystem::file_type::directory_file);
+#else
+ BOOST_CHECK(item.fetch_lstat(test.get_handle()).st_type == filesystem::file_type::directory);
+#endif
+ BOOST_CHECK(item.fetch_lstat(test.get_handle()).st_ino == mkdirstat.st_ino);
+ }
+ else if (item.name() == "linktodir")
+ {
+#ifdef BOOST_AFIO_USE_LEGACY_FILESYSTEM_SEMANTICS
+ BOOST_CHECK(item.fetch_lstat(test.get_handle()).st_type == filesystem::file_type::symlink_file);
+#else
+ BOOST_CHECK(item.fetch_lstat(test.get_handle()).st_type == filesystem::file_type::symlink);
+#endif
+ BOOST_CHECK(item.fetch_lstat(test.get_handle()).st_ino == mklinkstat.st_ino);
+ }
+ }
+
+ auto rmlink(dispatcher->close(dispatcher->rmsymlink(mklink)));
+ auto rmfile(dispatcher->close(dispatcher->rmfile(dispatcher->depends(rmlink, mkfile))));
+ auto rmdir(dispatcher->close(dispatcher->rmdir(dispatcher->depends(rmfile, mkdir))));
+ when_all_p(rmlink, rmfile, rmdir).get();
+ }
+ // For the laugh, do it synchronously
+ test->unlink();
+}
diff --git a/attic/test/tests/async_io_pagesize.cpp b/attic/test/tests/async_io_pagesize.cpp
new file mode 100644
index 00000000..b988164c
--- /dev/null
+++ b/attic/test/tests/async_io_pagesize.cpp
@@ -0,0 +1,219 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_pagesize, "Tests that the utility functions work", 120)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ typedef chrono::duration<double, ratio<1, 1>> secs_type;
+
+ std::cout << "\n\nSystem page sizes are: " << std::endl;
+ for(auto &i : utils::page_sizes(false))
+ std::cout << " " << i << " bytes" << std::endl;
+ BOOST_CHECK(!utils::page_sizes(false).empty());
+ std::cout << "\n\nActually available system page sizes are: " << std::endl;
+ for(auto &i : utils::page_sizes(true))
+ std::cout << " " << i << " bytes" << std::endl;
+ BOOST_CHECK(!utils::page_sizes(true).empty());
+
+ std::vector<char, utils::page_allocator<char>> fba(8*1024*1024);
+ auto fba_detail(utils::detail::calculate_large_page_allocation(8*1024*1024));
+ std::cout << "\n\nAllocating 8Mb with the file buffer allocator yields an address at " << ((void *) fba.data())
+ << " and may use pages of " << fba_detail.page_size_used << " and be actually "
+ << fba_detail.actual_size << " bytes allocated." << std::endl;
+
+ auto randomstring(utils::random_string(32));
+ std::cout << "\n\n256 bits of random string might be: " << randomstring << " which is " << randomstring.size() << " bytes long." << std::endl;
+ BOOST_CHECK(randomstring.size()==64);
+ auto begin=chrono::high_resolution_clock::now();
+ while(chrono::duration_cast<secs_type>(chrono::high_resolution_clock::now()-begin).count()<3);
+ static const size_t ITEMS=1000000;
+ std::vector<char> buffer(32*ITEMS, ' ');
+ begin=chrono::high_resolution_clock::now();
+ for(size_t n=0; n<buffer.size()/32; n++)
+ utils::random_fill(buffer.data()+n*32, 32);
+ auto end=chrono::high_resolution_clock::now();
+ auto diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "\n\nKernel can generate " << (buffer.size()/diff.count()/1024/1024) << " Mb/sec of 256 bit cryptographic randomness" << std::endl;
+
+ std::vector<std::vector<char>> filenames1(ITEMS, std::vector<char>(64, ' '));
+ begin=chrono::high_resolution_clock::now();
+ for(size_t n=0; n<ITEMS; n++)
+ utils::to_hex_string(const_cast<char *>(filenames1[n].data()), 64, buffer.data()+n*32, 32);
+ end=chrono::high_resolution_clock::now();
+ diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "\n\nto_hex_string can convert " << (ITEMS*64/diff.count()/1024/1024) << " Mb/sec of 256 bit numbers to hex" << std::endl;
+
+ std::vector<char> buffer1(32*ITEMS, ' ');
+ begin=chrono::high_resolution_clock::now();
+ for(size_t n=0; n<ITEMS; n++)
+ utils::from_hex_string(buffer1.data()+n*32, 32, filenames1[n].data(), 64);
+ end=chrono::high_resolution_clock::now();
+ diff=chrono::duration_cast<secs_type>(end-begin);
+ std::cout << "\n\nfrom_hex_string can convert " << (ITEMS*64/diff.count()/1024/1024) << " Mb/sec of hex to 256 bit numbers" << std::endl;
+ BOOST_CHECK(!memcmp(buffer.data(), buffer1.data(), buffer.size()));
+
+#if !RUNNING_ON_SANITIZER
+#ifndef _MSC_VER
+#if defined(__i386__) || defined(__x86_64__)
+ static int have_popcnt=[]{
+ size_t cx, dx;
+#if defined(__x86_64__)
+ asm("cpuid": "=c" (cx), "=d" (dx) : "a" (1), "b" (0), "c" (0), "d" (0));
+#else
+ asm("pushl %%ebx\n\tcpuid\n\tpopl %%ebx\n\t": "=c" (cx), "=d" (dx) : "a" (1), "c" (0), "d" (0));
+#endif
+ return (dx&(1<<26))!=0/*SSE2*/ && (cx&(1<<23))!=0/*POPCNT*/;
+ }();
+ std::cout << "\n\nThis CPU has the popcnt instruction: " << have_popcnt << std::endl;
+#endif
+#endif
+ {
+ static BOOST_CONSTEXPR_OR_CONST size_t bytes=4096;
+ std::vector<char> buffer(bytes);
+ utils::random_fill(buffer.data(), bytes);
+ utils::secded_ecc<bytes> engine;
+ typedef utils::secded_ecc<bytes>::result_type ecc_type;
+ ecc_type eccbits=engine.result_bits_valid();
+ std::cout << "\n\nECC will be " << eccbits << " bits long" << std::endl;
+ ecc_type ecc=engine(buffer.data());
+ std::cout << "ECC was calculated to be " << std::hex << ecc << std::dec << std::endl;
+
+ auto end=std::chrono::high_resolution_clock::now(), begin=std::chrono::high_resolution_clock::now();
+ auto diff=std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(end-begin);
+#ifdef _MSC_VER
+ auto rdtsc=[]
+ {
+ return (unsigned long long) __rdtsc();
+ };
+#else
+#ifdef __rdtsc
+ return (unsigned long long) __rdtsc();
+#elif defined(__x86_64__)
+ auto rdtsc=[]
+ {
+ unsigned lo, hi;
+ asm volatile ("rdtsc" : "=a"(lo), "=d"(hi));
+ return (unsigned long long) lo | ((unsigned long long) hi<<32);
+ };
+#elif defined(__i386__)
+ auto rdtsc=[]
+ {
+ unsigned count;
+ asm volatile ("rdtsc" : "=a"(count));
+ return (unsigned long long) count;
+ };
+#endif
+#if __ARM_ARCH>=6
+ auto rdtsc=[]
+ {
+ unsigned count;
+ asm volatile ("MRC p15, 0, %0, c9, c13, 0" : "=r"(count));
+ return (unsigned long long) count * 64;
+ };
+#endif
+#endif
+ unsigned long long _begin=rdtsc(), _end;
+#if 1
+ do
+ {
+ end=std::chrono::high_resolution_clock::now();
+ } while(std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(end-begin).count()<1);
+ _end=rdtsc();
+ std::cout << "There are " << (_end-_begin) << " TSCs in 1 second." << std::endl;
+#endif
+
+ std::cout << "Flipping every bit in the buffer to see if it is correctly detected ..." << std::endl;
+ begin=std::chrono::high_resolution_clock::now();
+ for(size_t toflip=0; toflip<bytes*8; toflip++)
+ {
+ buffer[toflip/8]^=((size_t)1<<(toflip%8));
+ ecc_type newecc=engine(buffer.data());
+ if(ecc==newecc)
+ {
+ std::cerr << "ERROR: Flipping bit " << toflip << " not detected!" << std::endl;
+ BOOST_CHECK(ecc!=newecc);
+ }
+ else
+ {
+ ecc_type badbit=engine.find_bad_bit(ecc, newecc);
+ if(badbit!=toflip)
+ {
+ std::cerr << "ERROR: Bad bit " << badbit << " is not the bit " << toflip << " we flipped!" << std::endl;
+ BOOST_CHECK(badbit==toflip);
+ }
+ // else
+ // std::cout << "SUCCESS: Bit flip " << toflip << " correctly detected" << std::endl;
+ }
+ if(2!=engine.verify(buffer.data(), ecc))
+ {
+ std::cerr << "ERROR: verify() did not heal the buffer!" << std::endl;
+ BOOST_CHECK(false);
+ }
+ }
+ end=std::chrono::high_resolution_clock::now();
+ diff=std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(end-begin);
+ std::cout << "Checking and fixing is approximately " << (bytes*10000/diff.count()/1024/1024) << " Mb/sec" << std::endl;
+
+ std::cout << "\nFlipping two bits in the buffer to see if it is correctly detected ..." << std::endl;
+ buffer[0]^=1;
+ begin=std::chrono::high_resolution_clock::now();
+ for(size_t toflip=1; toflip<bytes*8; toflip++)
+ {
+ buffer[toflip/8]^=((size_t)1<<(toflip%8));
+ ecc_type newecc=engine(buffer.data());
+ if(ecc==newecc)
+ {
+ std::cerr << "ERROR: Flipping bits 0 and " << toflip << " not detected!" << std::endl;
+ BOOST_CHECK(ecc!=newecc);
+ }
+ buffer[toflip/8]^=((size_t)1<<(toflip%8));
+ }
+ end=std::chrono::high_resolution_clock::now();
+ diff=std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(end-begin);
+ std::cout << "Calculating is approximately " << (bytes*10000/diff.count()/1024/1024) << " Mb/sec" << std::endl;
+
+ std::cout << "\nCalculating speeds ..." << std::endl;
+ size_t foo=0;
+ begin=std::chrono::high_resolution_clock::now();
+ _begin=rdtsc();
+ for(size_t n=0; n<10000; n++)
+ {
+ buffer[0]=(char)n;
+ foo+=engine(buffer.data());
+ }
+ _end=rdtsc();
+ end=std::chrono::high_resolution_clock::now();
+ diff=std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(end-begin);
+ if(foo)
+ std::cout << "Fixed buffer size calculating is approximately " << (bytes*10000/diff.count()/1024/1024) << " Mb/sec, or " << ((_end-_begin)/10000.0/4096) << " cycles/byte" << std::endl;
+ foo=0;
+ begin=std::chrono::high_resolution_clock::now();
+ _begin=rdtsc();
+ for(size_t n=0; n<10000; n++)
+ {
+ buffer[0]=(char)n;
+ foo+=engine(buffer.data(), bytes);
+ }
+ _end=rdtsc();
+ end=std::chrono::high_resolution_clock::now();
+ diff=std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(end-begin);
+ if(foo)
+ std::cout << "Variable buffer size calculating is approximately " << (bytes*10000/diff.count()/1024/1024) << " Mb/sec, or " << ((_end-_begin)/10000.0/4096) << " cycles/byte" << std::endl;
+ foo=0;
+ begin=std::chrono::high_resolution_clock::now();
+ for(size_t n=0; n<1000; n++)
+ {
+ buffer[0]=(char)n;
+ foo+=engine.verify(buffer.data(), ecc);
+ }
+ end=std::chrono::high_resolution_clock::now();
+ diff=std::chrono::duration_cast<std::chrono::duration<double, std::ratio<1, 1>>>(end-begin);
+ if(foo)
+ std::cout << "Checking and fixing is approximately " << (bytes*1000/diff.count()/1024/1024) << " Mb/sec" << std::endl;
+ }
+#endif
+
+ auto dispatcher=make_dispatcher().get();
+ std::cout << "\n\nThread source use count is: " << dispatcher->threadsource().use_count() << std::endl;
+ BOOST_AFIO_CHECK_THROWS(dispatcher->op_from_scheduled_id(78));
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_statfs_test.cpp b/attic/test/tests/async_io_statfs_test.cpp
new file mode 100644
index 00000000..3ae18dc1
--- /dev/null
+++ b/attic/test/tests/async_io_statfs_test.cpp
@@ -0,0 +1,47 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_statfs, "Tests statfs", 20)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ auto dispatcher = make_dispatcher().get();
+ std::cout << "\n\nTesting statfs:\n";
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ auto mkfile(dispatcher->file(path_req::relative(mkdir, "foo", file_flags::create | file_flags::read_write)));
+ auto statfs_(dispatcher->statfs(mkfile, fs_metadata_flags::All));
+ auto delfile(dispatcher->rmfile(statfs_));
+ auto closefile=dispatcher->close(delfile);
+ BOOST_CHECK_NO_THROW(when_all_p(mkdir, mkfile, statfs_, closefile, delfile).get());
+ auto deldir(dispatcher->rmdir(mkdir));
+ BOOST_CHECK_NO_THROW(when_all_p(deldir).wait()); // virus checkers sometimes make this spuriously fail
+
+ auto statfs(statfs_.get());
+#define PRINT_FIELD(field, ...) \
+ std::cout << " f_flags." #field ": "; std::cout << statfs.f_flags.field __VA_ARGS__ << std::endl
+ PRINT_FIELD(rdonly);
+ PRINT_FIELD(noexec);
+ PRINT_FIELD(nosuid);
+ PRINT_FIELD(acls);
+ PRINT_FIELD(xattr);
+ PRINT_FIELD(compression);
+ PRINT_FIELD(extents);
+ PRINT_FIELD(filecompression);
+#undef PRINT_FIELD
+#define PRINT_FIELD(field, ...) \
+ std::cout << " f_" #field ": "; std::cout << statfs.f_##field __VA_ARGS__ << std::endl
+ PRINT_FIELD(bsize);
+ PRINT_FIELD(iosize);
+ PRINT_FIELD(blocks, << " (" << (statfs.f_blocks*statfs.f_bsize / 1024.0 / 1024.0 / 1024.0) << " Gb)");
+ PRINT_FIELD(bfree, << " (" << (statfs.f_bfree*statfs.f_bsize / 1024.0 / 1024.0 / 1024.0) << " Gb)");
+ PRINT_FIELD(bavail, << " (" << (statfs.f_bavail*statfs.f_bsize / 1024.0 / 1024.0 / 1024.0) << " Gb)");
+ PRINT_FIELD(files);
+ PRINT_FIELD(ffree);
+ PRINT_FIELD(namemax);
+#ifndef WIN32
+ PRINT_FIELD(owner);
+#endif
+ PRINT_FIELD(fsid, [0] << statfs.f_fsid[1]);
+ PRINT_FIELD(fstypename);
+ PRINT_FIELD(mntfromname);
+ PRINT_FIELD(mntonname);
+#undef PRINT_FIELD
+}
diff --git a/attic/test/tests/async_io_sync_test.cpp b/attic/test/tests/async_io_sync_test.cpp
new file mode 100644
index 00000000..67db7dca
--- /dev/null
+++ b/attic/test/tests/async_io_sync_test.cpp
@@ -0,0 +1,35 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_sync, "Tests async fsync", 5)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ std::vector<char> buffer(64, 'n');
+ auto dispatcher = make_dispatcher("file:///", file_flags::always_sync).get();
+ std::cout << "\n\nTesting synchronous directory and file creation:\n";
+ {
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ auto mkfile(dispatcher->file(path_req::relative(mkdir, "foo", file_flags::create | file_flags::read_write)));
+ auto writefile1(dispatcher->write(io_req < std::vector < char >> (mkfile, buffer, 0)));
+ auto sync1(dispatcher->sync(writefile1));
+ auto writefile2(dispatcher->write(io_req < std::vector < char >> (sync1, buffer, 0)));
+ auto closefile1(dispatcher->close(writefile2));
+ auto openfile(dispatcher->file(path_req::relative(closefile1, file_flags::read)));
+ char b[64];
+ auto readfile(dispatcher->read(make_io_req(openfile, b, 0)));
+ auto closefile2=dispatcher->close(readfile);
+ auto delfile(dispatcher->rmfile(dispatcher->depends(closefile2, closefile1)));
+ auto deldir(dispatcher->rmdir(dispatcher->depends(delfile, mkdir)));
+ BOOST_CHECK_NO_THROW(mkdir.get());
+ BOOST_CHECK_NO_THROW(mkfile.get());
+ BOOST_CHECK_NO_THROW(writefile1.get());
+ BOOST_CHECK_NO_THROW(sync1.get());
+ BOOST_CHECK_NO_THROW(writefile2.get());
+ BOOST_CHECK_NO_THROW(closefile1.get());
+ BOOST_CHECK_NO_THROW(openfile.get());
+ BOOST_CHECK_NO_THROW(readfile.get());
+ BOOST_CHECK_NO_THROW(closefile2.get());
+ BOOST_CHECK_NO_THROW(delfile.get());
+ BOOST_CHECK_NO_THROW(deldir.wait()); // virus checkers sometimes make this spuriously fail
+ }
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_threadpool_test.cpp b/attic/test/tests/async_io_threadpool_test.cpp
new file mode 100644
index 00000000..d084671c
--- /dev/null
+++ b/attic/test/tests/async_io_threadpool_test.cpp
@@ -0,0 +1,54 @@
+/* Unit tests for TripleGit
+(C) 2013 Niall Douglas http://www.nedprod.com/
+Created: Feb 2013
+*/
+
+#include "test_functions.hpp"
+
+static int task()
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ thread::id this_id = this_thread::get_id();
+ std::cout << "I am worker thread " << this_id << std::endl;
+ return 78;
+}
+
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_thread_pool_works, "Tests that the async i/o thread pool implementation works", 10)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+
+ thread::id this_id = this_thread::get_id();
+
+ std::cout << "I am main thread " << this_id << std::endl;
+ std_thread_pool pool(4);
+ auto r=task();
+ BOOST_CHECK(r==78);
+ std::vector<shared_future<int>> results(8);
+
+ for(auto &i: results)
+ {
+ i=pool.enqueue(task);
+ }
+
+#if BOOST_AFIO_USE_BOOST_THREAD && defined(BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY)
+ std::vector<shared_future<int>> results2;
+ results2.push_back(pool.enqueue(task));
+ results2.push_back(pool.enqueue(task));
+ std::pair<size_t, int> allresults2=boost::when_any(results2.begin(), results2.end()).get();
+ BOOST_CHECK(allresults2.first<2);
+ BOOST_CHECK(allresults2.second==78);
+ std::vector<int> allresults=boost::when_all_p(results.begin(), results.end()).get();
+#else
+ std::vector<int> allresults;
+ for(auto &i : results)
+ allresults.push_back(i.get());
+#endif
+
+ for(int i: allresults)
+ {
+ BOOST_CHECK(i==78);
+ }
+}
diff --git a/attic/test/tests/async_io_torture_autoflush_test.cpp b/attic/test/tests/async_io_torture_autoflush_test.cpp
new file mode 100644
index 00000000..8bbe0557
--- /dev/null
+++ b/attic/test/tests/async_io_torture_autoflush_test.cpp
@@ -0,0 +1,12 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_torture_autoflush, "Tortures the autoflush async i/o implementation", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+#ifndef BOOST_AFIO_RUNNING_IN_CI
+ auto dispatcher = make_dispatcher("file:///", file_flags::sync_on_close).get();
+ std::cout << "\n\nSustained random autoflush i/o to 10 files of 1Mb:\n";
+ evil_random_io(dispatcher, 10, 1 * 1024 * 1024);
+#endif
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_torture_direct_sync_test.cpp b/attic/test/tests/async_io_torture_direct_sync_test.cpp
new file mode 100644
index 00000000..19b03011
--- /dev/null
+++ b/attic/test/tests/async_io_torture_direct_sync_test.cpp
@@ -0,0 +1,12 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_torture_directsync, "Tortures the direct synchronous async i/o implementation", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+#ifndef BOOST_AFIO_RUNNING_IN_CI
+ auto dispatcher = make_dispatcher("file:///", file_flags::os_direct | file_flags::always_sync).get();
+ std::cout << "\n\nSustained random direct synchronous i/o to 10 files of 1Mb:\n";
+ evil_random_io(dispatcher, 10, 1 * 1024 * 1024, 4096);
+#endif
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_torture_direct_test.cpp b/attic/test/tests/async_io_torture_direct_test.cpp
new file mode 100644
index 00000000..149e7503
--- /dev/null
+++ b/attic/test/tests/async_io_torture_direct_test.cpp
@@ -0,0 +1,10 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_torture_direct, "Tortures the direct async i/o implementation", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ auto dispatcher = make_dispatcher("file:///", file_flags::os_direct).get();
+ std::cout << "\n\nSustained random direct i/o to 10 files of 10Mb:\n";
+ evil_random_io(dispatcher, 10, 10 * 1024 * 1024, 4096);
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_torture_sync_test.cpp b/attic/test/tests/async_io_torture_sync_test.cpp
new file mode 100644
index 00000000..920111c3
--- /dev/null
+++ b/attic/test/tests/async_io_torture_sync_test.cpp
@@ -0,0 +1,12 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_torture_sync, "Tortures the synchronous async i/o implementation", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+#ifndef BOOST_AFIO_RUNNING_IN_CI
+ auto dispatcher = make_dispatcher("file:///", file_flags::always_sync).get();
+ std::cout << "\n\nSustained random synchronous i/o to 10 files of 1Mb:\n";
+ evil_random_io(dispatcher, 10, 1 * 1024 * 1024);
+#endif
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_torture_test.cpp b/attic/test/tests/async_io_torture_test.cpp
new file mode 100644
index 00000000..4d3d6a7d
--- /dev/null
+++ b/attic/test/tests/async_io_torture_test.cpp
@@ -0,0 +1,10 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_torture, "Tortures the async i/o implementation", 120)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ auto dispatcher = make_dispatcher("file:///", file_flags::none).get();
+ std::cout << "\n\nSustained random i/o to 10 files of 10Mb:\n";
+ evil_random_io(dispatcher, 10, 10 * 1024 * 1024);
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_works_1_autoflush_test.cpp b/attic/test/tests/async_io_works_1_autoflush_test.cpp
new file mode 100644
index 00000000..40edf7d6
--- /dev/null
+++ b/attic/test/tests/async_io_works_1_autoflush_test.cpp
@@ -0,0 +1,13 @@
+#include "test_functions.hpp"
+
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_works_1_autoflush, "Tests that the autoflush async i/o implementation works", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+#ifndef BOOST_AFIO_RUNNING_IN_CI
+ auto dispatcher=make_dispatcher("file:///", file_flags::sync_on_close).get();
+ std::cout << "\n\n1000 file opens, writes 1 byte, closes, and deletes with autoflush i/o:\n";
+ _1000_open_write_close_deletes(dispatcher, 1);
+#endif
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_works_1_prime_test.cpp b/attic/test/tests/async_io_works_1_prime_test.cpp
new file mode 100644
index 00000000..6e441cd7
--- /dev/null
+++ b/attic/test/tests/async_io_works_1_prime_test.cpp
@@ -0,0 +1,10 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_works_1prime, "Tests that the async i/o implementation works (primes system)", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ auto dispatcher=make_dispatcher("file:///", file_flags::none).get();
+ std::cout << "\n\n1000 file opens, writes 1 byte, closes, and deletes (primes system):\n";
+ _1000_open_write_close_deletes(dispatcher, 1);
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_works_1_sync_test.cpp b/attic/test/tests/async_io_works_1_sync_test.cpp
new file mode 100644
index 00000000..99e6a666
--- /dev/null
+++ b/attic/test/tests/async_io_works_1_sync_test.cpp
@@ -0,0 +1,12 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_works_1_sync, "Tests that the synchronous async i/o implementation works", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+#ifndef BOOST_AFIO_RUNNING_IN_CI
+ auto dispatcher=make_dispatcher("file:///", file_flags::always_sync).get();
+ std::cout << "\n\n1000 file opens, writes 1 byte, closes, and deletes with synchronous i/o:\n";
+ _1000_open_write_close_deletes(dispatcher, 1);
+#endif
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_works_1_test.cpp b/attic/test/tests/async_io_works_1_test.cpp
new file mode 100644
index 00000000..d8b92415
--- /dev/null
+++ b/attic/test/tests/async_io_works_1_test.cpp
@@ -0,0 +1,10 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_works_1, "Tests that the async i/o implementation works", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ auto dispatcher=make_dispatcher("file:///", file_flags::none).get();
+ std::cout << "\n\n1000 file opens, writes 1 byte, closes, and deletes:\n";
+ _1000_open_write_close_deletes(dispatcher, 1);
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_works_64_autoflush_test.cpp b/attic/test/tests/async_io_works_64_autoflush_test.cpp
new file mode 100644
index 00000000..a3a808d2
--- /dev/null
+++ b/attic/test/tests/async_io_works_64_autoflush_test.cpp
@@ -0,0 +1,12 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_works_64_autoflush, "Tests that the autoflush async i/o implementation works", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+#ifndef BOOST_AFIO_RUNNING_IN_CI
+ auto dispatcher=make_dispatcher("file:///", file_flags::sync_on_close).get();
+ std::cout << "\n\n1000 file opens, writes 64Kb, closes, and deletes with autoflush i/o:\n";
+ _1000_open_write_close_deletes(dispatcher, 65536);
+#endif
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_works_64_direct_test.cpp b/attic/test/tests/async_io_works_64_direct_test.cpp
new file mode 100644
index 00000000..b085dfa0
--- /dev/null
+++ b/attic/test/tests/async_io_works_64_direct_test.cpp
@@ -0,0 +1,10 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_works_64_direct, "Tests that the direct async i/o implementation works", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ auto dispatcher = make_dispatcher("file:///", file_flags::os_direct).get();
+ std::cout << "\n\n1000 file opens, writes 64Kb, closes, and deletes with direct i/o:\n";
+ _1000_open_write_close_deletes(dispatcher, 65536);
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_works_64_directsync_test.cpp b/attic/test/tests/async_io_works_64_directsync_test.cpp
new file mode 100644
index 00000000..a4fffd26
--- /dev/null
+++ b/attic/test/tests/async_io_works_64_directsync_test.cpp
@@ -0,0 +1,12 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_works_64_directsync, "Tests that the direct synchronous async i/o implementation works", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+#ifndef BOOST_AFIO_RUNNING_IN_CI
+ auto dispatcher = make_dispatcher("file:///", file_flags::os_direct | file_flags::always_sync).get();
+ std::cout << "\n\n1000 file opens, writes 64Kb, closes, and deletes with direct synchronous i/o:\n";
+ _1000_open_write_close_deletes(dispatcher, 65536);
+#endif
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_works_64_sync_test.cpp b/attic/test/tests/async_io_works_64_sync_test.cpp
new file mode 100644
index 00000000..5cd9684c
--- /dev/null
+++ b/attic/test/tests/async_io_works_64_sync_test.cpp
@@ -0,0 +1,12 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_works_64_sync, "Tests that the synchronous async i/o implementation works", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+#ifndef BOOST_AFIO_RUNNING_IN_CI
+ auto dispatcher=make_dispatcher("file:///", file_flags::always_sync).get();
+ std::cout << "\n\n1000 file opens, writes 64Kb, closes, and deletes with synchronous i/o:\n";
+ _1000_open_write_close_deletes(dispatcher, 65536);
+#endif
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_works_64_test.cpp b/attic/test/tests/async_io_works_64_test.cpp
new file mode 100644
index 00000000..196a3e48
--- /dev/null
+++ b/attic/test/tests/async_io_works_64_test.cpp
@@ -0,0 +1,10 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_works_64, "Tests that the async i/o implementation works", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ auto dispatcher=make_dispatcher("file:///", file_flags::none).get();
+ std::cout << "\n\n1000 file opens, writes 64Kb, closes, and deletes:\n";
+ _1000_open_write_close_deletes(dispatcher, 65536);
+} \ No newline at end of file
diff --git a/attic/test/tests/async_io_zero_test.cpp b/attic/test/tests/async_io_zero_test.cpp
new file mode 100644
index 00000000..8857468a
--- /dev/null
+++ b/attic/test/tests/async_io_zero_test.cpp
@@ -0,0 +1,130 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(async_io_zero, "Tests async range content zeroing of sparse and compressed files", 60)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ std::vector<char> buffer(1024*1024, 'n');
+ ranctx ctx; raninit(&ctx, 1);
+ u4 *buf=(u4 *) buffer.data();
+ for(size_t n=0; n<buffer.size()/sizeof(*buf); n++)
+ buf[n]=ranval(&ctx);
+ auto dispatcher = make_dispatcher().get();
+ std::cout << "\n\nTesting async range content zeroing of sparse and compressed files:\n";
+ {
+ bool compressed_file_support = true;
+ // Create a 1Mb file
+ auto mkdir(dispatcher->dir(path_req("testdir", file_flags::create)));
+ auto mkfilesp(dispatcher->file(path_req::relative(mkdir, "sparse", file_flags::create | file_flags::read_write)));
+ auto mkfilec(dispatcher->file(path_req::relative(mkdir, "compressed", file_flags::create | file_flags::create_compressed | file_flags::read_write)));
+ // FAT32 or ReFS will error out on compressed file creation
+ mkfilec.wait();
+ if (mkfilec.has_error())
+ {
+ mkfilec = future<>();
+ compressed_file_support = false;
+ }
+ auto resizefilesp(dispatcher->truncate(mkfilesp, buffer.size()));
+ auto resizefilec(compressed_file_support ? dispatcher->truncate(mkfilec, buffer.size()) : mkfilec);
+ auto writefilesp(dispatcher->write(make_io_req(resizefilesp, buffer, 0)));
+ auto writefilec(compressed_file_support ? dispatcher->write(make_io_req(resizefilec, buffer, 0)) : resizefilec);
+ // Need to fsync to work around lazy or delayed allocation
+ auto syncfilesp1(dispatcher->sync(writefilesp));
+ auto syncfilec1(compressed_file_support ? dispatcher->sync(writefilec) : writefilec);
+ BOOST_REQUIRE_NO_THROW(when_all_p(mkdir, mkfilesp, resizefilesp, writefilesp, syncfilesp1).get());
+ if(compressed_file_support)
+ BOOST_REQUIRE_NO_THROW(when_all_p(mkfilec, resizefilec, writefilec, syncfilec1).get());
+
+ // Verify they really does consume 1Mb of disc space
+ stat_t beforezerostatsp=mkfilesp->lstat(metadata_flags::All);
+ stat_t beforezerostatc= compressed_file_support ? mkfilec->lstat(metadata_flags::All) : stat_t();
+ BOOST_REQUIRE(beforezerostatsp.st_size==buffer.size());
+ if(compressed_file_support) BOOST_REQUIRE(beforezerostatc.st_size==buffer.size());
+ if(beforezerostatsp.st_allocated<buffer.size())
+ {
+ BOOST_WARN_MESSAGE(false, "The sparse file allocation is smaller than expected, is this file system compressed?");
+ std::cout << "WARNING: The sparse file allocation is smaller than expected, is this file system compressed? allocated=" << beforezerostatsp.st_allocated << "." << std::endl;
+ }
+ if(compressed_file_support && beforezerostatc.st_compressed)
+ {
+ std::cout << "The compressed file consumes " << beforezerostatc.st_allocated << " bytes on disc for " << beforezerostatc.st_size << " bytes." << std::endl;
+ }
+ else
+ {
+ BOOST_WARN_MESSAGE(false, "File isn't marked as compressed, assuming no filing system support for per-file compression.");
+ std::cout << "File isn't marked as compressed, assuming no filing system support for per-file compression." << std::endl;
+ }
+
+ // Punch a 256Mb hole into the front and make sure it's definitely zero, again fsyncing to workaround delayed deallocation
+ std::vector<char> buffer2(buffer.size()/4, 'n');
+ auto punchholesp(dispatcher->zero(writefilesp, {{0, buffer2.size()}, {buffer.size()/2, buffer2.size()}}));
+ auto syncfilesp2(dispatcher->sync(punchholesp));
+ auto readfilesp1(dispatcher->read(make_io_req(syncfilesp2, buffer2, 0)));
+ BOOST_CHECK_NO_THROW(when_all_p(punchholesp, syncfilesp2, readfilesp1).get());
+ bool allzero=true;
+ for(auto &i : buffer2)
+ if(i) { allzero=false; break; }
+ BOOST_CHECK(allzero);
+ auto punchholec(compressed_file_support ? dispatcher->zero(writefilec, {{0, buffer2.size()}, {buffer.size()/2, buffer2.size()}}) : writefilec);
+ auto syncfilec2(compressed_file_support ? dispatcher->sync(punchholec) : punchholec);
+ auto readfilec1(compressed_file_support ? dispatcher->read(make_io_req(syncfilec2, buffer2, 0)) : syncfilec2);
+ if(compressed_file_support)
+ BOOST_CHECK_NO_THROW(when_all_p(punchholec, syncfilec2, readfilec1).get());
+ allzero = true;
+ for(auto &i : buffer2)
+ if(i) { allzero = false; break; }
+ BOOST_CHECK(allzero);
+
+ // Sleep for a second to let the filing system do delayed deallocation
+ this_thread::sleep_for(chrono::seconds(1));
+
+ // Verify they now consumes less than 1Mb of disc space
+ stat_t afterzerostatsp=mkfilesp->lstat(metadata_flags::All);
+ stat_t afterzerostatc=compressed_file_support ? mkfilec->lstat(metadata_flags::All) : stat_t();
+ BOOST_REQUIRE(afterzerostatsp.st_size==buffer.size());
+ if(compressed_file_support)
+ BOOST_REQUIRE(afterzerostatc.st_size==buffer.size());
+ std::cout << "The sparse file now consumes " << afterzerostatsp.st_allocated << " bytes on disc for " << afterzerostatsp.st_size << " bytes." << std::endl;
+ if(compressed_file_support)
+ std::cout << "The compressed file now consumes " << afterzerostatc.st_allocated << " bytes on disc for " << afterzerostatc.st_size << " bytes." << std::endl;
+ if(afterzerostatsp.st_allocated==buffer.size())
+ {
+ BOOST_WARN_MESSAGE(false, "This filing system does not support sparse files, so skipping some tests.");
+ std::cout << "This filing system does not support sparse files, so skipping some tests." << std::endl;
+ BOOST_CHECK(!afterzerostatsp.st_sparse);
+ }
+ else if(beforezerostatsp.st_allocated==buffer.size())
+ {
+ BOOST_CHECK(afterzerostatsp.st_sparse);
+ BOOST_CHECK((afterzerostatsp.st_allocated+2*buffer2.size())==beforezerostatsp.st_allocated);
+ auto extentssp=dispatcher->extents(mkfilesp).get();
+ // Tolerate systems which can't enumerate extents, one those a single file sized extent is returned
+ BOOST_CHECK(!extentssp.empty());
+ std::cout << "extents() reports " << extentssp.size() << " extents. These were:" << std::endl;
+ for(auto &i : extentssp)
+ std::cout << " " << i.first << ", " << i.second << std::endl;
+ BOOST_CHECK((extentssp.size()==2 || extentssp.size()==1));
+ if(extentssp.size()==1)
+ {
+ BOOST_WARN_MESSAGE(false, "This filing system reports one extent where there should be two, skipping some tests.");
+ }
+ else if(extentssp.size()==2)
+ {
+ BOOST_CHECK(extentssp[0].first==buffer.size()/4);
+ BOOST_CHECK(extentssp[0].second==buffer.size()/4);
+ BOOST_CHECK(extentssp[1].first==buffer.size()/4*3);
+ BOOST_CHECK(extentssp[1].second==buffer.size()/4);
+ }
+ }
+
+ auto delfilesp(dispatcher->rmfile(readfilesp1));
+ auto delfilec(compressed_file_support ? dispatcher->rmfile(readfilec1) : readfilec1);
+ auto closefilesp=dispatcher->close(delfilesp);
+ auto closefilec= compressed_file_support ? dispatcher->close(delfilec) : delfilec;
+ BOOST_CHECK_NO_THROW(when_all_p(closefilesp, delfilesp).get());
+ if(compressed_file_support)
+ BOOST_CHECK_NO_THROW(when_all_p(closefilec, delfilec).get());
+ auto deldir(dispatcher->rmdir(mkdir));
+ BOOST_CHECK_NO_THROW(when_all_p(deldir).wait()); // virus checkers sometimes make this spuriously fail
+ }
+} \ No newline at end of file
diff --git a/attic/test/tests/atomic_log_append_test.cpp b/attic/test/tests/atomic_log_append_test.cpp
new file mode 100644
index 00000000..5c1b2faf
--- /dev/null
+++ b/attic/test/tests/atomic_log_append_test.cpp
@@ -0,0 +1,150 @@
+#include "test_functions.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4201) // nameless struct union
+#endif
+BOOST_AFIO_AUTO_TEST_CASE(atomic_log_append, "Tests that atomic append to a shared log file works as expected", 60)
+{
+ std::cout << "\n\nTesting atomic append to a shared log file\n";
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ using BOOST_AFIO_V2_NAMESPACE::off_t;
+ try { filesystem::remove_all("testdir"); } catch(...) {}
+ filesystem::create_directory("testdir");
+ std::vector<thread> threads;
+ atomic<bool> done(false);
+ for(size_t n=0; n<4; n++)
+ {
+ threads.push_back(thread([&done, n]{
+ try
+ {
+//[extents_example
+ // Create a dispatcher
+ auto dispatcher = make_dispatcher().get();
+ // Schedule opening the log file for hole punching
+ auto logfilez(dispatcher->file(path_req("testdir/log",
+ file_flags::create | file_flags::read_write)));
+ // Schedule opening the log file for atomic appending of log entries
+ auto logfilea(dispatcher->file(path_req("testdir/log",
+ file_flags::create | file_flags::write | file_flags::append)));
+ // Retrieve any errors which occurred
+ logfilez.get(); logfilea.get();
+ // Initialise a random number generator
+ ranctx ctx; raninit(&ctx, (u4) n);
+ while(!done)
+ {
+ // Each log entry is 32 bytes in length
+ union
+ {
+ char bytes[32];
+ struct
+ {
+ uint64 id; // The id of the writer
+ uint64 r; // A random number
+ uint64 h1, h2; // A hash of the previous two items
+ };
+ } buffer;
+ buffer.id=n;
+ buffer.r=ranval(&ctx);
+ buffer.h1=buffer.h2=1;
+ SpookyHash::Hash128(buffer.bytes, 16, &buffer.h1, &buffer.h2);
+ // Atomically append the log entry to the log file and wait for it
+ // to complete, then fetch the new size of the log file.
+ stat_t s=dispatcher->write(make_io_req(logfilea,
+ buffer.bytes, 32, 0))->lstat();
+ if(s.st_allocated>8192 || s.st_size>8192)
+ {
+ // Allocated space exceeds 8Kb. The actual file size reported may be
+ // many times larger if the filing system supports hole punching.
+
+ // Get the list of allocated extents
+ std::vector<std::pair<off_t, off_t>> extents=
+ dispatcher->extents(logfilez).get();
+ // Filing system may not yet have allocated any storage ...
+ if (!extents.empty())
+ {
+ if (extents.back().second > 1024)
+ extents.back().second -= 1024;
+ else
+ extents.resize(extents.size() - 1);
+ if (!extents.empty())
+ {
+ dispatcher->zero(logfilez, extents).get();
+ }
+ }
+ else
+ std::cout << "NOTE: extents() returns empty despite " << s.st_allocated << " bytes allocated (possibly delayed allocation)" << std::endl;
+ }
+ }
+//]
+ }
+ catch(const system_error &e) { std::cerr << "ERROR: unit test exits via system_error code " << e.code().value() << "(" << e.what() << ")" << std::endl; BOOST_FAIL("Unit test exits via exception"); abort(); }
+ catch(const std::exception &e) { std::cerr << "ERROR: unit test exits via exception (" << e.what() << ")" << std::endl; BOOST_FAIL("Unit test exits via exception"); abort(); }
+ catch(...) { std::cerr << "ERROR: unit test exits via unknown exception" << std::endl; BOOST_FAIL("Unit test exits via exception"); abort(); }
+ }));
+ }
+ this_thread::sleep_for(chrono::seconds(10));
+ done=true;
+ std::cout << "Waiting for threads to exit ..." << std::endl;
+ for(auto &i : threads)
+ i.join();
+ // Examine the file for consistency
+ {
+ std::cout << "Examining the file, skipping zeros ..." << std::endl;
+ std::ifstream is("testdir/log", std::ifstream::binary);
+ size_t count=0;
+ uint64 hash1, hash2;
+ union
+ {
+ char bytes[32];
+ struct { uint64 r1, r2, h1, h2; };
+ } buffer;
+ bool isZero=true;
+ size_t entries=0;
+ while(is.good() && isZero)
+ {
+ is.read(buffer.bytes, 32);
+ if(!is) break;
+ count+=32;
+ for(size_t n=0; n<32; n++)
+ if(buffer.bytes[n])
+ {
+ isZero=false;
+ break;
+ }
+ }
+ std::cout << "Examining the file, checking records ..." << std::endl;
+ while(is.good())
+ {
+ if(isZero)
+ {
+ is.read(buffer.bytes, 32);
+ if(!is) break;
+ count+=32;
+ }
+ isZero=true;
+ for(size_t n=0; n<32; n++)
+ if(buffer.bytes[n]) isZero=false;
+ BOOST_CHECK(!isZero);
+ if(isZero)
+ {
+ std::cout << "(zero)" << std::endl;
+ }
+ else
+ {
+ // First 16 bytes is random, second 16 bytes is hash
+ hash1=hash2=1;
+ SpookyHash::Hash128(buffer.bytes, 16, &hash1, &hash2);
+ BOOST_CHECK((buffer.h1==hash1 && buffer.h2==hash2));
+ entries++;
+ isZero=true;
+ }
+ }
+ BOOST_CHECK((entries>=32 && entries<=8192/32+1));
+ std::cout << "There were " << entries << " valid entries in " << count << " bytes" << std::endl;
+ }
+ filesystem::remove_all("testdir");
+}
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
diff --git a/attic/test/tests/delete_stability.cpp b/attic/test/tests/delete_stability.cpp
new file mode 100644
index 00000000..43e15dd1
--- /dev/null
+++ b/attic/test/tests/delete_stability.cpp
@@ -0,0 +1,73 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(delete_stability, "Tests that deleting files and directories always succeeds", 120)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ static BOOST_CONSTEXPR_OR_CONST size_t ITERATIONS=1000;
+ static BOOST_CONSTEXPR_OR_CONST size_t ITEMS=10;
+ // Oh Windows, oh Windows, how strange you are ...
+ for (size_t n = 0; n < 10; n++)
+ {
+ try
+ {
+ if (filesystem::exists("testdir"))
+ filesystem::remove_all("testdir");
+ break;
+ }
+ catch (...)
+ {
+ this_thread::sleep_for(chrono::milliseconds(10));
+ }
+ }
+
+ try
+ {
+ // HoldParentOpen is actually ineffectual as renames zap the parent container, but it tests more code.
+ auto dispatcher = make_dispatcher("file:///", file_flags::hold_parent_open).get();
+ auto testdir = dispatcher->dir(path_req("testdir", file_flags::create));
+
+ // Monte Carlo creating or deleting lots of directories containing a few files of 4Kb
+ ranctx ctx;
+ raninit(&ctx, 1);
+ for(size_t iter=0; iter<ITERATIONS; iter++)
+ {
+ size_t idx=ranval(&ctx) % ITEMS;
+ if(filesystem::exists("testdir/"+to_string(idx)))
+ {
+ auto dirh=dispatcher->dir(path_req::relative(testdir, to_string(idx), file_flags::read_write));
+ std::vector<path_req> reqs={path_req::relative(dirh, "a", file_flags::read_write), path_req::relative(dirh, "b", file_flags::read_write), path_req::relative(dirh, "c", file_flags::read_write)};
+ auto files=dispatcher->file(reqs);
+ // Go synchronous for these
+ for(auto &i : files)
+ i->unlink();
+ dirh->unlink();
+ }
+ else
+ {
+ static char buffer[4096];
+ auto dirh=dispatcher->dir(path_req::relative(testdir, to_string(idx), file_flags::create));
+ std::vector<path_req> reqs={path_req::relative(dirh, "a", file_flags::create|file_flags::read_write), path_req::relative(dirh, "b", file_flags::create|file_flags::read_write), path_req::relative(dirh, "c", file_flags::create|file_flags::read_write)};
+ auto files=dispatcher->file(reqs);
+ auto resized=dispatcher->truncate(files, { sizeof(buffer), sizeof(buffer), sizeof(buffer)});
+ std::vector<io_req<char>> reqs2;
+ for(auto &i : resized)
+ reqs2.push_back(make_io_req(i, buffer, 0));
+ auto written=dispatcher->write(reqs2);
+ dirh.get();
+ when_all_p(files.begin(), files.end()).get();
+ when_all_p(resized.begin(), resized.end()).get();
+ when_all_p(written.begin(), written.end()).get();
+ }
+ }
+ BOOST_CHECK(true);
+
+ filesystem::remove_all("testdir");
+ }
+ catch(...)
+ {
+ BOOST_CHECK(false);
+ filesystem::remove_all("testdir");
+ throw;
+ }
+//]
+}
diff --git a/attic/test/tests/free_functions_test.cpp b/attic/test/tests/free_functions_test.cpp
new file mode 100644
index 00000000..78b424be
--- /dev/null
+++ b/attic/test/tests/free_functions_test.cpp
@@ -0,0 +1,31 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(free_functions_work, "Tests that the free functions work as intended", 5)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ using BOOST_AFIO_V2_NAMESPACE::file;
+ using BOOST_AFIO_V2_NAMESPACE::rmdir;
+ current_dispatcher_guard h(make_dispatcher().get());
+
+ {
+ auto diropened = async_dir("testdir", file_flags::create | file_flags::write);
+ auto fileopened = async_file(diropened, "testfile", file_flags::create | file_flags::write);
+ auto filedeleted = async_rmfile(fileopened);
+ auto dirdeleted = async_rmdir(depends(filedeleted, diropened));
+ dirdeleted.get();
+ }
+ {
+ auto diropened = dir("testdir", file_flags::create | file_flags::write);
+ auto fileopened = file(diropened, "testfile", file_flags::create | file_flags::write);
+ rmfile(fileopened);
+ rmdir(diropened);
+ }
+ {
+ error_code ec;
+ auto diropened = dir(ec, "testdir", file_flags::create | file_flags::write);
+ auto fileopened = file(ec, diropened, "testfile", file_flags::create | file_flags::write);
+ rmfile(ec, fileopened);
+ rmdir(ec, diropened);
+ }
+}
diff --git a/attic/test/tests/op_container_deduced_compilation.cpp b/attic/test/tests/op_container_deduced_compilation.cpp
new file mode 100644
index 00000000..49a958d4
--- /dev/null
+++ b/attic/test/tests/op_container_deduced_compilation.cpp
@@ -0,0 +1,20 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(op_container_deduced_compilation, "Tests that all the op container classes compile with single arg deduced construction", 10)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ namespace asio = BOOST_AFIO_V2_NAMESPACE::asio;
+ // Note that this test is mainly for testing metaprogramming compilation.
+ if(false)
+ {
+ auto dispatcher=make_dispatcher().get();
+ // Test that path_req constructs from path
+ auto file1(dispatcher->file(filesystem::path("foo")));
+ // Test that path_req constructs from literal
+ auto file2(dispatcher->file("foo"));
+ // Test that detail::io_req_impl<false> constructs from ?
+ // Test that detail::io_req_impl<true> constructs from ?
+ // Test that enumerate_req constructs from future<>
+ auto enum1(dispatcher->enumerate(file1));
+ }
+} \ No newline at end of file
diff --git a/attic/test/tests/path_works.cpp b/attic/test/tests/path_works.cpp
new file mode 100644
index 00000000..6ac56147
--- /dev/null
+++ b/attic/test/tests/path_works.cpp
@@ -0,0 +1,177 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(path_works, "Tests that the path functions work as they are supposed to", 20)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+ auto dispatcher = make_dispatcher().get();
+ auto dirh = dispatcher->dir(path_req("testdir", file_flags::create));
+ dirh.get();
+ {
+#ifdef WIN32
+#define BOOST_AFIO_PATH_WORKS_STR(s) L ## s
+#else
+#define BOOST_AFIO_PATH_WORKS_STR(s) s
+#endif
+ static const auto hellobabystr = BOOST_AFIO_PATH_WORKS_STR("hellobaby"), testfilestr = BOOST_AFIO_PATH_WORKS_STR("testfile"), foostr = BOOST_AFIO_PATH_WORKS_STR("foo");
+#undef BOOST_AFIO_PATH_WORKS_STR
+ {
+ future<> op = dispatcher->file(path_req("testdir/testfile", file_flags::create | file_flags::read_write));
+ auto h = op.get_handle();
+ auto originalpath = h->path();
+ print_stat(h);
+ auto originalpath2 = h->path();
+ BOOST_CHECK(originalpath == originalpath2);
+
+#ifdef WIN32
+ // Verify pass through
+ path p("testfile");
+ BOOST_CHECK(p.native() == L"testfile");
+
+ p = path::make_absolute("testfile");
+ BOOST_CHECK(p.native() != L"testfile");
+ // Make sure it prepended \??\ to make a NT kernel path
+ BOOST_CHECK((p.native()[0] == '\\' && p.native()[1] == '?' && p.native()[2] == '?' && p.native()[3] == '\\'));
+ BOOST_CHECK((isalpha(p.native()[4]) && p.native()[5] == ':'));
+ // Make sure it converts back via fast path
+ auto fp = p.filesystem_path();
+ BOOST_CHECK((fp.native()[0] == '\\' && fp.native()[1] == '\\' && fp.native()[2] == '?' && fp.native()[3] == '\\'));
+ BOOST_CHECK(p.native().substr(4) == fp.native().substr(4));
+ // Make sure it converts back perfectly via slow path
+ fp = normalise_path(p);
+ auto a = fp.native();
+ auto b = filesystem::absolute("testfile").native();
+ // Filesystem has been known to return lower case drive letters ...
+ std::transform(a.begin(), a.end(), a.begin(), ::tolower);
+ std::transform(b.begin(), b.end(), b.begin(), ::tolower);
+ if (b.size() >= 260)
+ b = L"\\\\?\\" + b;
+ std::wcout << a << " (sized " << a.size() << ")" << std::endl;
+ std::wcout << b << " (sized " << b.size() << ")" << std::endl;
+ BOOST_CHECK(a == b);
+
+ // Make sure it handles extended path inputs
+ p = filesystem::path("\\\\?") / filesystem::absolute("testfile");
+ BOOST_CHECK((p.native()[0] == '\\' && p.native()[1] == '?' && p.native()[2] == '?' && p.native()[3] == '\\'));
+ BOOST_CHECK((isalpha(p.native()[4]) && p.native()[5] == ':'));
+
+ // Make sure native NT path inputs are preserved
+ filesystem::path o("\\\\.\\Device1");
+ p = o;
+ BOOST_CHECK(p.native() == L"\\Device1");
+ fp = p.filesystem_path();
+ BOOST_CHECK(fp == o);
+
+#endif
+
+ std::cout << "\nRenaming testfile to hellobaby using OS ..." << std::endl;
+ filesystem::rename("testdir/testfile", "testdir/hellobaby");
+ print_stat(h);
+ auto afterrename = h->path();
+#ifndef __FreeBSD__ // FreeBSD can't track file renames
+ BOOST_CHECK((originalpath.parent_path() / hellobabystr) == afterrename);
+#endif
+ std::cout << "\nDeleting hellobaby file using OS ..." << std::endl;
+ filesystem::remove("testdir/hellobaby");
+ auto afterdelete = print_stat(h);
+ BOOST_CHECK(h->path() == BOOST_AFIO_V2_NAMESPACE::path());
+ std::cout << "\nEnumerating directory to make sure hellobaby is not there ..." << std::endl;
+ auto contents = dispatcher->enumerate(enumerate_req(dirh, metadata_flags::All, 50)).get().first;
+ for (auto &i : contents)
+ {
+ std::cout << " " << i.name() << std::endl;
+#ifndef WIN32 // Windows only marks for deletion, doesn't actually delete
+ BOOST_CHECK(i.name() != hellobabystr);
+#endif
+ }
+ }
+
+ std::cout << "\nCreating hard links testfile2 and testfile3 from testfile ..." << std::endl;
+ handle_ptr h;
+ future<> op = dispatcher->file(path_req::relative(dirh, testfilestr, file_flags::create | file_flags::read_write));
+ h = op.get_handle();
+ BOOST_CHECK(h->path(true) == dirh->path() / testfilestr);
+ bool supports_hard_links = true;
+ try
+ {
+ h->link(path_req::relative(dirh, "testfile2"));
+ }
+ catch (...)
+ {
+ supports_hard_links = false;
+ dispatcher->rmfile(path_req::relative(dirh, "testfile")).get();
+ }
+ if (supports_hard_links)
+ {
+ BOOST_CHECK(h->path(true) == dirh->path() / testfilestr);
+ h->link(path_req::relative(dirh, "testfile3"));
+ BOOST_CHECK(h->path(true) == dirh->path() / testfilestr);
+ dispatcher->truncate(op, 78).get();
+ dispatcher->sync(op).get();
+ auto entry = h->lstat();
+ BOOST_CHECK(entry.st_size == 78);
+ auto contents = dispatcher->enumerate(enumerate_req(dirh, metadata_flags::All, 50)).get().first;
+ BOOST_CHECK(contents.size() == 3);
+ for (auto &i : contents)
+ {
+ print_stat(dirh.get_handle(), i);
+ BOOST_CHECK(i.st_ino() == entry.st_ino);
+#ifndef WIN32 // Windows takes too long to update this
+ BOOST_CHECK(i.st_size() == entry.st_size);
+#endif
+ BOOST_CHECK(i.st_nlink() == 3);
+ }
+
+ std::cout << "\nRelinking hard link testfile to foo ..." << std::endl;
+ h->atomic_relink(path_req::relative(dirh, foostr));
+ BOOST_CHECK(h->path(true) == dirh->path() / foostr);
+ dispatcher->truncate(op, 79).get();
+ dispatcher->sync(op).get();
+ entry = h->lstat();
+ BOOST_CHECK(entry.st_size == 79);
+ contents = dispatcher->enumerate(enumerate_req(dirh, metadata_flags::All, 50)).get().first;
+ BOOST_CHECK(contents.size() == 3);
+ for (auto &i : contents)
+ {
+ print_stat(dirh.get_handle(), i);
+ BOOST_CHECK(i.name() != testfilestr);
+ BOOST_CHECK(i.st_ino() == entry.st_ino);
+#ifndef WIN32 // Windows takes too long to update this
+ BOOST_CHECK(i.st_size() == entry.st_size);
+#endif
+ BOOST_CHECK(i.st_nlink() == 3);
+ }
+
+ std::cout << "\nUnlinking hard links ..." << std::endl;
+ h->unlink();
+#ifndef __FreeBSD__ // FreeBSD will not notice a file with multiple hard links is deleted
+ BOOST_CHECK(h->path(true).empty());
+#endif
+ contents = dispatcher->enumerate(enumerate_req(dirh, metadata_flags::All, 50)).get().first;
+ BOOST_CHECK(contents.size() == 2);
+ for (auto &i : contents)
+ {
+ print_stat(dirh.get_handle(), i);
+ BOOST_CHECK(i.name() != foostr); // This should get filtered out by AFIO on Windows due to magic naming
+ BOOST_CHECK(i.st_nlink() == 2);
+ }
+ op = future<>();
+ h.reset(); // Should actually cause the unlink to really happen on Windows
+ contents = dispatcher->enumerate(enumerate_req(dirh, metadata_flags::All, 50)).get().first;
+ BOOST_CHECK(contents.size() == 2);
+ for (auto &i : contents)
+ {
+ print_stat(dirh.get_handle(), i);
+ BOOST_CHECK(i.name() != foostr);
+ BOOST_CHECK(i.st_nlink() == 2);
+ }
+ dispatcher->rmfile(path_req::relative(dirh, "testfile2")).get();
+ dispatcher->rmfile(path_req::relative(dirh, "testfile3")).get();
+ contents = dispatcher->enumerate(enumerate_req(dirh, metadata_flags::All, 50)).get().first;
+ BOOST_CHECK(contents.size() == 0);
+ }
+ }
+
+ // Reopen with write privs in order to unlink
+ dirh = dispatcher->dir(path_req("testdir", file_flags::read_write));
+ dirh->unlink();
+}
diff --git a/attic/test/tests/race_protection_works.cpp b/attic/test/tests/race_protection_works.cpp
new file mode 100644
index 00000000..e779878f
--- /dev/null
+++ b/attic/test/tests/race_protection_works.cpp
@@ -0,0 +1,180 @@
+#include "test_functions.hpp"
+
+BOOST_AFIO_AUTO_TEST_CASE(race_protection_works, "Tests that the race protection works", 300)
+{
+ using namespace BOOST_AFIO_V2_NAMESPACE;
+#ifdef __APPLE__
+ // This test cannot pass on OS X currently, so exit immediately.
+ return;
+#endif
+#if defined(__FreeBSD__) || defined(BOOST_AFIO_COMPILING_FOR_GCOV)
+ // ZFS is not a fan of this test
+ static BOOST_CONSTEXPR_OR_CONST size_t ITERATIONS=50;
+ static BOOST_CONSTEXPR_OR_CONST size_t ITEMS=10;
+#elif defined(WIN32)
+ // NTFS is punished by very slow file handle opening
+ static BOOST_CONSTEXPR_OR_CONST size_t ITERATIONS=50;
+ static BOOST_CONSTEXPR_OR_CONST size_t ITEMS=100;
+#else
+ static BOOST_CONSTEXPR_OR_CONST size_t ITERATIONS=100;
+ static BOOST_CONSTEXPR_OR_CONST size_t ITEMS=100;
+#endif
+ // Oh Windows, oh Windows, how strange you are ...
+ for (size_t n = 0; n < 10; n++)
+ {
+ try
+ {
+ if (filesystem::exists("testdir"))
+ filesystem::remove_all("testdir");
+ break;
+ }
+ catch (...)
+ {
+ this_thread::sleep_for(chrono::milliseconds(10));
+ }
+ }
+
+//[race_protection_example
+ try
+ {
+ // HoldParentOpen is actually ineffectual as renames zap the parent container, but it tests more code.
+ auto dispatcher = make_dispatcher("file:///", file_flags::hold_parent_open).get();
+ auto testdir = dispatcher->dir(path_req("testdir", file_flags::create));
+ future<> dirh;
+
+ try
+ {
+ // We can only reliably track directory renames on all platforms, so let's create 100 directories
+ // which will be constantly renamed to something different by a worker thread
+ std::vector<path_req> dirreqs;
+ for(size_t n=0; n<ITEMS; n++)
+ dirreqs.push_back(path_req::relative(testdir, to_string(n), file_flags::create|file_flags::read_write));
+ // Windows needs write access to the directory to enable relinking, but opening a handle
+ // with write access causes any renames into that directory to fail. So mark the first
+ // directory which is always the destination for renames as non-writable
+ dirreqs.front().flags=file_flags::create;
+ std::cout << "Creating " << ITEMS << " directories ..." << std::endl;
+ auto dirs=dispatcher->dir(dirreqs);
+ when_all_p(dirs).get();
+ dirh=dirs.front();
+ atomic<bool> done(false);
+ std::cout << "Creating worker thread to constantly rename those " << ITEMS << " directories ..." << std::endl;
+ thread worker([&done, &testdir, &dirs]{
+ for(size_t number=0; !done; number++)
+ {
+ try
+ {
+#ifdef WIN32
+ for(size_t n=1; n<ITEMS; n++)
+#else
+ for(size_t n=0; n<ITEMS; n++)
+#endif
+ {
+ path_req::relative req(testdir, to_string(number)+"_"+to_string(n));
+ //std::cout << "Renaming " << dirs[n].get()->path(true) << " ..." << std::endl;
+ try
+ {
+ dirs[n]->atomic_relink(req);
+ }
+#ifdef WIN32
+ catch(const system_error &/*e*/)
+ {
+ // Windows does not permit renaming a directory containing open file handles
+ //std::cout << "NOTE: Failed to rename directory " << dirs[n]->path() << " due to " << e.what() << ", this is usual on Windows." << std::endl;
+ }
+#else
+ catch(...)
+ {
+ throw;
+ }
+#endif
+ }
+ std::cout << "Worker relinked all dirs to " << number << std::endl;
+ }
+ catch(const system_error &e) { std::cerr << "ERROR: worker thread exits via system_error code " << e.code().value() << "(" << e.what() << ")" << std::endl; BOOST_CHECK(false); }
+ catch(const std::exception &e) { std::cerr << "ERROR: worker thread exits via exception (" << e.what() << ")" << std::endl; BOOST_CHECK(false); }
+ catch(...) { std::cerr << "ERROR: worker thread exits via unknown exception" << std::endl; BOOST_CHECK(false); }
+ }
+ });
+ auto unworker=detail::Undoer([&done, &worker]{done=true; worker.join();});
+
+ // Create some files inside the changing directories and rename them across changing directories
+ std::vector<future<>> newfiles;
+ for(size_t n=0; n<ITEMS; n++)
+ {
+ dirreqs[n].precondition=dirs[n];
+ dirreqs[n].flags=file_flags::create_only_if_not_exist|file_flags::read_write;
+ }
+ for(size_t i=0; i<ITERATIONS; i++)
+ {
+ if(!newfiles.empty())
+ std::cout << "Iteration " << i << ": Renaming " << ITEMS << " files and directories inside the " << ITEMS << " constantly changing directories ..." << std::endl;
+ for(size_t n=0; n<ITEMS; n++)
+ {
+ if(!newfiles.empty())
+ {
+ // Relink previous new file into first directory
+ //std::cout << "Renaming " << newfiles[n].get()->path() << std::endl;
+ newfiles[n]->atomic_relink(path_req::relative(dirh, to_string(n)+"_"+to_string(i)));
+ // Note that on FreeBSD if this is a file its path would be now be incorrect and moreover lost due to lack of
+ // path enumeration support for files. As we throw away the handle, this doesn't show up here.
+
+ // Have the file creation depend on the previous file creation
+ dirreqs[n].precondition=dispatcher->depends(newfiles[n], dirs[n]);
+ }
+ dirreqs[n].path=to_string(i);
+ }
+ // Split into two
+ std::vector<path_req> front(dirreqs.begin(), dirreqs.begin()+ITEMS/2), back(dirreqs.begin()+ITEMS/2, dirreqs.end());
+ std::cout << "Iteration " << i << ": Creating " << ITEMS << " files and directories inside the " << ITEMS << " constantly changing directories ..." << std::endl;
+#ifdef __FreeBSD__ // FreeBSD can only track directories not files when their parent directories change
+ newfiles=dispatcher->dir(front);
+#else
+ newfiles=dispatcher->file(front);
+#endif
+ auto newfiles2=dispatcher->dir(back);
+ newfiles.insert(newfiles.end(), std::make_move_iterator(newfiles2.begin()), std::make_move_iterator(newfiles2.end()));
+
+ // Pace the scheduling, else we slow things down a ton. Also retrieve and throw any errors.
+ when_all_p(newfiles).get();
+ }
+ // Wait around for all that to process
+ do
+ {
+ this_thread::sleep_for(chrono::seconds(1));
+ } while(dispatcher->wait_queue_depth());
+ // Close all handles opened during this context except for dirh
+ }
+ catch(const system_error &e) { std::cerr << "ERROR: test exits via system_error code " << e.code().value() << "(" << e.what() << ")" << std::endl; BOOST_REQUIRE(false); }
+ catch(const std::exception &e) { std::cerr << "ERROR: test exits via exception (" << e.what() << ")" << std::endl; BOOST_REQUIRE(false); }
+ catch(...) { std::cerr << "ERROR: test exits via unknown exception" << std::endl; BOOST_REQUIRE(false); }
+
+ // Check that everything is as it ought to be
+ auto _contents = dispatcher->enumerate(enumerate_req(dirh, metadata_flags::All, 10*ITEMS*ITERATIONS)).get().first;
+ testdir=future<>(); // Kick out AFIO now so NTFS has itself cleaned up by the end of the checks
+ dirh=future<>();
+ dispatcher.reset();
+ std::cout << "Checking that we successfully renamed " << (ITEMS*(ITERATIONS-1)+1) << " items into the same directory ..." << std::endl;
+ BOOST_CHECK(_contents.size() == (ITEMS*(ITERATIONS-1)+1));
+ std::set<BOOST_AFIO_V2_NAMESPACE::filesystem::path> contents;
+ for(auto &i : _contents)
+ contents.insert(i.name());
+ BOOST_CHECK(contents.size() == (ITEMS*(ITERATIONS-1)+1));
+ for(size_t i=1; i<ITERATIONS; i++)
+ {
+ for(size_t n=0; n<ITEMS; n++)
+ {
+ if(contents.count(to_string(n)+"_"+to_string(i))==0)
+ std::cerr << to_string(n)+"_"+to_string(i) << std::endl;
+ BOOST_CHECK(contents.count(to_string(n)+"_"+to_string(i))>0);
+ }
+ }
+ filesystem::remove_all("testdir");
+ }
+ catch(...)
+ {
+ filesystem::remove_all("testdir");
+ throw;
+ }
+//]
+}
diff --git a/attic/test/tsan.supp b/attic/test/tsan.supp
new file mode 100644
index 00000000..57f20d5d
--- /dev/null
+++ b/attic/test/tsan.supp
@@ -0,0 +1,33 @@
+# These are from GCC
+##race:boost::detail::future_object_base::~future_object_base()
+
+# These are from clang
+##fun:*pthread_mutex_destroy*
+##src:*boost/smart_ptr/scoped_ptr.hpp
+##fun:*scoped_ptr*
+##src:*boost/smart_ptr/detail/sp_counted_impl.hpp
+##fun:*sp_counted_base*
+##src:*boost/exception/detail/exception_ptr.hpp
+##fun:*current_exception_std_exception_wrapper*
+##src:*boost/exception/exception.hpp
+##fun:*refcount_ptr*
+##fun:*exception_detail*
+##src:boost/smart_ptr/detail/shared_count.hpp
+##fun:*shared_count*
+
+# Stuff from libstdc++ not understood by tsan
+race:include/c++/*/bits/shared_ptr_base.h
+race:std::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_add_ref_lock()
+race:std::string::_Rep::_M_refdata()
+
+# Stuff from Boost not understood by tsan
+race:boost/exception/detail/exception_ptr.hpp
+race:boost/exception/exception.hpp
+race:void std::swap<boost::exception_detail::clone_base const*>
+race:boost/smart_ptr/detail/shared_count.hpp
+race:boost::detail::shared_count::swap(boost::detail::shared_count&)
+race:boost::detail::shared_count::~shared_count()
+
+# Explicitly known thread unsafe code in test_functions.hpp (false positive)
+race:boost::afio::v1_std_boost_boost::async_file_io_dispatcher_base::post_op_filter_clear()
+race:boost::afio::v1_std_boost_boost::async_file_io_dispatcher_base::post_op_filter(std::vector<std::pair<boost::afio::v1_std_boost_boost::detail::OpType, std::function<void (boost::afio::v1_std_boost_boost::detail::OpType, boost::afio::v1_std_boost_boost::async_io_op&)> >, std::allocator<std::pair<boost::afio::v1_std_boost_boost::detail::OpType, std::function<void (boost::afio::v1_std_boost_boost::detail::OpType, boost::afio::v1_std_boost_boost::async_io_op&)> > > >)
diff --git a/attic/test/unittests.vcxproj b/attic/test/unittests.vcxproj
new file mode 100644
index 00000000..ad63d2d3
--- /dev/null
+++ b/attic/test/unittests.vcxproj
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{4324A554-FA77-4276-A4A0-657E9849D1C3}</ProjectGuid>
+ <Keyword>MakeFileProj</Keyword>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v120</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <NMakeOutput>unittests.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>..\..\..\b2 --fast-build --running-in-ci --link-test link=static</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>..\..\..\b2 -a</NMakeReBuildCommandLine>
+ <IncludePath>C:\Users\Project Kyo\Documents\GitHub\boost;$(IncludePath)</IncludePath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <NMakeOutput>unittests.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>..\..\..\b2 toolset=msvc-13.0 address-model=64 debug-symbols=on link=static --link-test -j 4 release</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>..\..\..\b2 toolset=msvc-13.0 address-model=64 debug-symbols=on link=static --link-test -j 4 -a</NMakeReBuildCommandLine>
+ <IncludePath>$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);..\..\..</IncludePath>
+ <ExecutablePath>$(VCInstallDir)bin;$(WindowsSdkDir)bin\NETFX 4.0 Tools;$(WindowsSdkDir)bin;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;$(ProgramFiles)\HTML Help Workshop;$(FrameworkSDKDir)\bin;$(MSBuildToolsPath32);$(VSInstallDir);$(SystemRoot)\SysWow64;$(FxCopDir);$(PATH);</ExecutablePath>
+ <LibraryPath>$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib</LibraryPath>
+ <NMakeCleanCommandLine>..\..\..\b2 toolset=msvc-13.0 address-model=64 debug-symbols=on link=static --link-test -j 4 --clean</NMakeCleanCommandLine>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <NMakeOutput>unittests.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;NDEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>..\..\..\b2 release</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>..\..\..\b2 release -a</NMakeReBuildCommandLine>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <NMakeOutput>unittests.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;NDEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>..\..\..\b2 address-model=64 link=static --test=async_io_barrier_test.cpp release</NMakeBuildCommandLine>
+ <NMakeReBuildCommandLine>..\..\..\b2 address-model=64 link=static release -a</NMakeReBuildCommandLine>
+ </PropertyGroup>
+ <ItemDefinitionGroup>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="main.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\build\afio.vcxproj">
+ <Project>{31a5e53d-7416-4e1d-891a-1c69ef8553bd}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/attic/test/update_coveralls.sh b/attic/test/update_coveralls.sh
new file mode 100755
index 00000000..b9be886a
--- /dev/null
+++ b/attic/test/update_coveralls.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Adapted from https://github.com/purpleKarrot/Karrot/blob/develop/test/coveralls.in
+# which itself was adapted from https://github.com/berenm/cmake-extra/blob/master/coveralls-upload.in
+
+if [ 0 -eq $(find -iname "*.gcda" | wc -l) ]
+then
+ exit 0
+fi
+
+gcov-4.8 --source-prefix $1 --preserve-paths --relative-only $(find -iname "*.gcda") 1>/dev/null || exit 0
+
+cat >coverage.json <<EOF
+{
+ "service_name": "travis-ci",
+ "service_job_id": "${TRAVIS_JOB_ID}",
+ "run_at": "$(date --iso-8601=s)",
+ "source_files": [
+EOF
+
+for file in $(find * -iname '*.gcov' -print | egrep '.*' | egrep -v 'valgrind|SpookyV2|bindlib|test')
+do
+ FILEPATH=$(echo ${file} | sed -re 's%#%\/%g; s%.gcov$%%')
+ echo Reporting coverage for $FILEPATH ...
+ cat >>coverage.json <<EOF
+ {
+ "name": "$FILEPATH",
+ "source": $(cat $FILEPATH | python test/json_encode.py),
+ "coverage": [$(tail -n +3 ${file} | cut -d ':' -f 1 | sed -re 's%^ +%%g; s%-%null%g; s%^[#=]+$%0%;' | tr $'\n' ',' | sed -re 's%,$%%')]
+ },
+EOF
+done
+
+#cat coverage.json
+mv coverage.json coverage.json.tmp
+cat >coverage.json <(head -n -1 coverage.json.tmp) <(echo -e " }\n ]\n}")
+rm *.gcov coverage.json.tmp
+
+#head coverage.json
+#echo
+curl -F json_file=@coverage.json https://coveralls.io/api/v1/jobs
+#head coverage.json