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
diff options
context:
space:
mode:
-rw-r--r--.clang-format57
-rw-r--r--.gitattributes97
-rw-r--r--.gitmodules16
-rw-r--r--Doxyfile2406
-rw-r--r--Readme.md62
-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
-rw-r--r--fs_probe/benchmark_locking.cpp108
-rw-r--r--fs_probe/fs_probe.cpp166
-rw-r--r--fs_probe/fs_probe.vcxproj241
-rw-r--r--fs_probe/fs_probe.vcxproj.filters116
-rw-r--r--fs_probe/fs_probe_results.yaml364
-rw-r--r--fs_probe/withmsvc.bat7
-rwxr-xr-xfs_probe/withposix.sh20
-rw-r--r--include/boost/afio.hpp1
-rw-r--r--include/boost/afio/afio.hpp1
m---------include/boost/afio/bindlib0
m---------include/boost/afio/gsl-lite0
m---------include/boost/afio/outcome0
-rw-r--r--include/boost/afio/v1/afio.hpp1
m---------include/boost/afio/v1/branch0
-rw-r--r--include/boost/afio/v2/afio.hpp3
-rw-r--r--include/boost/afio/v2/async_file_handle.hpp248
-rw-r--r--include/boost/afio/v2/config.hpp516
-rw-r--r--include/boost/afio/v2/deadline.h99
-rw-r--r--include/boost/afio/v2/detail/child_process.hpp117
-rw-r--r--include/boost/afio/v2/detail/impl/posix/child_process.ipp49
-rw-r--r--include/boost/afio/v2/detail/impl/posix/handle.ipp422
-rw-r--r--include/boost/afio/v2/detail/impl/posix/io_service.ipp349
-rw-r--r--include/boost/afio/v2/detail/impl/posix/statfs.ipp164
-rw-r--r--include/boost/afio/v2/detail/impl/posix/storage_profile.ipp300
-rw-r--r--include/boost/afio/v2/detail/impl/posix/utils.ipp166
-rw-r--r--include/boost/afio/v2/detail/impl/storage_profile.ipp584
-rw-r--r--include/boost/afio/v2/detail/impl/windows/async_file_handle.ipp235
-rw-r--r--include/boost/afio/v2/detail/impl/windows/child_process.ipp190
-rw-r--r--include/boost/afio/v2/detail/impl/windows/file_handle.ipp96
-rw-r--r--include/boost/afio/v2/detail/impl/windows/handle.ipp185
-rw-r--r--include/boost/afio/v2/detail/impl/windows/import.hpp1031
-rw-r--r--include/boost/afio/v2/detail/impl/windows/io_service.ipp89
-rw-r--r--include/boost/afio/v2/detail/impl/windows/statfs.ipp183
-rw-r--r--include/boost/afio/v2/detail/impl/windows/storage_profile.ipp374
-rw-r--r--include/boost/afio/v2/detail/impl/windows/utils.ipp126
-rw-r--r--include/boost/afio/v2/file_handle.hpp152
-rw-r--r--include/boost/afio/v2/handle.hpp370
-rw-r--r--include/boost/afio/v2/io_service.hpp247
-rw-r--r--include/boost/afio/v2/lockable_handle.hpp29
-rw-r--r--include/boost/afio/v2/native_handle_type.hpp139
-rw-r--r--include/boost/afio/v2/statfs.hpp123
-rw-r--r--include/boost/afio/v2/storage_profile.hpp294
-rw-r--r--include/boost/afio/v2/utils.hpp629
-rw-r--r--meta/libraries.json16
-rw-r--r--reference/11.1.pdfbin0 -> 971081 bytes
-rw-r--r--reference/54d0f0190cf29ca811040c8a.pdfbin0 -> 364048 bytes
-rw-r--r--reference/Chidambaram.pdfbin0 -> 563249 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct.html2434
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-analysis-cdf.gifbin0 -> 6451 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-method-edp.gifbin0 -> 7633 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-result-big-legend.gifbin0 -> 12226 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-result-big.gifbin0 -> 40604 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-result-small-ext3.gifbin0 -> 18507 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-result-small-hfsplus.gifbin0 -> 19125 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-result-small-jfs.gifbin0 -> 20197 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-result-small-nfs.gifbin0 -> 22061 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-result-small-reiserfs.gifbin0 -> 23507 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-result-small-xfs.gifbin0 -> 96047 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/fig-result-zoom.gifbin0 -> 5942 bytes
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/main.css210
-rw-r--r--reference/Error Handling is Ocassionally Correct_files/new_usenix.jpgbin0 -> 20617 bytes
-rw-r--r--reference/Files are hard.html484
-rw-r--r--reference/Files are hard_files/fs_properties.pngbin0 -> 34362 bytes
-rw-r--r--reference/Files are hard_files/program_bugs.pngbin0 -> 85886 bytes
-rw-r--r--reference/Linux KAIO/History of Linux KAIO API.pdfbin0 -> 145418 bytes
-rw-r--r--reference/Linux KAIO/KAIOUserGuide.htm733
-rw-r--r--reference/Linux KAIO/linux-kaio.txt552
-rw-r--r--reference/iron-sosp05.pdfbin0 -> 277604 bytes
-rw-r--r--reference/osdi14-paper-pillai.pdfbin0 -> 453389 bytes
-rw-r--r--release_notes.md40
-rw-r--r--scripts/AddTryItNow.py29
-rwxr-xr-xscripts/BuildTimes.py87
-rwxr-xr-xscripts/GenJenkinsMatrixDashboard.py96
-rwxr-xr-xscripts/JenkinsMatrixToDashboard.py83
-rwxr-xr-xscripts/SoakTest.py16
-rwxr-xr-xscripts/XMLTidy.py28
-rwxr-xr-xscripts/readme_to_html.sh8
-rw-r--r--scripts/travis_lldb.expect10
-rw-r--r--scripts/xhtml_to_docbook.xsl537
495 files changed, 63853 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..6ac52227
--- /dev/null
+++ b/.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: 320
+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/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..ee812ad4
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,97 @@
+* text=auto !eol svneol=native#text/plain
+*.gitattributes text svneol=native#text/plain
+
+# Scriptish formats
+*.bat text svneol=native#text/plain
+*.bsh text svneol=native#text/x-beanshell
+*.cgi text svneol=native#text/plain
+*.cmd text svneol=native#text/plain
+*.js text svneol=native#text/javascript
+*.php text svneol=native#text/x-php
+*.pl text svneol=native#text/x-perl
+*.pm text svneol=native#text/x-perl
+*.py text svneol=native#text/x-python
+*.sh eol=lf svneol=LF#text/x-sh
+configure eol=lf svneol=LF#text/x-sh
+
+# Image formats
+*.bmp binary svneol=unset#image/bmp
+*.gif binary svneol=unset#image/gif
+*.ico binary svneol=unset#image/ico
+*.jpeg binary svneol=unset#image/jpeg
+*.jpg binary svneol=unset#image/jpeg
+*.png binary svneol=unset#image/png
+*.tif binary svneol=unset#image/tiff
+*.tiff binary svneol=unset#image/tiff
+*.svg text svneol=native#image/svg%2Bxml
+
+# Data formats
+*.pdf binary svneol=unset#application/pdf
+*.avi binary svneol=unset#video/avi
+*.doc binary svneol=unset#application/msword
+*.dsp text svneol=crlf#text/plain
+*.dsw text svneol=crlf#text/plain
+*.eps binary svneol=unset#application/postscript
+*.json text svneol=native#application/json
+*.gz binary svneol=unset#application/gzip
+*.mov binary svneol=unset#video/quicktime
+*.mp3 binary svneol=unset#audio/mpeg
+*.ppt binary svneol=unset#application/vnd.ms-powerpoint
+*.ps binary svneol=unset#application/postscript
+*.psd binary svneol=unset#application/photoshop
+*.rdf binary svneol=unset#text/rdf
+*.rss text svneol=unset#text/xml
+*.rtf binary svneol=unset#text/rtf
+*.sln text svneol=native#text/plain
+*.swf binary svneol=unset#application/x-shockwave-flash
+*.tgz binary svneol=unset#application/gzip
+*.vcproj text svneol=native#text/xml
+*.vcxproj text svneol=native#text/xml
+*.vsprops text svneol=native#text/xml
+*.wav binary svneol=unset#audio/wav
+*.xls binary svneol=unset#application/vnd.ms-excel
+*.zip binary svneol=unset#application/zip
+
+# Text formats
+.htaccess text svneol=native#text/plain
+*.bbk text svneol=native#text/xml
+*.cmake text svneol=native#text/plain
+*.css text svneol=native#text/css
+*.dtd text svneol=native#text/xml
+*.htm text svneol=native#text/html
+*.html text svneol=native#text/html
+*.ini text svneol=native#text/plain
+*.log text svneol=native#text/plain
+*.mak text svneol=native#text/plain
+*.qbk text svneol=native#text/plain
+*.rst text svneol=native#text/plain
+*.sql text svneol=native#text/x-sql
+*.txt text svneol=native#text/plain
+*.xhtml text svneol=native#text/xhtml%2Bxml
+*.xml text svneol=native#text/xml
+*.xsd text svneol=native#text/xml
+*.xsl text svneol=native#text/xml
+*.xslt text svneol=native#text/xml
+*.xul text svneol=native#text/xul
+*.yml text svneol=native#text/plain
+boost-no-inspect text svneol=native#text/plain
+CHANGES text svneol=native#text/plain
+COPYING text svneol=native#text/plain
+INSTALL text svneol=native#text/plain
+Jamfile text svneol=native#text/plain
+Jamroot text svneol=native#text/plain
+Jamfile.v2 text svneol=native#text/plain
+Jamrules text svneol=native#text/plain
+Makefile* text svneol=native#text/plain
+README text svneol=native#text/plain
+TODO text svneol=native#text/plain
+
+# Code formats
+*.c text svneol=native#text/plain
+*.cpp text svneol=native#text/plain
+*.h text svneol=native#text/plain
+*.hpp text svneol=native#text/plain
+*.ipp text svneol=native#text/plain
+*.tpp text svneol=native#text/plain
+*.jam text svneol=native#text/plain
+*.java text svneol=native#text/plain
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..e01bdcfd
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,16 @@
+[submodule "include/boost/afio/bindlib"]
+ path = include/boost/afio/bindlib
+ url = https://github.com/ned14/Boost.BindLib.git
+ branch = master
+[submodule "include/boost/afio/v1/branch"]
+ path = include/boost/afio/v1/branch
+ url = https://github.com/BoostGSoC13/boost.afio.git
+ branch = v1.3_Boost_v1.58_branch
+[submodule "include/boost/afio/outcome"]
+ path = include/boost/afio/outcome
+ url = https://github.com/ned14/boost.outcome.git
+ branch = master
+[submodule "include/boost/afio/gsl-lite"]
+ path = include/boost/afio/gsl-lite
+ url = https://github.com/martinmoene/gsl-lite.git
+ branch = master
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 00000000..5e8a7b2f
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,2406 @@
+# Doxyfile 1.8.10
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all text
+# before the first occurrence of this tag. Doxygen uses libiconv (or the iconv
+# built into libc) for the transcoding. See http://www.gnu.org/software/libiconv
+# for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME = "Boost.AFIO"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER = "v2.00 early alpha"
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF =
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY =
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB = YES
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE = 2
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines.
+
+ALIASES = "errors=@par Errors returnable\n" "mallocs=@par Memory Allocations\n"
+
+# This tag can be used to specify a number of word-keyword mappings (TCL only).
+# A mapping has the form "name=value". For example adding "class=itcl::class"
+# will allow you to use the command class in the itcl::class meaning.
+
+TCL_SUBST =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, Javascript,
+# C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran:
+# FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran:
+# Fortran. In the later case the parser tries to guess whether the code is fixed
+# or free formatted code, this is the default for Fortran type files), VHDL. For
+# instance to make doxygen treat .inc files as Fortran files (default is PHP),
+# and .f files as C (default is Fortran), use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See http://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT = YES
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT = YES
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# (class|struct|union) declarations. If set to NO, these declarations will be
+# included in the documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES = NO
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME = NO
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC = NO
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT = include/boost/afio/v2 release_notes.md
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: http://www.gnu.org/software/libiconv) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.f90, *.f, *.for, *.tcl, *.vhd,
+# *.vhdl, *.ucf, *.qsf, *.as and *.js.
+
+FILE_PATTERNS =
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS = detail::* *::detail::*
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH =
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+
+INPUT_FILTER =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = release_notes.md
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES = YES
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# function all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see http://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the config file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# compiled with the --with-libclang option.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX = YES
+
+# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in
+# which the alphabetical index list will be split.
+# Minimum value: 1, maximum value: 20, default value: 5.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+COLS_IN_ALPHA_INDEX = 5
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET =
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# http://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: http://developer.apple.com/tools/xcode/), introduced with
+# OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html
+# for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW = YES
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW = NO
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE = 10
+
+# Use the FORMULA_TRANPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT = YES
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# http://www.mathjax.org) which uses client side Javascript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from http://www.mathjax.org before deployment.
+# The default value is: http://cdn.mathjax.org/mathjax/latest.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using Javascript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: http://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when enabling USE_PDFLATEX this option is only used for generating
+# bitmaps for formulas in the HTML output, but not in the Makefile that is
+# written to the output directory.
+# The default file is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# http://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE = plain
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's config
+# file, i.e. a series of assignments. You only have to provide replacements,
+# missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's config file. A template extensions file can be generated
+# using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT = xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sf.net) file that captures the
+# structure of the code including all documentation. Note that this feature is
+# still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION = YES
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF = YES
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED = __cplusplus=201402L DOXYGEN_SHOULD_SKIP_THIS=1
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED = BOOST_AFIO_DEADLINE_NAME BOOST_CXX14_CONSTEXPR \
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC \
+ BOOST_AFIO_BITFIELD_BEGIN BOOST_AFIO_BITFIELD_END \
+ BOOST_AFIO_V2_NAMESPACE BOOST_AFIO_V2_NAMESPACE_BEGIN BOOST_AFIO_V2_NAMESPACE_END
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of 'which perl').
+# The default file (with absolute path) is: /usr/bin/perl.
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS = YES
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see:
+# http://www.mcternan.me.uk/mscgen/)) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP = YES
diff --git a/Readme.md b/Readme.md
new file mode 100644
index 00000000..6d7415c5
--- /dev/null
+++ b/Readme.md
@@ -0,0 +1,62 @@
+This is the beginnings of the post-peer-review AFIO
+v2 rewrite. You can view its documentation at https://ned14.github.io/boost.afio/
+
+Todo:
+- [ ] Somehow implement make_errored_result(errcode, extended_msg)
+- [ ] Move caching into native_handle_type.
+- [ ] Implement [[bindlib::make_free]] which injects member functions into the enclosing
+namespace.
+- [ ] Add macro helpers to Outcome for returning outcomes out of things
+which cannot return values like constructors, and convert said exceptions/TLS
+back into outcomes.
+ - Make use of std::system_error(errno, system_category, "custom error message");
+- [ ] Get Outcome to work perfectly with exceptions and RTTI disabled, this makes
+Outcome useful in the games/audio world.
+ - [ ] Where Outcome might throw, do macro based action which could be any of:
+ - Throw an exception
+ - Call a deliberately undefined function to create a link error (release) or
+assert & trap (debug)
+ - Some user based action e.g. fatal exit
+ - [ ] Add unit tests proving it for all platforms.
+ - [ ] Move AFIO to being tested with exceptions and RTTI disabled.
+- [ ] There is much duplicate and sloppy code in AFIO v2. Reduce and eliminate.
+
+- [ ] C bindings for all AFIO v2 APIs. Write libclang parser which autogenerates
+SWIG interface files from the .hpp files.
+
+- [ ] Add native BSD kqueues to POSIX AIO backend as is vastly more efficient.
+ - http://www.informit.com/articles/article.aspx?p=607373&seqNum=4 is a
+very useful programming guide for POSIX AIO.
+- [ ] Port to Linux KAIO
+ - http://linux.die.net/man/2/io_getevents would be in the run() loop.
+pthread_sigqueue() can be used by post() to cause aio_suspend() to break
+early to run user supplied functions.
+- [ ] Add to docs for every API the number of malloc + free performed.
+ - Unit test op codes generated per set of i/o calls
+- [ ] Don't run the cpu and sys tests if cpu and sys ids already in fs_probe_results.yaml
+ - Need to uniquely fingerprint a machine somehow?
+- [ ] Fatter afio::path. We probably need to allow relative paths
+based on a handle and fragment in afio::path, therefore might as well encapsulate
+NT kernel vs win32 paths in there too.
+- [ ] Add monitoring of CPU usage to tests. See GetThreadTimes. Make sure
+worker thread times are added into results.
+- [ ] Configurable tracking of op latency and throughput (bytes) for all
+handles on some storage i.e. storage needs to be kept in a global map.
+ - Something which strongly resembles the memory bandwidth test
+ - [ ] Should have decile bucketing e.g. percentage in bottom 10%, percentage
+ in next 10% etc. Plus mean and stddev.
+ - [ ] Should either be resettable or subtractable i.e. points can be diffed.
+ - [ ] Add IOPS QD=1..N storage profile test
+ - [ ] Add throughput storage profile test
+- [ ] Output into YAML comparable hashes for OS + device + FS + flags
+so we can merge partial results for some combo into the results database.
+- [ ] Write YAML parsing tool which merges fs_probe_results.yaml into
+the results directory where flags and OS get its own directory and each YAML file
+is named FS + device e.g.
+ - results/win64 direct=1 sync=0/NTFS + WDC WD30EFRX-68EUZN0
+- [ ] virtual handle::path_type handle::path(bool refresh=false) should be added using
+GetFinalPathNameByHandle(FILE_NAME_OPENED). VOLUME_NAME_DOS vs VOLUME_NAME_NT should
+depend on the current afio::path setting.
+- [ ] directory_handle
+- [ ] symlink_handle
+- [ ] Missing functions on handle/file_handle from AFIO v1
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
diff --git a/fs_probe/benchmark_locking.cpp b/fs_probe/benchmark_locking.cpp
new file mode 100644
index 00000000..b50ced53
--- /dev/null
+++ b/fs_probe/benchmark_locking.cpp
@@ -0,0 +1,108 @@
+/* benchmark_locking.cpp
+Test the performance of various file locking mechanisms
+(C) 2016 Niall Douglas http://www.nedprod.com/
+File Created: Mar 2016
+
+
+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.
+*/
+
+#define _CRT_SECURE_NO_WARNINGS 1
+
+#include "include/detail/child_process.hpp"
+#include "include/file_handle.hpp"
+
+namespace afio = BOOST_AFIO_V2_NAMESPACE;
+
+namespace append_only_mutual_exclusion
+{
+ union alignas(16) uint128 {
+ unsigned char bytes[16];
+#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+ // Strongly hint to the compiler what to do here
+ __m128i sse;
+#endif
+ };
+ using uint64 = unsigned long long;
+ struct header
+ {
+ uint128 hash;
+ uint64 unique_id;
+ uint64 time_t_offset;
+ uint64 first_valid_lock_request;
+ uint64 end_last_hole_punch;
+ char _padding[128 - 48];
+ };
+ static_assert(sizeof(header) == 128, "header is not 128 bytes long!");
+ struct lock_request
+ {
+ uint128 hash;
+ uint64 unique_id;
+ uint64 us_timestamp : 56;
+ uint64 want_to_lock_items : 8;
+ uint128 want_to_lock[6];
+ };
+ static_assert(sizeof(lock_request) == 128, "lock_request is not 128 bytes long!");
+}
+
+int main(int argc, char *argv[])
+{
+ if(argc < 2)
+ {
+ std::cerr << "Usage: " << argv[0] << " <no of waiters>" << std::endl;
+ return 1;
+ }
+ if(strcmp(argv[1], "spawned"))
+ {
+ size_t waiters = atoi(argv[1]);
+ if(!waiters)
+ {
+ std::cerr << "Usage: " << argv[0] << " <no of waiters>" << std::endl;
+ return 1;
+ }
+ std::vector<afio::detail::child_process> children;
+ auto mypath = afio::detail::current_process_path();
+#ifdef WIN32
+ std::vector<afio::stl1z::filesystem::path::string_type> args;
+ args.push_back(L"spawned");
+#else
+ std::vector<afio::stl1z::filesystem::path::string_type> args;
+ args.push_back("spawned");
+#endif
+ auto env = afio::detail::current_process_env();
+ for(size_t n = 0; n < waiters; n++)
+ {
+ auto child = afio::detail::child_process::launch(mypath, args, env);
+ if(child.has_error())
+ {
+ std::cerr << "FATAL: Child " << n << " could not be launched due to " << child.get_error().message() << std::endl;
+ return 1;
+ }
+ children.push_back(std::move(child.get()));
+ }
+ }
+ // todo
+ return 0;
+}
diff --git a/fs_probe/fs_probe.cpp b/fs_probe/fs_probe.cpp
new file mode 100644
index 00000000..7a704fcb
--- /dev/null
+++ b/fs_probe/fs_probe.cpp
@@ -0,0 +1,166 @@
+/* fs_probe.cpp
+Probes the OS and filing system for various characteristics
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Nov 2015
+
+
+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.
+*/
+
+#define _CRT_SECURE_NO_WARNINGS 1
+
+#include "include/async_file_handle.hpp"
+#include "include/storage_profile.hpp"
+
+#include <fstream>
+#include <iomanip>
+
+using namespace BOOST_AFIO_V2_NAMESPACE;
+#ifdef __linux__
+#define file_handle BOOST_AFIO_V2_NAMESPACE::file_handle
+#endif
+
+constexpr unsigned permute_flags_max = 4;
+static const std::regex sp_preamble{"(system|storage).*"};
+
+static storage_profile::storage_profile profile[permute_flags_max];
+
+#define RETCHECK(expr) \
+ { \
+ auto ret = (expr); \
+ if(ret.has_error()) \
+ { \
+ std::cerr << "WARNING: Operation " #expr " failed due to '" << ret.get_error().message() << "'" << std::endl; \
+ abort(); \
+ } \
+ }
+
+int main(int argc, char *argv[])
+{
+ std::regex torun(".*");
+ bool regexvalid = false;
+ unsigned torunflags = permute_flags_max - 1;
+ if(argc > 1)
+ {
+ try
+ {
+ torun.assign(argv[1]);
+ regexvalid = true;
+ }
+ catch(...)
+ {
+ }
+ if(argc > 2)
+ torunflags = atoi(argv[2]);
+ if(!regexvalid)
+ {
+ std::cerr << "Usage: " << argv[0] << " <regex for tests to run> [<flags>]" << std::endl;
+ return 1;
+ }
+ }
+
+ // Force extent allocation before test begins
+ {
+ // Create file with O_SYNC
+ auto _testfile(file_handle::file("test", handle::mode::write, handle::creation::if_needed, handle::caching::reads));
+ if(!_testfile)
+ {
+ std::cerr << "WARNING: Failed to create test file due to '" << _testfile.get_error().message() << "', failing" << std::endl;
+ return 1;
+ }
+ file_handle testfile(std::move(_testfile.get()));
+ std::vector<char> buffer(1024 * 1024);
+ RETCHECK(testfile.truncate(buffer.size()));
+ file_handle::const_buffer_type _reqs[1] = {std::make_pair(buffer.data(), buffer.size())};
+ file_handle::io_request<file_handle::const_buffers_type> reqs(_reqs, 0);
+ RETCHECK(testfile.write(reqs));
+ }
+ // File closes, as it was opened with O_SYNC it forces extent allocation
+ // Pause as Windows still takes a while
+ stl11::this_thread::sleep_for(stl11::chrono::seconds(3));
+ std::ofstream results("fs_probe_results.yaml", std::ios::app);
+ {
+ auto put_time = [](const std::tm *tmb, const char *fmt) {
+ std::string buffer(256, 0);
+ buffer.resize(std::strftime((char *) buffer.data(), buffer.size(), fmt, tmb));
+ return buffer;
+ };
+ std::time_t t = std::time(nullptr);
+ results << "---\ntimestamp: " << put_time(std::gmtime(&t), "%F %T %z") << "\n";
+ }
+ bool first = true;
+ for(unsigned flags = 0; flags <= torunflags; flags++)
+ {
+ if(!flags || !!(flags & torunflags))
+ {
+ handle::caching strategy = handle::caching::all;
+ switch(flags)
+ {
+ case 1:
+ strategy = handle::caching::only_metadata; // O_DIRECT
+ break;
+ case 2:
+ strategy = handle::caching::reads; // O_SYNC
+ break;
+ case 3:
+ strategy = handle::caching::none; // O_DIRECT|O_SYNC
+ break;
+ }
+ std::cout << "\ndirect=" << !!(flags & 1) << " sync=" << !!(flags & 2) << ":\n";
+ auto _testfile(file_handle::file("test", handle::mode::write, handle::creation::open_existing, strategy));
+ if(!_testfile)
+ {
+ std::cerr << "WARNING: Failed to create test file due to '" << _testfile.get_error().message() << "', skipping" << std::endl;
+ continue;
+ }
+ file_handle testfile(std::move(_testfile.get()));
+ for(auto &test : profile[flags])
+ {
+ if(std::regex_match(test.name, torun))
+ {
+ std::cout << "Running test " << test.name << " ..." << std::endl;
+ auto result = test(profile[flags], testfile);
+ if(result)
+ {
+ test.invoke([](auto &i) { std::cout << " " << i.name << " = " << i.value << std::endl; });
+ }
+ else
+ std::cerr << " ERROR running test '" << test.name << "': " << result.get_error().message() << std::endl;
+ }
+ }
+ // Write out results for this combination of flags
+ if(first)
+ {
+ profile[flags].write(results, sp_preamble);
+ first = false;
+ }
+ results << "direct=" << !!(flags & 1) << " sync=" << !!(flags & 2) << ":\n";
+ profile[flags].write(results, sp_preamble, 4, true);
+ results.flush();
+ }
+ }
+
+ return 0;
+}
diff --git a/fs_probe/fs_probe.vcxproj b/fs_probe/fs_probe.vcxproj
new file mode 100644
index 00000000..443c04a3
--- /dev/null
+++ b/fs_probe/fs_probe.vcxproj
@@ -0,0 +1,241 @@
+<?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 clang|Win32">
+ <Configuration>Debug clang</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug clang|x64">
+ <Configuration>Debug clang</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="benchmark_locking.cpp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </ClCompile>
+ <ClCompile Include="fs_probe.cpp">
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">..\include;..\asio\asio\include</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'">..\include;..\asio\asio\include</AdditionalIncludeDirectories>
+ <AdditionalIncludeDirectories Condition="'$(Configuration)|$(Platform)'=='Release|x64'">..\include;..\asio\asio\include</AdditionalIncludeDirectories>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="include\async_file_handle.hpp" />
+ <ClInclude Include="include\config.hpp" />
+ <ClInclude Include="include\deadline.h" />
+ <ClInclude Include="include\detail\child_process.hpp" />
+ <ClInclude Include="include\detail\impl\windows\import.hpp" />
+ <ClInclude Include="include\file_handle.hpp" />
+ <ClInclude Include="include\handle.hpp" />
+ <ClInclude Include="include\io_service.hpp" />
+ <ClInclude Include="include\lockable_handle.hpp" />
+ <ClInclude Include="include\native_handle_type.hpp" />
+ <ClInclude Include="include\statfs.hpp" />
+ <ClInclude Include="include\storage_profile.hpp" />
+ <ClInclude Include="include\utils.hpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="include\detail\impl\posix\child_process.ipp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </None>
+ <None Include="include\detail\impl\posix\handle.ipp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </None>
+ <None Include="include\detail\impl\posix\io_service.ipp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </None>
+ <None Include="include\detail\impl\posix\statfs.ipp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </None>
+ <None Include="include\detail\impl\posix\storage_profile.ipp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </None>
+ <None Include="include\detail\impl\posix\utils.ipp">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </None>
+ <None Include="include\detail\impl\storage_profile.ipp" />
+ <None Include="include\detail\impl\windows\async_file_handle.ipp" />
+ <None Include="include\detail\impl\windows\child_process.ipp" />
+ <None Include="include\detail\impl\windows\file_handle.ipp" />
+ <None Include="include\detail\impl\windows\handle.ipp" />
+ <None Include="include\detail\impl\windows\io_service.ipp" />
+ <None Include="include\detail\impl\windows\statfs.ipp" />
+ <None Include="include\detail\impl\windows\storage_profile.ipp" />
+ <None Include="include\detail\impl\windows\utils.ipp" />
+ <None Include="Readme.md" />
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{4FCBD8AB-30CB-41C9-91C9-E74B9DEB1495}</ProjectGuid>
+ <Keyword>MakeFileProj</Keyword>
+ <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug clang|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Makefile</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <PlatformToolset>v140_Clang_3_7</PlatformToolset>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <PlatformToolset>v140</PlatformToolset>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Label="Shared">
+ </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 clang|Win32'" 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 Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug clang|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|x64'">
+ <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>Project1.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>withmsvc.bat</NMakeBuildCommandLine>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug clang|Win32'">
+ <NMakeOutput>Project1.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>withmsvc.bat</NMakeBuildCommandLine>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <NMakeOutput>Project1.exe</NMakeOutput>
+ <NMakePreprocessorDefinitions>WIN32;NDEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>
+ <NMakeBuildCommandLine>withmsvc.bat</NMakeBuildCommandLine>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <CodeAnalysisRuleSet>C:\Program Files (x86)\Microsoft Visual Studio 14.0\Team Tools\Static Analysis Tools\Rule Sets\NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ <RunCodeAnalysis>false</RunCodeAnalysis>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'">
+ <CodeAnalysisRuleSet>C:\Program Files (x86)\Microsoft Visual Studio 14.0\Team Tools\Static Analysis Tools\Rule Sets\NativeRecommendedRules.ruleset</CodeAnalysisRuleSet>
+ <RunCodeAnalysis>false</RunCodeAnalysis>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>UNICODE=1;WIN32=1;_UNICODE=1;_WIN32=1;AFIO_STANDALONE=1;ASIO_STANDALONE=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <WarningLevel>Level4</WarningLevel>
+ <DisableSpecificWarnings>4503;4201;4100;4127;4189;4459</DisableSpecificWarnings>
+ <EnablePREfast>false</EnablePREfast>
+ <AdditionalOptions>/analyze:stacksize 204800 /analyze- %(AdditionalOptions)</AdditionalOptions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug clang|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>UNICODE=1;WIN32=1;_UNICODE=1;_WIN32=1;AFIO_STANDALONE=1;ASIO_STANDALONE=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <WarningLevel>EnableAllWarnings</WarningLevel>
+ <DisableSpecificWarnings>4503;4201;4100;4127;4189;4459</DisableSpecificWarnings>
+ <EnablePREfast>false</EnablePREfast>
+ <StrictAliasing>true</StrictAliasing>
+ <ExceptionHandling>Enabled</ExceptionHandling>
+ <RuntimeTypeInfo>true</RuntimeTypeInfo>
+ <CppLanguageStandard>c++1y</CppLanguageStandard>
+ <AdditionalOptions>-Wno-unused-variable %(AdditionalOptions)</AdditionalOptions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <PreprocessorDefinitions>UNICODE=1;WIN32=1;_UNICODE=1;_WIN32=1;AFIO_STANDALONE=1;ASIO_STANDALONE=1</PreprocessorDefinitions>
+ <AdditionalIncludeDirectories>
+ </AdditionalIncludeDirectories>
+ <WarningLevel>Level4</WarningLevel>
+ <DisableSpecificWarnings>4503;4201;4100;4127;4189;4459</DisableSpecificWarnings>
+ <EnableEnhancedInstructionSet>NotSet</EnableEnhancedInstructionSet>
+ </ClCompile>
+ </ItemDefinitionGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/fs_probe/fs_probe.vcxproj.filters b/fs_probe/fs_probe.vcxproj.filters
new file mode 100644
index 00000000..57b5d0ad
--- /dev/null
+++ b/fs_probe/fs_probe.vcxproj.filters
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="fs_probe.cpp" />
+ <ClCompile Include="benchmark_locking.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="include">
+ <UniqueIdentifier>{eaf8c87d-0aa9-4405-9f57-f59ccbf10dc5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="src">
+ <UniqueIdentifier>{86fd8c6e-956b-41fd-b91f-779a84c30465}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="include\detail">
+ <UniqueIdentifier>{e6691a26-84c1-48fa-a204-7cdd745bd9d5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="include\detail\impl">
+ <UniqueIdentifier>{338a3cea-fb30-4109-b51f-42bd9b32d29a}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="include\detail\impl\windows">
+ <UniqueIdentifier>{b72d7aa6-ab5d-4d37-af22-7abb240b61ce}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="include\detail\impl\posix">
+ <UniqueIdentifier>{ba2460d4-f197-423a-a6b0-455bcdf2c3d3}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="include\storage_profile.hpp">
+ <Filter>include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\io_service.hpp">
+ <Filter>include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\config.hpp">
+ <Filter>include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\handle.hpp">
+ <Filter>include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\lockable_handle.hpp">
+ <Filter>include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\deadline.h">
+ <Filter>include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\utils.hpp">
+ <Filter>include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\statfs.hpp">
+ <Filter>include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\detail\impl\windows\import.hpp">
+ <Filter>include\detail\impl\windows</Filter>
+ </ClInclude>
+ <ClInclude Include="include\detail\child_process.hpp">
+ <Filter>include\detail</Filter>
+ </ClInclude>
+ <ClInclude Include="include\native_handle_type.hpp">
+ <Filter>include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\async_file_handle.hpp">
+ <Filter>include</Filter>
+ </ClInclude>
+ <ClInclude Include="include\file_handle.hpp">
+ <Filter>include</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="include\detail\impl\storage_profile.ipp">
+ <Filter>include\detail\impl</Filter>
+ </None>
+ <None Include="Readme.md" />
+ <None Include="include\detail\impl\windows\statfs.ipp">
+ <Filter>include\detail\impl\windows</Filter>
+ </None>
+ <None Include="include\detail\impl\windows\handle.ipp">
+ <Filter>include\detail\impl\windows</Filter>
+ </None>
+ <None Include="include\detail\impl\windows\io_service.ipp">
+ <Filter>include\detail\impl\windows</Filter>
+ </None>
+ <None Include="include\detail\impl\windows\storage_profile.ipp">
+ <Filter>include\detail\impl\windows</Filter>
+ </None>
+ <None Include="include\detail\impl\windows\utils.ipp">
+ <Filter>include\detail\impl\windows</Filter>
+ </None>
+ <None Include="include\detail\impl\posix\child_process.ipp">
+ <Filter>include\detail\impl\posix</Filter>
+ </None>
+ <None Include="include\detail\impl\posix\handle.ipp">
+ <Filter>include\detail\impl\posix</Filter>
+ </None>
+ <None Include="include\detail\impl\posix\io_service.ipp">
+ <Filter>include\detail\impl\posix</Filter>
+ </None>
+ <None Include="include\detail\impl\posix\statfs.ipp">
+ <Filter>include\detail\impl\posix</Filter>
+ </None>
+ <None Include="include\detail\impl\posix\storage_profile.ipp">
+ <Filter>include\detail\impl\posix</Filter>
+ </None>
+ <None Include="include\detail\impl\posix\utils.ipp">
+ <Filter>include\detail\impl\posix</Filter>
+ </None>
+ <None Include="include\detail\impl\windows\child_process.ipp">
+ <Filter>include\detail\impl\windows</Filter>
+ </None>
+ <None Include="include\detail\impl\windows\async_file_handle.ipp">
+ <Filter>include\detail\impl\windows</Filter>
+ </None>
+ <None Include="include\detail\impl\windows\file_handle.ipp">
+ <Filter>include\detail\impl\windows</Filter>
+ </None>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/fs_probe/fs_probe_results.yaml b/fs_probe/fs_probe_results.yaml
new file mode 100644
index 00000000..08a31945
--- /dev/null
+++ b/fs_probe/fs_probe_results.yaml
@@ -0,0 +1,364 @@
+---
+timestamp: 2016-01-12 09:53:59 -0000
+system:
+ os:
+ name: Microsoft Windows NT
+ ver: 10.0.10240
+ cpu:
+ name: GenuineIntel Intel(R) Core(TM) i7-3770K CPU @ 3.50GHz
+ architecture: x64
+ physical_cores: 4
+ mem:
+ # Main memory bandwidth when accessed sequentially
+ max_bandwidth: 21555367116
+ # Main memory bandwidth when 4Kb pages are accessed randomly
+ min_bandwidth: 17099338547
+ quantity: 17127100416
+ in_use: 0.605072
+storage:
+ controller:
+ kind: SATA
+ # The maximum number of bytes the disk controller can transfer at once
+ max_transfer: 131072
+ # The maximum number of scatter-gather buffers the disk controller can handle
+ max_buffers: 33
+ device:
+ name: Samsung SSD 850 EVO 1TB,EMT02B6Q
+ min_io_size: 512
+ size: 1000204886016
+ fs:
+ name: NTFS
+ config: todo
+ size: 744143671296
+ in_use: 0.854889
+direct=0 sync=0:
+ concurrency:
+ # The i/o write quantum guaranteed to be atomically visible to readers irrespective of write quantity
+ atomic_write_quantum: 1
+ # The maximum single aligned i/o write quantity atomically visible to readers
+ max_aligned_atomic_write: 1
+direct=1 sync=0:
+ concurrency:
+ # The i/o write quantum guaranteed to be atomically visible to readers irrespective of write quantity
+ atomic_write_quantum: 512
+ # The maximum single aligned i/o write quantity atomically visible to readers
+ max_aligned_atomic_write: 4096
+direct=0 sync=1:
+ concurrency:
+ # The i/o write quantum guaranteed to be atomically visible to readers irrespective of write quantity
+ atomic_write_quantum: 1
+ # The maximum single aligned i/o write quantity atomically visible to readers
+ max_aligned_atomic_write: 1
+direct=1 sync=1:
+ concurrency:
+ # The i/o write quantum guaranteed to be atomically visible to readers irrespective of write quantity
+ atomic_write_quantum: 512
+ # The maximum single aligned i/o write quantity atomically visible to readers
+ max_aligned_atomic_write: 4096
+---
+timestamp: 2016-01-12 08:50:32 -0000
+system:
+ os:
+ name: Microsoft Windows NT
+ ver: 10.0.10240
+ cpu:
+ name: GenuineIntel Intel(R) Core(TM) i5 CPU M 540 @ 2.53GHz
+ architecture: x64
+ physical_cores: 2
+ mem:
+ # Main memory bandwidth when accessed sequentially
+ max_bandwidth: 4992899481
+ # Main memory bandwidth when 4Kb pages are accessed randomly
+ min_bandwidth: 4670776934
+ quantity: 4217192448
+ in_use: 0.737706
+storage:
+ controller:
+ kind: RAID
+ # The maximum number of bytes the disk controller can transfer at once
+ max_transfer: 131072
+ # The maximum number of scatter-gather buffers the disk controller can handle
+ max_buffers: 33
+ device:
+ name: Samsung SSD 840 Series,DXT07B0Q
+ min_io_size: 512
+ size: 120034123776
+ fs:
+ name: NTFS
+ config: todo
+ size: 116417097728
+ in_use: 0.946461
+direct=0 sync=0:
+ concurrency:
+ # The i/o write quantum guaranteed to be atomically visible to readers irrespective of write quantity
+ atomic_write_quantum: 1
+ # The maximum single aligned i/o write quantity atomically visible to readers
+ max_aligned_atomic_write: 1
+direct=1 sync=0:
+ concurrency:
+ # The i/o write quantum guaranteed to be atomically visible to readers irrespective of write quantity
+ atomic_write_quantum: 512
+ # The maximum single aligned i/o write quantity atomically visible to readers
+ max_aligned_atomic_write: 4096
+direct=0 sync=1:
+ concurrency:
+ # The i/o write quantum guaranteed to be atomically visible to readers irrespective of write quantity
+ atomic_write_quantum: 1
+ # The maximum single aligned i/o write quantity atomically visible to readers
+ max_aligned_atomic_write: 1
+direct=1 sync=1:
+ concurrency:
+ # The i/o write quantum guaranteed to be atomically visible to readers irrespective of write quantity
+ atomic_write_quantum: 512
+ # The maximum single aligned i/o write quantity atomically visible to readers
+ max_aligned_atomic_write: 4096
+---
+timestamp: 2016-01-14 09:35:10 -0000
+system:
+ os:
+ name: Microsoft Windows NT
+ ver: 10.0.10240
+ cpu:
+ name: GenuineIntel Intel(R) Core(TM) i5 CPU M 540 @ 2.53GHz
+ architecture: x64
+ physical_cores: 2
+ mem:
+ # Main memory bandwidth when accessed sequentially
+ max_bandwidth: 4429185024
+ # Main memory bandwidth when 4Kb pages are accessed randomly
+ min_bandwidth: 4375497932
+ quantity: 4217192448
+ in_use: 0.829525
+storage:
+ controller:
+ kind: RAID
+ # The maximum number of bytes the disk controller can transfer at once
+ max_transfer: 131072
+ # The maximum number of scatter-gather buffers the disk controller can handle
+ max_buffers: 33
+ device:
+ name: Samsung SSD 840 Series,DXT07B0Q
+ min_io_size: 512
+ size: 120034123776
+ fs:
+ name: NTFS
+ config: todo
+ size: 116417097728
+ in_use: 0.953878
+direct=0 sync=0:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 1
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 1
+
+direct=1 sync=0:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 512
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 4096
+
+ # The multiple of offset in a file where update atomicity breaks, so if you
+ # wrote 4096 bytes at a 512 offset and this value was 4096, your write would
+ # tear at 3584 because all writes would tear on a 4096 offset multiple. Linux
+ # has a famously broken kernel i/o design which causes this value to be a page
+ # multiple, except on filing systems which take special measures to work around
+ # it. Windows NT appears to lose all atomicity as soon as an i/o straddles a
+ # 4096 file offset multiple and DMA suddenly goes into many 64 byte cache lines
+ # :(, so if this value is less than max_aligned_atomic_rewrite and some multiple
+ # of the CPU cache line size then this is what has happened.
+ atomic_rewrite_offset_boundary: 960
+
+direct=0 sync=1:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 1
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 1
+
+direct=1 sync=1:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 64
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 512
+---
+timestamp: 2016-02-06 18:54:23 +0000
+system:
+ os:
+ name: FreeBSD
+ ver: 10.2-RELEASE-p11
+ cpu:
+ name: GenuineIntel Intel(R) Core(TM) i5 CPU M 540 @ 2.53GHz
+ architecture: amd64
+ physical_cores: 2
+ mem:
+ # Main memory bandwidth when accessed sequentially
+ max_bandwidth: 375809638
+ # Main memory bandwidth when 4Kb pages are accessed randomly
+ min_bandwidth: 429496729
+ quantity: 2117103616
+ in_use: 0.262044
+storage:
+ device:
+ min_io_size: 131072
+ size: 34359738368
+ fs:
+ name: zfs
+ config: todo
+ size: 20207178752
+ in_use: 0.00916991
+direct=0 sync=0:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 1048576
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 1048576
+
+direct=1 sync=0:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 1048576
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 1048576
+
+direct=0 sync=1:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 1048576
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 1048576
+
+direct=1 sync=1:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 1048576
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 1048576
+---
+timestamp: 2016-02-07 16:23:11 +0000
+system:
+ os:
+ name: Linux
+ ver: 4.2.6-1-pve
+ cpu:
+ name: GenuineIntel Intel(R) Xeon(R) CPU E5504 @ 2.00GHz
+ architecture: x86_64
+ physical_cores: 8
+ mem:
+ # Main memory bandwidth when accessed sequentially
+ max_bandwidth: 456340275
+ # Main memory bandwidth when 4Kb pages are accessed randomly
+ min_bandwidth: 483183820
+ quantity: 16815341568
+ in_use: 0.197089
+storage:
+ device:
+ min_io_size: 4096
+ size: 2000398934016
+ fs:
+ name: ext4
+ config: todo
+ size: 32892682240
+ in_use: 0.102166
+direct=0 sync=0:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 1
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 1
+
+direct=1 sync=0:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 1048576
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 1048576
+
+direct=0 sync=1:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 1
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 1
+
+direct=1 sync=1:
+ concurrency:
+ # The i/o modify quantum guaranteed to be atomically visible to readers
+ # irrespective of rewrite quantity
+ atomic_rewrite_quantum: 1048576
+
+ # The maximum single aligned i/o modify quantity atomically visible to readers
+ # (can be [potentially unreliably] much larger than atomic_rewrite_quantum). A
+ # very common value on modern hardware with direct i/o thanks to PCIe DMA is
+ # 4096, don't trust values higher than this because of potentially discontiguous
+ # memory page mapping.
+ max_aligned_atomic_rewrite: 1048576
+
diff --git a/fs_probe/withmsvc.bat b/fs_probe/withmsvc.bat
new file mode 100644
index 00000000..ed0fa6c5
--- /dev/null
+++ b/fs_probe/withmsvc.bat
@@ -0,0 +1,7 @@
+cd ..
+if not exist asio git clone https://github.com/chriskohlhoff/asio.git
+cd asio
+git pull
+cd ..\fs_probe
+
+cl /Zi /EHsc /O2 /DNDEBUG /arch:SSE2 /MD /GF /GR /Gy /bigobj /wd4503 fs_probe.cpp /DUNICODE=1 /DWIN32=1 /D_UNICODE=1 /D_WIN32=1 /I../include /DAFIO_STANDALONE=1 /I../asio/asio/include /DASIO_STANDALONE=1
diff --git a/fs_probe/withposix.sh b/fs_probe/withposix.sh
new file mode 100755
index 00000000..855819ea
--- /dev/null
+++ b/fs_probe/withposix.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+if [ -z "$CXX" ]; then
+ if [ "$HOSTTYPE" = "FreeBSD" ]; then
+ CXX=clang++37
+ else
+ CXX=g++-4.9
+ fi
+fi
+HOSTOS=$(uname)
+if [ "$HOSTOS" = "Linux" ]; then
+ LIBATOMIC="-ldl -lrt"
+ 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
+rm -rf fs_probe
+$CXX -o fs_probe -g -O3 -DDEBUG -std=c++14 -rdynamic -fstrict-aliasing -Wstrict-aliasing -Wno-unused -fasynchronous-unwind-tables fs_probe.cpp -Iinclude -DBOOST_AFIO_RUNNING_IN_CI=1 -Wno-unused-value -lboost_filesystem -lboost_system -lpthread $LIBATOMIC
diff --git a/include/boost/afio.hpp b/include/boost/afio.hpp
new file mode 100644
index 00000000..a74367f3
--- /dev/null
+++ b/include/boost/afio.hpp
@@ -0,0 +1 @@
+#include "afio/afio.hpp"
diff --git a/include/boost/afio/afio.hpp b/include/boost/afio/afio.hpp
new file mode 100644
index 00000000..0c10b76f
--- /dev/null
+++ b/include/boost/afio/afio.hpp
@@ -0,0 +1 @@
+#include "v2/afio.hpp"
diff --git a/include/boost/afio/bindlib b/include/boost/afio/bindlib
new file mode 160000
+Subproject edcb490f551c2e20887010c571dc284e66d8fc5
diff --git a/include/boost/afio/gsl-lite b/include/boost/afio/gsl-lite
new file mode 160000
+Subproject dd106a06ac08f13ca7afd979b543e67cf04fa8f
diff --git a/include/boost/afio/outcome b/include/boost/afio/outcome
new file mode 160000
+Subproject 977e786200721d69015b9d8aecbb2eb368e10f3
diff --git a/include/boost/afio/v1/afio.hpp b/include/boost/afio/v1/afio.hpp
new file mode 100644
index 00000000..65e0c1da
--- /dev/null
+++ b/include/boost/afio/v1/afio.hpp
@@ -0,0 +1 @@
+#include "branch/include/boost/afio/afio.hpp"
diff --git a/include/boost/afio/v1/branch b/include/boost/afio/v1/branch
new file mode 160000
+Subproject ef42f65530fcc56378b6689bb9df6d00cda3309
diff --git a/include/boost/afio/v2/afio.hpp b/include/boost/afio/v2/afio.hpp
new file mode 100644
index 00000000..1f4b99b8
--- /dev/null
+++ b/include/boost/afio/v2/afio.hpp
@@ -0,0 +1,3 @@
+#include "async_file_handle.hpp"
+#include "statfs.hpp"
+#include "storage_profile.hpp"
diff --git a/include/boost/afio/v2/async_file_handle.hpp b/include/boost/afio/v2/async_file_handle.hpp
new file mode 100644
index 00000000..bebcc419
--- /dev/null
+++ b/include/boost/afio/v2/async_file_handle.hpp
@@ -0,0 +1,248 @@
+/* async_file_handle.hpp
+An async handle to a file
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "file_handle.hpp"
+#include "io_service.hpp"
+
+#ifndef BOOST_AFIO_ASYNC_FILE_HANDLE_H
+#define BOOST_AFIO_ASYNC_FILE_HANDLE_H
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+//! A handle to an open something
+class BOOST_AFIO_DECL async_file_handle : public file_handle
+{
+public:
+ using path_type = io_handle::path_type;
+ using extent_type = io_handle::extent_type;
+ using size_type = io_handle::size_type;
+ using mode = io_handle::mode;
+ using creation = io_handle::creation;
+ using caching = io_handle::caching;
+ using flag = io_handle::flag;
+ using buffer_type = io_handle::buffer_type;
+ using const_buffer_type = io_handle::const_buffer_type;
+ using buffers_type = io_handle::buffers_type;
+ using const_buffers_type = io_handle::const_buffers_type;
+ template <class T> using io_request = io_handle::io_request<T>;
+ template <class T> using io_result = io_handle::io_result<T>;
+
+protected:
+ io_service *_service;
+
+public:
+ //! Default constructor
+ async_file_handle()
+ : file_handle()
+ , _service(nullptr)
+ {
+ }
+
+ //! Construct a handle from a supplied native handle
+ async_file_handle(io_service *service, path_type path, native_handle_type h, caching caching = caching::none, flag flags = flag::none)
+ : file_handle(std::move(path), std::move(h), std::move(caching), std::move(flags))
+ , _service(service)
+ {
+ }
+ //! Implicit move construction of async_file_handle permitted
+ async_file_handle(async_file_handle &&o) noexcept : file_handle(std::move(o)), _service(o._service) { o._service = nullptr; }
+ //! Explicit conversion from file_handle permitted
+ explicit async_file_handle(file_handle &&o) noexcept : file_handle(std::move(o)) {}
+ //! Explicit conversion from handle and io_handle permitted
+ explicit async_file_handle(handle &&o, io_service *service, path_type path) noexcept : file_handle(std::move(o), std::move(path)), _service(service) {}
+ using file_handle::really_copy;
+ //! Copy the handle. Tag enabled because copying handles is expensive (fd duplication).
+ explicit async_file_handle(const async_file_handle &o, really_copy _)
+ : file_handle(o, _)
+ {
+ }
+ //! Move assignment of async_file_handle permitted
+ async_file_handle &operator=(async_file_handle &&o) noexcept
+ {
+ this->~async_file_handle();
+ new(this) async_file_handle(std::move(o));
+ return *this;
+ }
+ //! Swap with another instance
+ void swap(async_file_handle &o) noexcept
+ {
+ async_file_handle temp(std::move(*this));
+ *this = std::move(o);
+ o = std::move(temp);
+ }
+
+ /*! Create an async file handle opening access to a file on path
+ using the given io_service.
+
+ \errors Any of the values POSIX open() or CreateFile() can return.
+ */
+ //[[bindlib::make_free]]
+ static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<async_file_handle> async_file(io_service &service, path_type _path, mode _mode = mode::read, creation _creation = creation::open_existing, caching _caching = caching::all, flag flags = flag::none) noexcept;
+
+ /*! Clone this handle to a different io_service (copy constructor is disabled to avoid accidental copying)
+
+ \errors Any of the values POSIX dup() or DuplicateHandle() can return.
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<async_file_handle> clone(io_service &service) const noexcept;
+ using file_handle::clone;
+
+#if DOXYGEN_SHOULD_SKIP_THIS
+private:
+#else
+protected:
+#endif
+ using shared_size_type = size_type;
+ enum class operation_t
+ {
+ read,
+ write
+ };
+ // Holds state for an i/o in progress. Will be subclassed with platform specific state and how to implement completion.
+ // Note this is allocated using malloc not new to avoid memory zeroing, and therefore it has a custom deleter.
+ struct _erased_io_state_type
+ {
+ async_file_handle *parent;
+ operation_t operation;
+ size_t items;
+ shared_size_type items_to_go;
+ constexpr _erased_io_state_type(async_file_handle *_parent, operation_t _operation, size_t _items)
+ : parent(_parent)
+ , operation(_operation)
+ , items(_items)
+ , items_to_go(0)
+ {
+ }
+ /*
+ For Windows:
+ - errcode: GetLastError() code
+ - bytes_transferred: obvious
+ - internal_state: LPOVERLAPPED for this op
+
+ For POSIX AIO:
+ - errcode: errno code
+ - bytes_transferred: return from aio_return(), usually bytes transferred
+ - internal_state: address of pointer to struct aiocb in io_service's _aiocbsv
+ */
+ virtual void operator()(long errcode, long bytes_transferred, void *internal_state) noexcept = 0;
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~_erased_io_state_type()
+ {
+ // i/o still pending is very bad, this should never happen
+ assert(!items_to_go);
+ if(items_to_go)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("FATAL: io_state destructed while i/o still in flight, the derived class should never allow this." << std::endl);
+ abort();
+ }
+ }
+ };
+ // State for an i/o in progress, but with the per operation typing
+ template <class CompletionRoutine, class BuffersType> struct _io_state_type : public _erased_io_state_type
+ {
+ io_result<BuffersType> result;
+ CompletionRoutine completion;
+ constexpr _io_state_type(async_file_handle *_parent, operation_t _operation, CompletionRoutine &&f, size_t _items)
+ : _erased_io_state_type(_parent, _operation, _items)
+ , result(make_result(BuffersType()))
+ , completion(std::forward<CompletionRoutine>(f))
+ {
+ }
+ };
+ struct _io_state_deleter
+ {
+ template <class U> void operator()(U *_ptr) const
+ {
+ _ptr->~U();
+ char *ptr = (char *) _ptr;
+ ::free(ptr);
+ }
+ };
+
+public:
+ /*! Smart pointer to state of an i/o in progress. Destroying this before an i/o has completed
+ is <b>blocking</b> because the i/o must be cancelled before the destructor can safely exit.
+ */
+ using erased_io_state_ptr = std::unique_ptr<_erased_io_state_type, _io_state_deleter>;
+ /*! Smart pointer to state of an i/o in progress. Destroying this before an i/o has completed
+ is <b>blocking</b> because the i/o must be cancelled before the destructor can safely exit.
+ */
+ template <class CompletionRoutine, class BuffersType> using io_state_ptr = std::unique_ptr<_io_state_type<CompletionRoutine, BuffersType>, _io_state_deleter>;
+
+#if DOXYGEN_SHOULD_SKIP_THIS
+private:
+#else
+protected:
+#endif
+ template <class CompletionRoutine, class BuffersType, class IORoutine> result<io_state_ptr<CompletionRoutine, BuffersType>> _begin_io(operation_t operation, io_request<BuffersType> reqs, CompletionRoutine &&completion, IORoutine &&ioroutine) noexcept;
+
+public:
+ /*! \brief Schedule a read to occur asynchronously.
+
+ \return Either an io_state_ptr to the i/o in progress, or an error code.
+ \param reqs A scatter-gather and offset request.
+ \param completion A callable to call upon i/o completion. Spec is void(async_file_handle *, io_result<buffers_type> &).
+ Note that buffers returned may not be buffers input, see documentation for read().
+ \errors As for read(), plus ENOMEM.
+ \mallocs One calloc, one free. The allocation is unavoidable due to the need to store a type
+ erased completion handler of unknown type.
+ */
+ //[[bindlib::make_free]]
+ template <class CompletionRoutine> result<io_state_ptr<CompletionRoutine, buffers_type>> async_read(io_request<buffers_type> reqs, CompletionRoutine &&completion) noexcept;
+
+ /*! \brief Schedule a write to occur asynchronously.
+
+ \return Either an io_state_ptr to the i/o in progress, or an error code.
+ \param reqs A scatter-gather and offset request.
+ \param completion A callable to call upon i/o completion. Spec is void(async_file_handle *, io_result<const_buffers_type> &).
+ Note that buffers returned may not be buffers input, see documentation for write().
+ \errors As for write(), plus ENOMEM.
+ \mallocs One calloc, one free. The allocation is unavoidable due to the need to store a type
+ erased completion handler of unknown type.
+ */
+ //[[bindlib::make_free]]
+ template <class CompletionRoutine> result<io_state_ptr<CompletionRoutine, const_buffers_type>> async_write(io_request<const_buffers_type> reqs, CompletionRoutine &&completion) noexcept;
+
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> read(io_request<buffers_type> reqs, deadline d = deadline()) noexcept override;
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept override;
+};
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#define BOOST_AFIO_INCLUDED_BY_HEADER 1
+#ifdef WIN32
+#include "detail/impl/windows/async_file_handle.ipp"
+#else
+#include "detail/impl/posix/async_file_handle.ipp"
+#endif
+#undef BOOST_AFIO_INCLUDED_BY_HEADER
+#endif
+
+#endif
diff --git a/include/boost/afio/v2/config.hpp b/include/boost/afio/v2/config.hpp
new file mode 100644
index 00000000..0fba7f58
--- /dev/null
+++ b/include/boost/afio/v2/config.hpp
@@ -0,0 +1,516 @@
+/* config.hpp
+Configures Boost.AFIO
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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
+
+#if defined(WIN32)
+# if !defined(_WIN32_WINNT)
+# define _WIN32_WINNT 0x0600
+# elif _WIN32_WINNT<0x0600
+# error _WIN32_WINNT must at least be set to Windows Vista for Boost AFIO to work
+# endif
+# if defined(NTDDI_VERSION) && NTDDI_VERSION < 0x06000000
+# error NTDDI_VERSION must at least be set to Windows Vista for Boost AFIO to work
+# endif
+#endif
+
+// Pull in detection of __MINGW64_VERSION_MAJOR
+#ifdef __MINGW32__
+# include <_mingw.h>
+#endif
+
+#include "../../include/boost/afio/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
+#ifndef __cpp_init_captures
+# error Boost.AFIO needs lambda init captures support in the compiler (C++ 14)
+#endif
+#ifndef __cpp_attributes
+# error Boost.AFIO needs attributes support in the compiler
+#endif
+#if (defined(__GNUC__) && !defined(__clang__))
+# define BOOST_AFIO_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
+# if BOOST_AFIO_GCC_VERSION < 40900
+# error Boost.AFIO needs GCC 4.9 or later as the <regex> shipped in libstdc++ < 4.9 does not work
+# endif
+#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
+#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.Outcome 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.Outcome 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
+#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), inline)
+#else
+# define BOOST_AFIO_V2 (boost), (afio), (BOOST_BINDLIB_NAMESPACE_VERSION(v2, BOOST_AFIO_V2_STL11_IMPL, BOOST_AFIO_V2_FILESYSTEM_IMPL))
+#endif
+#if DOXYGEN_SHOULD_SKIP_THIS
+//! The Boost namespace
+namespace boost {
+ //! The AFIO namespace
+ namespace afio {
+ //! Inline namespace for this version of AFIO
+ inline namespace v2 {
+} } }
+#define BOOST_AFIO_V2_NAMESPACE boost::afio::v2
+#define BOOST_AFIO_V2_NAMESPACE_BEGIN namespace boost { namespace afio { inline namespace v2 {
+#define BOOST_AFIO_V2_NAMESPACE_END } } }
+#else
+#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)
+#endif
+
+// 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))
+#define BOOST_STL11_ATOMIC_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11))
+#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), (chrono))
+#define BOOST_STL11_CHRONO_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11), (chrono))
+#define BOOST_STL11_CONDITION_VARIABLE_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11))
+#define BOOST_STL11_CONDITION_VARIABLE_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11))
+#define BOOST_STL1z_FILESYSTEM_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl1z), (filesystem))
+#define BOOST_STL1z_FILESYSTEM_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl1z), (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))
+#define BOOST_STL11_FUTURE_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11))
+#define BOOST_STL11_FUTURE_MAP_NO_FUTURE
+#define BOOST_STL11_MUTEX_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11))
+#define BOOST_STL11_MUTEX_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11))
+#define BOOST_STL11_RATIO_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11))
+#define BOOST_STL11_RATIO_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11))
+#define BOOST_STL11_THREAD_MAP_NAMESPACE_BEGIN BOOST_BINDLIB_NAMESPACE_BEGIN(BOOST_AFIO_V2, (stl11))
+#define BOOST_STL11_THREAD_MAP_NAMESPACE_END BOOST_BINDLIB_NAMESPACE_END (BOOST_AFIO_V2, (stl11))
+#include BOOST_BINDLIB_INCLUDE_STL11(../../include/boost/afio/bindlib, BOOST_AFIO_V2_STL11_IMPL, atomic)
+#include BOOST_BINDLIB_INCLUDE_STL11(../../include/boost/afio/bindlib, BOOST_AFIO_V2_STL11_IMPL, chrono)
+#include BOOST_BINDLIB_INCLUDE_STL11(../../include/boost/afio/bindlib, BOOST_AFIO_V2_STL11_IMPL, condition_variable)
+#include BOOST_BINDLIB_INCLUDE_STL1z(../../include/boost/afio/bindlib, BOOST_AFIO_V2_FILESYSTEM_IMPL, filesystem)
+#include BOOST_BINDLIB_INCLUDE_STL11(../../include/boost/afio/bindlib, BOOST_AFIO_V2_STL11_IMPL, future)
+#include BOOST_BINDLIB_INCLUDE_STL11(../../include/boost/afio/bindlib, BOOST_AFIO_V2_STL11_IMPL, mutex)
+#include BOOST_BINDLIB_INCLUDE_STL11(../../include/boost/afio/bindlib, BOOST_AFIO_V2_STL11_IMPL, ratio)
+#include BOOST_BINDLIB_INCLUDE_STL11(../../include/boost/afio/bindlib, BOOST_AFIO_V2_STL11_IMPL, thread)
+
+#include "../../include/boost/afio/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_ready_outcome;
+using BOOST_OUTCOME_V1_NAMESPACE::make_errored_outcome;
+using BOOST_OUTCOME_V1_NAMESPACE::make_exceptional_outcome;
+using BOOST_OUTCOME_V1_NAMESPACE::make_result;
+using BOOST_OUTCOME_V1_NAMESPACE::make_ready_result;
+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
+
+#if !BOOST_AFIO_HAVE_CXX17_SPAN_IMPLEMENTATION
+#include "../../include/boost/afio/gsl-lite/include/gsl.h"
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+template<class T> using span = gsl::span<T>;
+using gsl::as_span;
+BOOST_AFIO_V2_NAMESPACE_END
+#endif
+
+#include <time.h> // for struct timespec
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+// The C++ 11 runtime is much better at exception state than Boost so no choice here
+using std::make_exception_ptr;
+using std::error_code;
+using std::generic_category;
+using std::system_category;
+using std::system_error;
+
+// Too darn useful
+using std::to_string;
+namespace detail
+{
+ template<class F> using function_ptr = boost::outcome::detail::function_ptr<F>;
+ using boost::outcome::detail::make_function_ptr;
+ using boost::outcome::detail::emplace_function_ptr;
+}
+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
+
+
+// Temporary in lieu of afio::path
+using fixme_path = stl1z::filesystem::path;
+
+//! Constexpr typesafe bitwise flags support
+template<class Enum> struct bitfield : public Enum
+{
+ //! The C style enum type which represents flags in this bitfield
+ using enum_type = typename Enum::enum_type;
+ //! The type which the C style enum implicitly converts to
+ using underlying_type = std::underlying_type_t<enum_type>;
+private:
+ underlying_type _value;
+public:
+ //! Default construct to all bits zero
+ constexpr bitfield() noexcept : _value(0) { }
+ //! Implicit construction from the C style enum
+ constexpr bitfield(enum_type v) noexcept : _value(v) { }
+ //! Implicit construction from the underlying type of the C enum
+ constexpr bitfield(underlying_type v) noexcept : _value(v) { }
+
+ //! Permit explicit casting to the underlying type
+ explicit constexpr operator underlying_type() const noexcept { return _value; }
+ //! Test for non-zeroness
+ explicit constexpr operator bool() const noexcept { return !!_value; }
+ //! Test for zeroness
+ constexpr bool operator !() const noexcept { return !_value; }
+
+ //! Performs a bitwise NOT
+ constexpr bitfield operator ~() const noexcept { return bitfield(~_value); }
+ //! Performs a bitwise AND
+ constexpr bitfield operator &(bitfield o) const noexcept { return bitfield(_value&o._value); }
+ //! Performs a bitwise AND
+ BOOST_CXX14_CONSTEXPR bitfield &operator &=(bitfield o) noexcept { _value &= o._value; return *this; }
+ //! Performs a bitwise OR
+ constexpr bitfield operator |(bitfield o) const noexcept { return bitfield(_value | o._value); }
+ //! Performs a bitwise OR
+ BOOST_CXX14_CONSTEXPR bitfield &operator |=(bitfield o) noexcept { _value |= o._value; return *this; }
+ //! Performs a bitwise XOR
+ constexpr bitfield operator ^(bitfield o) const noexcept { return bitfield(_value ^ o._value); }
+ //! Performs a bitwise XOR
+ BOOST_CXX14_CONSTEXPR bitfield &operator ^=(bitfield o) noexcept { _value ^= o._value; return *this; }
+};
+
+//! Begins a typesafe bitfield
+#define BOOST_AFIO_BITFIELD_BEGIN(type) \
+struct type##_base \
+{ \
+ enum enum_type : unsigned
+
+//! Ends a typesafe bitfield
+#define BOOST_AFIO_BITFIELD_END(type) \
+;}; \
+using type = bitfield<type##_base>;
+
+// Native handle support
+namespace win
+{
+ using handle = void *;
+ using dword = unsigned long;
+}
+
+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
+#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
+#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_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
+#include <iostream>
+#define BOOST_AFIO_LOG_FATAL_EXIT(expr) std::cerr << expr
+#endif
+
+#endif // BOOST_AFIO_NEED_DEFINE
+
diff --git a/include/boost/afio/v2/deadline.h b/include/boost/afio/v2/deadline.h
new file mode 100644
index 00000000..08871d5f
--- /dev/null
+++ b/include/boost/afio/v2/deadline.h
@@ -0,0 +1,99 @@
+/* deadline.hpp
+Specifies a time deadline
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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_DEADLINE_H
+#define BOOST_AFIO_DEADLINE_H
+
+#include <stdbool.h>
+#include <time.h>
+
+#ifdef __cplusplus
+#include "config.hpp"
+#include <stdexcept>
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+#define BOOST_AFIO_DEADLINE_NAME deadline
+#else
+#define BOOST_AFIO_DEADLINE_NAME boost_afio_deadline
+#endif
+
+/*! \struct deadline
+\brief A time deadline in either relative-to-now or absolute (system clock) terms
+*/
+struct BOOST_AFIO_DEADLINE_NAME
+{
+ bool steady; //!< True if deadline does not change with system clock changes
+ union {
+ struct timespec utc; //!< System time from timespec_get(&ts, TIME_UTC)
+ unsigned long long nsecs; //!< Nanosecond ticks from start of operation
+ };
+#ifdef __cplusplus
+ deadline() noexcept { memset(this, 0, sizeof(*this)); }
+ //! True if deadline is valid
+ explicit operator bool() const noexcept { return steady || utc.tv_sec != 0; }
+ //! Construct a deadline from a system clock time point
+ deadline(stl11::chrono::system_clock::time_point tp)
+ : steady(false)
+ {
+ stl11::chrono::seconds secs(stl11::chrono::system_clock::to_time_t(tp));
+ utc.tv_sec = secs.count();
+ stl11::chrono::system_clock::time_point _tp(stl11::chrono::system_clock::from_time_t(utc.tv_sec));
+ utc.tv_nsec = (long) stl11::chrono::duration_cast<stl11::chrono::nanoseconds>(tp - _tp).count();
+ }
+ //! Construct a deadline from a duration from now
+ template <class Rep, class Period>
+ deadline(stl11::chrono::duration<Rep, Period> d)
+ : steady(true)
+ {
+ stl11::chrono::nanoseconds _nsecs = stl11::chrono::duration_cast<stl11::chrono::nanoseconds>(d);
+ // Negative durations are zero duration
+ if(_nsecs.count() > 0)
+ nsecs = _nsecs.count();
+ else
+ nsecs = 0;
+ }
+ //! Returns a system_clock::time_point for this deadline
+ stl11::chrono::system_clock::time_point to_time_point() const
+ {
+ if(steady)
+ throw std::invalid_argument("Not a UTC deadline!");
+ stl11::chrono::system_clock::time_point tp(stl11::chrono::system_clock::from_time_t(utc.tv_sec));
+ tp += stl11::chrono::duration_cast<stl11::chrono::system_clock::duration>(stl11::chrono::nanoseconds(utc.tv_nsec));
+ return tp;
+ }
+#endif
+};
+
+#undef BOOST_AFIO_DEADLINE_NAME
+#ifdef __cplusplus
+BOOST_AFIO_V2_NAMESPACE_END
+#endif
+
+#endif
diff --git a/include/boost/afio/v2/detail/child_process.hpp b/include/boost/afio/v2/detail/child_process.hpp
new file mode 100644
index 00000000..5e51bec2
--- /dev/null
+++ b/include/boost/afio/v2/detail/child_process.hpp
@@ -0,0 +1,117 @@
+/* child_process.hpp
+Routines for handling child processes
+(C) 2016 Niall Douglas http://www.nedprod.com/
+File Created: Marc 2016
+
+
+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_CHILD_PROCESS_H
+#define BOOST_AFIO_CHILD_PROCESS_H
+
+#include "../deadline.h"
+#include "../native_handle_type.hpp"
+
+#include <map>
+#include <vector>
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+namespace detail
+{
+ //! Returns the path of the calling process
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC stl1z::filesystem::path current_process_path();
+
+ //! Returns the environment of the calling process
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC std::map<stl1z::filesystem::path::string_type, stl1z::filesystem::path::string_type> current_process_env();
+
+ /*! \class child_process
+ \brief Launches and manages a child process with stdin, stdout and stderr.
+ */
+ class BOOST_AFIO_DECL child_process
+ {
+ stl1z::filesystem::path _path;
+ native_handle_type _processh;
+ native_handle_type _readh, _writeh, _errh;
+ std::vector<stl1z::filesystem::path::string_type> _args;
+ std::map<stl1z::filesystem::path::string_type, stl1z::filesystem::path::string_type> _env;
+
+ protected:
+ child_process(stl1z::filesystem::path path, std::vector<stl1z::filesystem::path::string_type> args, std::map<stl1z::filesystem::path::string_type, stl1z::filesystem::path::string_type> env)
+ : _path(std::move(path))
+ , _args(std::move(args))
+ , _env(std::move(env))
+ {
+ }
+
+ public:
+ child_process(const child_process &) = delete;
+ child_process(child_process &&o) = default;
+ child_process &operator=(const child_process &) = delete;
+ child_process &operator=(child_process &&) = default;
+ ~child_process();
+
+ //! Launches an executable as a child process. No shell is invoked on POSIX.
+ static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<child_process> launch(stl1z::filesystem::path path, std::vector<stl1z::filesystem::path::string_type> args, std::map<stl1z::filesystem::path::string_type, stl1z::filesystem::path::string_type> env = current_process_env()) noexcept;
+
+ //! Returns the path of the executable
+ const stl1z::filesystem::path &path() const noexcept { return _path; }
+ //! Returns the args used to launch the executable
+ const std::vector<stl1z::filesystem::path::string_type> &arguments() const noexcept { return _args; }
+ //! Returns the environment used to launch the executable
+ const std::map<stl1z::filesystem::path::string_type, stl1z::filesystem::path::string_type> &environment() const noexcept { return _env; }
+ //! Returns the process identifier
+ const native_handle_type &process_native_handle() const noexcept { return _processh; }
+ //! Returns the read handle
+ const native_handle_type &read_native_handle() const noexcept { return _readh; }
+ //! Returns the write handle
+ const native_handle_type &write_native_handle() const noexcept { return _writeh; }
+ //! Returns the error handle
+ const native_handle_type &error_native_handle() const noexcept { return _errh; }
+
+ //! True if child process is currently running
+ bool is_running() const noexcept;
+
+ //! Waits for a child process to exit until deadline /em d
+ result<intptr_t> wait_until(deadline d) noexcept;
+ //! \overload
+ result<intptr_t> wait() noexcept { return wait_until(deadline()); }
+ };
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#define BOOST_AFIO_INCLUDED_BY_HEADER 1
+#ifdef WIN32
+#include "impl/windows/child_process.ipp"
+#else
+#include "impl/posix/child_process.ipp"
+#endif
+#undef BOOST_AFIO_INCLUDED_BY_HEADER
+#endif
+
+#endif
diff --git a/include/boost/afio/v2/detail/impl/posix/child_process.ipp b/include/boost/afio/v2/detail/impl/posix/child_process.ipp
new file mode 100644
index 00000000..eb44f528
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/posix/child_process.ipp
@@ -0,0 +1,49 @@
+/* child_process.hpp
+Routines for handling child processes
+(C) 2016 Niall Douglas http://www.nedprod.com/
+File Created: Marc 2016
+
+
+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 "../../child_process.hpp"
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+namespace detail
+{
+ child_process::~child_process();
+
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<child_process> child_process::launch(stl1z::filesystem::path path) noexcept;
+
+ bool child_process::is_running() const noexcept;
+
+ result<intptr_t> child_process::wait_until(deadline d) noexcept;
+
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC stl1z::filesystem::path child_process::current_process_path();
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/posix/handle.ipp b/include/boost/afio/v2/detail/impl/posix/handle.ipp
new file mode 100644
index 00000000..700b6798
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/posix/handle.ipp
@@ -0,0 +1,422 @@
+/* handle.hpp
+A handle to a file
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "../../../handle.hpp"
+
+#include <unistd.h>
+#include <fcntl.h>
+#if BOOST_AFIO_USE_POSIX_AIO
+# include <aio.h>
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+result<handle> handle::clone(io_service &service, handle::mode mode, handle::caching caching) const noexcept
+{
+ result<handle> ret(handle(&service, _path, native_handle_type(), _caching, _flags));
+ ret.value()._v.behaviour = _v.behaviour;
+ // If current handle is read-only and clone request is to add write powers, we can't use dup()
+ if (mode != handle::mode::unchanged && !_v.is_writable() && (mode==handle::mode::write || mode==handle::mode::append))
+ {
+ // Race free fetch the handle's path and reopen it with the new permissions
+ // TODO FIXME
+ return make_errored_result<handle>(ENOSYS);
+ }
+ else
+ {
+ if (-1 == (ret.value()._v.fd = ::dup(_v.fd)))
+ return make_errored_result<handle>(errno);
+ // Only care if cloning and changing append only flag
+ if (mode != handle::mode::unchanged && (mode==handle::mode::write || mode==handle::mode::append))
+ {
+ ret.value()._v.behaviour = _v.behaviour & ~(native_handle_type::disposition::seekable | native_handle_type::disposition::readable | native_handle_type::disposition::writable | native_handle_type::disposition::append_only);
+ int attribs = 0;
+ if (-1 == (attribs = fcntl(ret.value()._v.fd, F_GETFL)))
+ return make_errored_result<handle>(errno);
+ switch (mode)
+ {
+ case handle::mode::unchanged:
+ break;
+ case handle::mode::none:
+ case handle::mode::attr_read:
+ case handle::mode::attr_write:
+ case handle::mode::read:
+ return make_errored_result<handle>(EINVAL);
+ case handle::mode::write:
+ attribs&=~O_APPEND;
+ ret.value()._v.behaviour |= native_handle_type::disposition::seekable | native_handle_type::disposition::readable| native_handle_type::disposition::writable;
+ break;
+ case handle::mode::append:
+ attribs |= O_APPEND;
+ ret.value()._v.behaviour |= native_handle_type::disposition::append_only | native_handle_type::disposition::writable;
+ break;
+ }
+ if(-1==fcntl(ret.value()._v.fd, F_SETFL, attribs))
+ return make_errored_result<handle>(errno);
+ }
+ if (caching != handle::caching::unchanged && caching != _caching)
+ {
+ // TODO: Allow fiddling with O_DIRECT
+ return make_errored_result<handle>(EINVAL);
+ }
+ }
+ return ret;
+}
+
+result<file_handle> file_handle::file(io_service &service, file_handle::path_type _path, file_handle::mode _mode, file_handle::creation _creation, file_handle::caching _caching, file_handle::flag flags) noexcept
+{
+ result<file_handle> ret(file_handle(&service, std::move(_path), native_handle_type(), _caching, flags));
+ native_handle_type &nativeh = ret.get()._v;
+ int attribs = 0;
+ switch (_mode)
+ {
+ case mode::unchanged:
+ return make_errored_result<file_handle>(EINVAL);
+ case mode::none:
+ break;
+ case mode::attr_read:
+ case mode::read:
+ attribs = O_RDONLY;
+ nativeh.behaviour |= native_handle_type::disposition::seekable|native_handle_type::disposition::readable;
+ break;
+ case mode::attr_write:
+ case mode::write:
+ attribs = O_RDWR;
+ nativeh.behaviour |= native_handle_type::disposition::seekable | native_handle_type::disposition::readable| native_handle_type::disposition::writable;
+ break;
+ case mode::append:
+ attribs = O_APPEND;
+ nativeh.behaviour |= native_handle_type::disposition::writable|native_handle_type::disposition::append_only;
+ break;
+ }
+ switch (_creation)
+ {
+ case creation::open_existing:
+ break;
+ case creation::only_if_not_exist:
+ attribs |= O_CREAT | O_EXCL;
+ break;
+ case creation::if_needed:
+ attribs |= O_CREAT;
+ break;
+ case creation::truncate:
+ attribs |= O_TRUNC;
+ break;
+ }
+ nativeh.behaviour |= native_handle_type::disposition::file;
+ switch (_caching)
+ {
+ case caching::unchanged:
+ return make_errored_result<file_handle>(EINVAL);
+ case caching::none:
+ attribs |= O_SYNC | O_DIRECT;
+ nativeh.behaviour |= native_handle_type::disposition::aligned_io;
+ break;
+ case caching::only_metadata:
+ attribs |= O_DIRECT;
+ nativeh.behaviour |= native_handle_type::disposition::aligned_io;
+ break;
+ case caching::reads:
+ attribs |= O_SYNC;
+ break;
+ case caching::reads_and_metadata:
+#ifdef O_DSYNC
+ attribs |= O_DSYNC;
+#else
+ attribs |= O_SYNC;
+#endif
+ break;
+ case caching::all:
+ case caching::safety_fsyncs:
+ case caching::temporary:
+ break;
+ }
+ const char *path_=ret.value()._path.c_str();
+ if (-1 == (nativeh.fd = ::open(path_, attribs, 0x1b0/*660*/)))
+ return make_errored_result<file_handle>(errno);
+ if (_creation == creation::truncate && ret.value().are_safety_fsyncs_issued())
+ fsync(nativeh.fd);
+ return ret;
+}
+
+handle::~handle()
+{
+ if (_v)
+ {
+ if(are_safety_fsyncs_issued())
+ {
+ fsync(_v.fd);
+ }
+ ::close(_v.fd);
+ _v = native_handle_type();
+ }
+}
+
+template<class CompletionRoutine, class BuffersType, class IORoutine> result<file_handle::io_state_ptr<CompletionRoutine, BuffersType>> file_handle::_begin_io(file_handle::operation_t operation, file_handle::io_request<BuffersType> reqs, CompletionRoutine &&completion, IORoutine &&ioroutine) noexcept
+{
+ // Need to keep a set of aiocbs matching the scatter-gather buffers
+ struct state_type : public _io_state_type<CompletionRoutine, BuffersType>
+ {
+#if BOOST_AFIO_USE_POSIX_AIO
+ struct aiocb aiocbs[1];
+#else
+#error todo
+#endif
+ state_type(handle *_parent, operation_t _operation, CompletionRoutine &&f, size_t _items) : _io_state_type<CompletionRoutine, BuffersType>(_parent, _operation, std::forward<CompletionRoutine>(f), _items) { }
+ virtual void operator()(long errcode, long bytes_transferred, void *internal_state) noexcept override final
+ {
+#if BOOST_AFIO_USE_POSIX_AIO
+ struct aiocb **_paiocb=(struct aiocb **) internal_state;
+ struct aiocb *aiocb=*_paiocb;
+ assert(aiocb>=aiocbs && aiocb<aiocbs+this->items);
+ *_paiocb=nullptr;
+#else
+#error todo
+#endif
+ if (this->result)
+ {
+ if (errcode)
+ this->result = make_errored_result<BuffersType>((int) errcode);
+ else
+ {
+ // Figure out which i/o I am and update the buffer in question
+#if BOOST_AFIO_USE_POSIX_AIO
+ size_t idx = aiocb - aiocbs;
+#else
+#error todo
+#endif
+ if(idx>=this->items)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("file_handle::io_state::operator() called with invalid index " << idx);
+ std::terminate();
+ }
+ this->result.value()[idx].second = bytes_transferred;
+ }
+ }
+ this->parent->service()->_work_done();
+ // Are we done?
+ if (!--this->items_to_go)
+ this->completion(this);
+ }
+ virtual ~state_type() override final
+ {
+ // Do we need to cancel pending i/o?
+ if (this->items_to_go)
+ {
+ for (size_t n = 0; n < this->items; n++)
+ {
+#if BOOST_AFIO_USE_POSIX_AIO
+ int ret=aio_cancel(this->parent->native_handle().fd, aiocbs + n);
+#if 0
+ if(ret<0 || ret==AIO_NOTCANCELED)
+ {
+ std::cout << "Failed to cancel " << (aiocbs+n) << std::endl;
+ }
+ else if(ret==AIO_CANCELED)
+ {
+ std::cout << "Cancelled " << (aiocbs+n) << std::endl;
+ }
+ else if(ret==AIO_ALLDONE)
+ {
+ std::cout << "Already done " << (aiocbs+n) << std::endl;
+ }
+#endif
+#else
+#error todo
+#endif
+ }
+ // Pump the i/o service until all pending i/o is completed
+ while (this->items_to_go)
+ {
+ auto res=this->parent->service()->run();
+#ifndef NDEBUG
+ if(res.has_error())
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("file_handle: io_service failed due to '" << res.get_error().message() << "'");
+ std::terminate();
+ }
+ if(!res.get())
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("file_handle: io_service returns no work when i/o has not completed");
+ std::terminate();
+ }
+#endif
+ }
+ }
+ }
+ } *state;
+ extent_type offset = reqs.offset;
+ size_t statelen = sizeof(state_type) + (reqs.buffers.size() - 1)*sizeof(struct aiocb), items(reqs.buffers.size());
+ using return_type = io_state_ptr<CompletionRoutine, BuffersType>;
+#if BOOST_AFIO_USE_POSIX_AIO && defined(AIO_LISTIO_MAX)
+ if(items>AIO_LISTIO_MAX)
+ return make_errored_result<return_type>(EINVAL);
+#endif
+ void *mem = ::calloc(1, statelen);
+ if (!mem)
+ return make_errored_result<return_type>(ENOMEM);
+ return_type _state((_io_state_type<CompletionRoutine, BuffersType> *) mem);
+ new((state = (state_type *)mem)) state_type(this, operation, std::forward<CompletionRoutine>(completion), items);
+ // Noexcept move the buffers from req into result
+ BuffersType &out = state->result.value();
+ out = std::move(reqs.buffers);
+ for (size_t n = 0; n < items; n++)
+ {
+#if BOOST_AFIO_USE_POSIX_AIO
+ struct aiocb *aiocb = state->aiocbs + n;
+ aiocb->aio_fildes = _v.fd;
+ aiocb->aio_offset = offset;
+ aiocb->aio_buf = (void *) out[n].first;
+ aiocb->aio_nbytes = out[n].second;
+ aiocb->aio_sigevent.sigev_notify = SIGEV_NONE;
+ aiocb->aio_sigevent.sigev_value.sival_ptr=(void *) state;
+ aiocb->aio_lio_opcode = (operation==operation_t::write) ? LIO_WRITE : LIO_READ;
+#else
+#error todo
+#endif
+ offset += out[n].second;
+ ++state->items_to_go;
+ }
+ int ret=0;
+#if BOOST_AFIO_USE_POSIX_AIO
+ if(service()->using_kqueues())
+ {
+# if BOOST_AFIO_COMPILE_KQUEUES
+ // Only issue one kqueue event when entire scatter-gather has completed
+ struct _sigev={0};
+#error todo
+#endif
+ }
+ else
+ {
+ // Add these i/o's to the quick aio_suspend list
+ service()->_aiocbsv.resize(service()->_aiocbsv.size()+items);
+ struct aiocb **thislist=service()->_aiocbsv.data()+service()->_aiocbsv.size()-items;
+ for (size_t n = 0; n < items; n++)
+ {
+ struct aiocb *aiocb = state->aiocbs + n;
+ thislist[n]=aiocb;
+ }
+ ret=lio_listio(LIO_NOWAIT, thislist, items, nullptr);
+ }
+#else
+#error todo
+#endif
+ if(ret<0)
+ {
+ service()->_aiocbsv.resize(service()->_aiocbsv.size()-items);
+ state->items_to_go=0;
+ state->result = make_errored_result<BuffersType>(errno);
+ state->completion(state);
+ return make_result<return_type>(std::move(_state));
+ }
+ service()->_work_enqueued(items);
+ return make_result<return_type>(std::move(_state));
+}
+
+template<class CompletionRoutine> result<file_handle::io_state_ptr<CompletionRoutine, file_handle::buffers_type>> file_handle::async_read(file_handle::io_request<file_handle::buffers_type> reqs, CompletionRoutine &&completion) noexcept
+{
+ return _begin_io(operation_t::read, std::move(reqs), [completion=std::forward<CompletionRoutine>(completion)](auto *state) {
+ completion(state->parent, state->result);
+ }, nullptr);
+}
+
+template<class CompletionRoutine> result<file_handle::io_state_ptr<CompletionRoutine, file_handle::const_buffers_type>> file_handle::async_write(file_handle::io_request<file_handle::const_buffers_type> reqs, CompletionRoutine &&completion) noexcept
+{
+ return _begin_io(operation_t::write, std::move(reqs), [completion = std::forward<CompletionRoutine>(completion)](auto *state) {
+ completion(state->parent, state->result);
+ }, nullptr);
+}
+
+file_handle::io_result<file_handle::buffers_type> file_handle::read(file_handle::io_request<file_handle::buffers_type> reqs, deadline d) noexcept
+{
+ io_result<buffers_type> ret;
+ auto _io_state(_begin_io(operation_t::read, std::move(reqs), [&ret](auto *state) {
+ ret = std::move(state->result);
+ }, nullptr));
+ BOOST_OUTCOME_FILTER_ERROR(io_state, _io_state);
+
+ // While i/o is not done pump i/o completion
+ while (!ret.is_ready())
+ {
+ auto t(_service->run_until(d));
+ // If i/o service pump failed or timed out, cancel outstanding i/o and return
+ if (!t)
+ return make_errored_result<buffers_type>(t.get_error());
+#ifndef NDEBUG
+ if(!ret.is_ready() && t && !t.get())
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("file_handle: io_service returns no work when i/o has not completed");
+ std::terminate();
+ }
+#endif
+ }
+ return ret;
+}
+
+file_handle::io_result<file_handle::const_buffers_type> file_handle::write(file_handle::io_request<file_handle::const_buffers_type> reqs, deadline d) noexcept
+{
+ io_result<const_buffers_type> ret;
+ auto _io_state(_begin_io(operation_t::write, std::move(reqs), [&ret](auto *state) {
+ ret = std::move(state->result);
+ }, nullptr));
+ BOOST_OUTCOME_FILTER_ERROR(io_state, _io_state);
+
+ // While i/o is not done pump i/o completion
+ while (!ret.is_ready())
+ {
+ auto t(_service->run_until(d));
+ // If i/o service pump failed or timed out, cancel outstanding i/o and return
+ if (!t)
+ return make_errored_result<const_buffers_type>(t.get_error());
+#ifndef NDEBUG
+ if(!ret.is_ready() && t && !t.get())
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("file_handle: io_service returns no work when i/o has not completed");
+ std::terminate();
+ }
+#endif
+ }
+ return ret;
+}
+
+result<file_handle::extent_type> file_handle::truncate(file_handle::extent_type newsize) noexcept
+{
+ if (ftruncate(_v.fd, newsize)<0)
+ return make_errored_result<extent_type>(errno);
+ if (are_safety_fsyncs_issued())
+ {
+ fsync(_v.fd);
+ }
+ return newsize;
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/posix/io_service.ipp b/include/boost/afio/v2/detail/impl/posix/io_service.ipp
new file mode 100644
index 00000000..9d740b42
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/posix/io_service.ipp
@@ -0,0 +1,349 @@
+/* io_service.hpp
+Multiplex file i/o
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "../../../handle.hpp"
+
+#include <pthread.h>
+#if BOOST_AFIO_USE_POSIX_AIO
+# include <aio.h>
+# include <sys/mman.h>
+# if BOOST_AFIO_COMPILE_KQUEUES
+# include <sys/types.h>
+# include <sys/event.h>
+# include <sys/time.h>
+# endif
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+static int interrupt_signal;
+static struct sigaction interrupt_signal_handler_old_action;
+struct ucontext;
+static inline void interrupt_signal_handler(int, siginfo_t *, void *)
+{
+ // We do nothing, and aio_suspend should exit with EINTR
+}
+
+int io_service::interruption_signal() noexcept
+{
+ return interrupt_signal;
+}
+
+int io_service::set_interruption_signal(int signo)
+{
+ int ret=interrupt_signal;
+ if(interrupt_signal)
+ {
+ if(sigaction(interrupt_signal, &interrupt_signal_handler_old_action, nullptr)<0)
+ throw std::system_error(errno, std::system_category());
+ interrupt_signal=0;
+ }
+ if(signo)
+ {
+#if BOOST_AFIO_HAVE_REALTIME_SIGNALS
+ if(-1==signo)
+ {
+ for(signo=SIGRTMIN; signo<SIGRTMAX; signo++)
+ {
+ struct sigaction sigact = { 0 };
+ if(sigaction(signo, nullptr, &sigact)>=0)
+ {
+ if(sigact.sa_handler==SIG_DFL)
+ break;
+ }
+ }
+ }
+#endif
+ // Install process wide signal handler for signal
+ struct sigaction sigact = { 0 };
+ sigact.sa_sigaction=&interrupt_signal_handler;
+ sigact.sa_flags=SA_SIGINFO;
+ sigemptyset(&sigact.sa_mask);
+ if(sigaction(signo, &sigact, &interrupt_signal_handler_old_action)<0)
+ throw std::system_error(errno, std::system_category());
+ interrupt_signal=signo;
+ }
+ return ret;
+}
+
+void io_service::_block_interruption() noexcept
+{
+ if(_use_kqueues)
+ return;
+ assert(!_blocked_interrupt_signal);
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, interrupt_signal);
+ pthread_sigmask(SIG_BLOCK, &set, nullptr);
+ _blocked_interrupt_signal=interrupt_signal;
+ _need_signal=false;
+}
+
+void io_service::_unblock_interruption() noexcept
+{
+ if(_use_kqueues)
+ return;
+ assert(_blocked_interrupt_signal);
+ if(_blocked_interrupt_signal)
+ {
+ sigset_t set;
+ sigemptyset(&set);
+ sigaddset(&set, _blocked_interrupt_signal);
+ pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
+ _blocked_interrupt_signal=0;
+ _need_signal=true;
+ }
+}
+
+io_service::io_service() : _work_queued(0)
+{
+ _threadh = pthread_self();
+#if BOOST_AFIO_USE_POSIX_AIO
+ _use_kqueues=true;
+ _blocked_interrupt_signal=0;
+# if BOOST_AFIO_COMPILE_KQUEUES
+ _kqueueh=0;
+# error todo
+# else
+ disable_kqueues();
+# endif
+#else
+# error todo
+#endif
+}
+
+io_service::~io_service()
+{
+ if (_work_queued)
+ {
+ std::cerr << "WARNING: ~io_service() sees work still queued, blocking until no work queued" << std::endl;
+ while (_work_queued)
+ std::this_thread::yield();
+ }
+#if BOOST_AFIO_USE_POSIX_AIO
+# if BOOST_AFIO_COMPILE_KQUEUES
+ if(_kqueueh)
+ ::close(_kqueueh);
+# endif
+ _aiocbsv.clear();
+ if (pthread_self() == _threadh)
+ _unblock_interruption();
+#else
+# error todo
+#endif
+}
+
+#if BOOST_AFIO_USE_POSIX_AIO
+void io_service::disable_kqueues()
+{
+ if(_use_kqueues)
+ {
+ if(_work_queued)
+ throw std::runtime_error("Cannot disable kqueues if work is pending");
+ if (pthread_self() != _threadh)
+ throw std::runtime_error("Cannot disable kqueues except from owning thread");
+ // Is the global signal handler set yet?
+ if(!interrupt_signal)
+ set_interruption_signal();
+ _use_kqueues=false;
+ // Block interruption on this thread
+ _block_interruption();
+ // Prepare for aio_suspend
+#ifdef AIO_LISTIO_MAX
+ _aiocbsv.reserve(AIO_LISTIO_MAX);
+#else
+ _aiocbsv.reserve(16);
+#endif
+ }
+}
+#endif
+
+result<bool> io_service::run_until(deadline d) noexcept
+{
+ if (!_work_queued)
+ return false;
+ if (pthread_self() != _threadh)
+ return make_errored_result<bool>(EOPNOTSUPP);
+ stl11::chrono::steady_clock::time_point began_steady;
+ stl11::chrono::system_clock::time_point end_utc;
+ if (d)
+ {
+ if (d.steady)
+ began_steady = stl11::chrono::steady_clock::now();
+ else
+ end_utc = d.to_time_point();
+ }
+ struct timespec *ts=nullptr, _ts={0};
+ bool done=false;
+ do
+ {
+ if (d)
+ {
+ stl11::chrono::nanoseconds ns;
+ if (d.steady)
+ ns = stl11::chrono::duration_cast<stl11::chrono::nanoseconds>((began_steady + stl11::chrono::nanoseconds(d.nsecs)) - stl11::chrono::steady_clock::now());
+ else
+ ns = stl11::chrono::duration_cast<stl11::chrono::nanoseconds>(end_utc - stl11::chrono::system_clock::now());
+ ts=&_ts;
+ if (ns.count() <= 0)
+ {
+ ts->tv_sec=0;
+ ts->tv_nsec=0;
+ }
+ else
+ {
+ ts->tv_sec=ns.count()/1000000000ULL;
+ ts->tv_nsec=ns.count()%1000000000ULL;
+ }
+ }
+ bool timedout=false;
+ // Unblock the interruption signal
+ _unblock_interruption();
+ // Execute any pending posts
+ {
+ std::unique_lock<decltype(_posts_lock)> g(_posts_lock);
+ if(!_posts.empty())
+ {
+ post_info *pi=&_posts.front();
+ g.unlock();
+ pi->f(this);
+ _post_done(pi);
+ // We did work, so exit
+ // Block the interruption signal
+ _block_interruption();
+ return _work_queued != 0;
+ }
+ }
+#if BOOST_AFIO_USE_POSIX_AIO
+ int errcode=0;
+ if(_use_kqueues)
+ {
+#if BOOST_AFIO_COMPILE_KQUEUES
+# error todo
+#endif
+ }
+ else
+ {
+ if(aio_suspend(_aiocbsv.data(), _aiocbsv.size(), ts)<0)
+ errcode=errno;
+ }
+ // Block the interruption signal
+ _block_interruption();
+ if(errcode)
+ {
+ switch(errcode)
+ {
+ case EAGAIN:
+ if(d)
+ timedout=true;
+ break;
+ case EINTR:
+ // Let him loop, recalculate any timeout and check for posts to be executed
+ break;
+ default:
+ return make_errored_result<bool>(errcode);
+ }
+ }
+ else
+ {
+ // Poll the outstanding aiocbs to see which are ready
+ for(auto &aiocb : _aiocbsv)
+ {
+ int ioret=aio_return(aiocb);
+ if(ioret>=0 || errno!=EINVAL)
+ {
+ int errcode=ioret<0 ? errno : 0;
+// std::cout << "aiocb " << aiocb << " sees return " << ioret << " errno " << errcode << std::endl;
+ // The aiocb aio_sigevent.sigev_value.sival_ptr field will point to a file_handle::_io_state_type
+ auto io_state=(file_handle::_erased_io_state_type *) aiocb->aio_sigevent.sigev_value.sival_ptr;
+ assert(io_state);
+ (*io_state)(errcode, ioret, &aiocb);
+ }
+ }
+ // Eliminate any empty holes in the quick aiocbs vector
+ _aiocbsv.erase(std::remove(_aiocbsv.begin(), _aiocbsv.end(), nullptr), _aiocbsv.end());
+ done=true;
+ }
+#else
+# error todo
+#endif
+ if(timedout)
+ {
+ if (d.steady)
+ {
+ if(stl11::chrono::steady_clock::now()>=(began_steady + stl11::chrono::nanoseconds(d.nsecs)))
+ return make_errored_result<bool>(ETIMEDOUT);
+ }
+ else
+ {
+ if(stl11::chrono::system_clock::now()>=end_utc)
+ return make_errored_result<bool>(ETIMEDOUT);
+ }
+ }
+ } while(!done);
+ return _work_queued != 0;
+}
+
+void io_service::post(detail::function_ptr<void(io_service *)> &&f)
+{
+ {
+ post_info pi(this, std::move(f));
+ std::lock_guard<decltype(_posts_lock)> g(_posts_lock);
+ _posts.push_back(std::move(pi));
+ }
+ _work_enqueued();
+#if BOOST_AFIO_USE_POSIX_AIO
+ if(_use_kqueues)
+ {
+#if BOOST_AFIO_COMPILE_KQUEUES
+# error todo
+#endif
+ }
+ else
+ {
+ // If run_until() is exactly between the unblock of the signal and the beginning
+ // of the aio_suspend(), we need to pump this until run_until() notices
+ while(_need_signal)
+ {
+//# if BOOST_AFIO_HAVE_REALTIME_SIGNALS
+// sigval val = { 0 };
+// pthread_sigqueue(_threadh, interrupt_signal, val);
+//#else
+ pthread_kill(_threadh, interrupt_signal);
+//# endif
+ }
+ }
+#else
+# error todo
+#endif
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/posix/statfs.ipp b/include/boost/afio/v2/detail/impl/posix/statfs.ipp
new file mode 100644
index 00000000..915552af
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/posix/statfs.ipp
@@ -0,0 +1,164 @@
+/* statfs.hpp
+Information about the volume storing a file
+(C) 2016 Niall Douglas http://www.nedprod.com/
+File Created: Jan 2016
+
+
+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 "../../../statfs.hpp"
+#include "../../../handle.hpp"
+
+#include <sys/mount.h>
+#ifdef __linux__
+# include <sys/statfs.h>
+# include <mntent.h>
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<size_t> statfs_t::fill(handle &h, statfs_t::want wanted) noexcept
+{
+ size_t ret = 0;
+#ifdef __linux__
+ struct statfs64 s={0};
+ if(-1==fstatfs64(h.native_handle().fd, &s))
+ return make_errored_result<size_t>(errno);
+ if(!!(wanted&&want::bsize)) { f_bsize =s.f_bsize; ++ret; }
+ if(!!(wanted&&want::iosize)) { f_iosize =s.f_frsize; ++ret; }
+ if(!!(wanted&&want::blocks)) { f_blocks =s.f_blocks; ++ret; }
+ if(!!(wanted&&want::bfree)) { f_bfree =s.f_bfree; ++ret; }
+ if(!!(wanted&&want::bavail)) { f_bavail =s.f_bavail; ++ret; }
+ if(!!(wanted&&want::files)) { f_files =s.f_files; ++ret; }
+ if(!!(wanted&&want::ffree)) { f_ffree =s.f_ffree; ++ret; }
+ if(!!(wanted&&want::namemax)) { f_namemax =s.f_namelen; ++ret; }
+// if(!!(wanted&&want::owner)) { f_owner =s.f_owner; ++ret; }
+ if(!!(wanted&&want::fsid)) { f_fsid[0]=(unsigned) s.f_fsid.__val[0]; f_fsid[1]=(unsigned) s.f_fsid.__val[1]; ++ret; }
+ if(!!(wanted&&want::flags) || !!(wanted&&want::fstypename) || !!(wanted&&want::mntfromname) || !!(wanted&&want::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)
+ return make_errored_result<size_t>(errno);
+ 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())
+ return make_errored_result<size_t>(ENOENT);
+ // 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(!!(wanted&&want::flags))
+ {
+ f_flags.rdonly =!!(s.f_flags & MS_RDONLY);
+ f_flags.noexec =!!(s.f_flags & MS_NOEXEC);
+ f_flags.nosuid =!!(s.f_flags & MS_NOSUID);
+ f_flags.acls =(std::string::npos!=mountentries.front().first.mnt_opts.find("acl") && std::string::npos==mountentries.front().first.mnt_opts.find("noacl"));
+ 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
+ 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");
+ ++ret;
+ }
+ if(!!(wanted&&want::fstypename)) { f_fstypename =mountentries.front().first.mnt_type; ++ret; }
+ if(!!(wanted&&want::mntfromname)) { f_mntfromname=mountentries.front().first.mnt_fsname; ++ret; }
+ if(!!(wanted&&want::mntonname)) { f_mntonname =mountentries.front().first.mnt_dir; ++ret; }
+ }
+#else
+ struct statfs s;
+ if(-1==fstatfs(h.native_handle().fd, &s))
+ return make_errored_result<size_t>(errno);
+ if(!!(wanted&&want::flags))
+ {
+ f_flags.rdonly =!!(s.f_flags & MNT_RDONLY);
+ f_flags.noexec =!!(s.f_flags & MNT_NOEXEC);
+ f_flags.nosuid =!!(s.f_flags & MNT_NOSUID);
+ f_flags.acls =0;
+#if defined(MNT_ACLS) && defined(MNT_NFS4ACLS)
+ f_flags.acls =!!(s.f_flags & (MNT_ACLS|MNT_NFS4ACLS));
+#endif
+ f_flags.xattr =1; // UFS and ZFS support xattr. TODO FIXME actually calculate this, zfs get xattr <f_mntfromname> would do it.
+ f_flags.compression=!strcmp(s.f_fstypename, "zfs");
+ f_flags.extents =!strcmp(s.f_fstypename, "ufs") || !strcmp(s.f_fstypename, "zfs");
+ ++ret;
+ }
+ if(!!(wanted&&want::bsize)) { f_bsize =s.f_bsize; ++ret; }
+ if(!!(wanted&&want::iosize)) { f_iosize =s.f_iosize; ++ret; }
+ if(!!(wanted&&want::blocks)) { f_blocks =s.f_blocks; ++ret; }
+ if(!!(wanted&&want::bfree)) { f_bfree =s.f_bfree; ++ret; }
+ if(!!(wanted&&want::bavail)) { f_bavail =s.f_bavail; ++ret; }
+ if(!!(wanted&&want::files)) { f_files =s.f_files; ++ret; }
+ if(!!(wanted&&want::ffree)) { f_ffree =s.f_ffree; ++ret; }
+#ifdef __APPLE__
+ if(!!(wanted&&want::namemax)) { f_namemax =255; ++ret; }
+#else
+ if(!!(wanted&&want::namemax)) { f_namemax =s.f_namemax; ++ret; }
+#endif
+ if(!!(wanted&&want::owner)) { f_owner =s.f_owner; ++ret; }
+ if(!!(wanted&&want::fsid)) { f_fsid[0]=(unsigned) s.f_fsid.val[0]; f_fsid[1]=(unsigned) s.f_fsid.val[1]; ++ret; }
+ if(!!(wanted&&want::fstypename)) { f_fstypename =s.f_fstypename; ++ret; }
+ if(!!(wanted&&want::mntfromname)) { f_mntfromname=s.f_mntfromname; ++ret; }
+ if(!!(wanted&&want::mntonname)) { f_mntonname =s.f_mntonname; ++ret; }
+#endif
+ return ret;
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/posix/storage_profile.ipp b/include/boost/afio/v2/detail/impl/posix/storage_profile.ipp
new file mode 100644
index 00000000..a2e062eb
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/posix/storage_profile.ipp
@@ -0,0 +1,300 @@
+/* storage_profile.hpp
+A profile of an OS and filing system
+(C) 2016 Niall Douglas http://www.nedprod.com/
+File Created: Jan 2016
+
+
+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 "../../../storage_profile.hpp"
+#include "../../../handle.hpp"
+
+#include <unistd.h>
+#include <sys/utsname.h> // for uname()
+#include <sys/ioctl.h>
+#if defined(__linux__)
+# include <linux/fs.h>
+#else
+# include <sys/sysctl.h>
+# include <sys/disk.h>
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+namespace storage_profile
+{
+ namespace system
+ {
+ // OS name, version
+ outcome<void> os(storage_profile &sp, file_handle &h) noexcept
+ {
+ static std::string os_name, os_ver;
+ if (!os_name.empty())
+ {
+ sp.os_name.value = os_name;
+ sp.os_ver.value = os_ver;
+ }
+ else
+ {
+ try
+ {
+ struct utsname name = {0};
+ if(uname(&name)<0)
+ return make_errored_outcome<void>(errno);
+ sp.os_name.value = os_name = name.sysname;
+ sp.os_ver.value = os_ver = name.release;
+ }
+ catch (...)
+ {
+ return std::current_exception();
+ }
+ }
+ return make_ready_outcome<void>();
+ }
+
+ // CPU name, architecture, physical cores
+ outcome<void> cpu(storage_profile &sp, file_handle &h) noexcept
+ {
+ static std::string cpu_name, cpu_architecture;
+ static unsigned cpu_physical_cores;
+ if (!cpu_name.empty())
+ {
+ sp.cpu_name.value = cpu_name;
+ sp.cpu_architecture.value = cpu_architecture;
+ sp.cpu_physical_cores.value = cpu_physical_cores;
+ }
+ else
+ {
+ try
+ {
+ struct utsname name = {0};
+ if(uname(&name)<0)
+ return make_errored_outcome<void>(errno);
+ sp.cpu_name.value = sp.cpu_architecture.value = name.machine;
+ sp.cpu_physical_cores.value = 0;
+#if defined(__linux__)
+ {
+ int ih=::open("/proc/cpuinfo", O_RDONLY);
+ if(ih>=0)
+ {
+ char cpuinfo[8192];
+ cpuinfo[::read(ih, cpuinfo, sizeof(cpuinfo)-1)]=0;
+ ::close(ih);
+ /* If siblings > cpu cores hyperthread is enabled:
+ siblings : 8
+ cpu cores : 4
+ */
+ const char *siblings=strstr(cpuinfo, "siblings");
+ const char *cpucores=strstr(cpuinfo, "cpu cores");
+ if(siblings && cpucores)
+ {
+ for(siblings=strchr(siblings, ':'); ' '==*siblings; siblings++);
+ for(cpucores=strchr(cpucores, ':'); ' '==*cpucores; cpucores++);
+ int s=atoi(siblings), c=atoi(cpucores);
+ if(s && c)
+ sp.cpu_physical_cores.value = sysconf( _SC_NPROCESSORS_ONLN ) * c / s;
+ }
+ }
+ }
+#else
+ // Currently only available on OS X
+ {
+ int physicalCores=0;
+ size_t len=sizeof(physicalCores);
+ if(sysctlbyname("hw.physicalcpu", &physicalCores, &len, NULL, 0)>=0)
+ sp.cpu_physical_cores.value=physicalCores;
+ }
+ if(!sp.cpu_physical_cores.value)
+ {
+ char topology[8192];
+ size_t len=sizeof(topology)-1;
+ if(sysctlbyname("kern.sched.topology_spec", topology, &len, NULL, 0)>=0)
+ {
+ topology[len]=0;
+ sp.cpu_physical_cores.value = sysconf( _SC_NPROCESSORS_ONLN );
+ if(strstr(topology, "HTT"))
+ sp.cpu_physical_cores.value /= 2;
+ }
+ }
+#endif
+ // Doesn't account for any hyperthreading
+ if(!sp.cpu_physical_cores.value)
+ sp.cpu_physical_cores.value = sysconf( _SC_NPROCESSORS_ONLN );
+#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+ // We can do a much better CPU name on x86/x64
+ sp.cpu_name.value.clear();
+ auto __cpuid=[](int *cpuInfo, int func)
+ {
+ __asm__ __volatile__ ("cpuid\n\t" : "=a" (cpuInfo[0]), "=b" (cpuInfo[1]), "=c" (cpuInfo[2]), "=d" (cpuInfo[3]) : "0" (func));
+ };
+ {
+ char buffer[62];
+ memset(buffer, 32, 62);
+ int nBuff[4];
+ __cpuid(nBuff, 0);
+ memcpy(buffer+0, nBuff+1, 4);
+ *(int*)(buffer+4) = nBuff[3];
+ *(int*)(buffer+8) = nBuff[2];
+
+ // Do we have a brand string?
+ __cpuid(nBuff, 0x80000000);
+ if ((unsigned)nBuff[0] >= 0x80000004)
+ {
+ __cpuid((int*)&buffer[14], 0x80000002);
+ __cpuid((int*)&buffer[30], 0x80000003);
+ __cpuid((int*)&buffer[46], 0x80000004);
+ }
+ else
+ strcpy(&buffer[14], "unbranded");
+
+ // Trim string
+ for (size_t n = 0; n < 62; n++)
+ {
+ if (!n || buffer[n] != 32 || buffer[n - 1] != 32)
+ if (buffer[n])
+ sp.cpu_name.value.push_back(buffer[n]);
+ }
+ }
+#endif
+ cpu_name = sp.cpu_name.value;
+ cpu_architecture = sp.cpu_architecture.value;
+ cpu_physical_cores = sp.cpu_physical_cores.value;
+ }
+ catch (...)
+ {
+ return std::current_exception();
+ }
+ }
+ return make_ready_outcome<void>();
+ }
+ namespace posix
+ {
+ outcome<void> _mem(storage_profile &sp, file_handle &h) noexcept
+ {
+#if defined(_SC_PHYS_PAGES)
+ size_t physpages=sysconf (_SC_PHYS_PAGES), pagesize=sysconf (_SC_PAGESIZE);
+ sp.mem_quantity.value = (unsigned long long)physpages * pagesize;
+#if defined(_SC_AVPHYS_PAGES)
+ size_t freepages=sysconf (_SC_AVPHYS_PAGES);
+ sp.mem_in_use.value = (float)(physpages - freepages) / physpages;
+#elif defined(HW_USERMEM)
+ unsigned long long freemem=0;
+ size_t len = sizeof(freemem);
+ int mib[2] = { CTL_HW, HW_USERMEM };
+ if (sysctl (mib, 2, &freemem, &len, nullptr, 0) >= 0)
+ {
+ size_t freepages=(size_t)(freemem/pagesize);
+ sp.mem_in_use.value = (float)(physpages - freepages) / physpages;
+ }
+#else
+#error Do not know how to get free physical RAM on this platform
+#endif
+#endif
+ return make_ready_outcome<void>();
+ }
+ }
+ }
+ namespace storage
+ {
+ namespace posix
+ {
+ // Controller type, max transfer, max buffers. Device name, size
+ outcome<void> _device(storage_profile &sp, file_handle &h, std::string mntfromname, std::string fstypename) noexcept
+ {
+ try
+ {
+ // Firstly open a handle to the device
+ if(!strncmp(mntfromname.data(), "/dev", 4))
+ {
+ if(std::isdigit(mntfromname.back()))
+ mntfromname.resize(mntfromname.size()-1);
+ }
+ else
+ {
+ // If the mount point doesn't begin with /dev we can't use that here, so return ENOSYS
+#ifdef __FreeBSD__
+ // If on ZFS and there is exactly one physical disk in the system, use that
+ if(fstypename=="zfs")
+ {
+ char buffer[4096];
+ size_t len=sizeof(buffer);
+ if(sysctlbyname("kern.disks",buffer, &len, NULL, 0)>=0)
+ {
+ mntfromname.clear();
+ // Might be a string like "ada0 cd0 ..."
+ const char *s, *e=buffer;
+ for(; e<buffer+len; e++)
+ {
+ for(s=e; e<buffer+len && *e!=' '; e++);
+ if(s[0]=='c' && s[1]=='d') continue;
+ if(s[0]=='f' && s[1]=='d') continue;
+ if(s[0]=='m' && s[1]=='c' && s[2]=='d') continue;
+ if(s[0]=='s' && s[1]=='c' && s[2]=='d') continue;
+ // Is there more than one physical disk device?
+ if(!mntfromname.empty())
+ return make_errored_outcome<void>(ENOSYS);
+ mntfromname="/dev/"+std::string(s, e-s);
+ }
+ }
+ else
+ return make_errored_outcome<void>(ENOSYS);
+ }
+ else
+#endif
+ return make_errored_outcome<void>(ENOSYS);
+ }
+ BOOST_OUTCOME_FILTER_ERROR(deviceh, file_handle::file(*h.service(), mntfromname, handle::mode::none, handle::creation::open_existing, handle::caching::only_metadata));
+
+ // TODO See https://github.com/baruch/diskscan/blob/master/arch/arch-linux.c
+ // sp.controller_type.value = "SCSI";
+ // sp.controller_max_transfer.value = sad->MaximumTransferLength;
+ // sp.controller_max_buffers.value = sad->MaximumPhysicalPages;
+ // sp.device_name.value.resize(sp.device_name.value.size() - 1);
+
+#ifdef DIOCGMEDIASIZE
+ // BSDs
+ ioctl(deviceh.native_handle().fd, DIOCGMEDIASIZE, &sp.device_size.value);
+#endif
+#ifdef BLKGETSIZE64
+ // Linux
+ ioctl(deviceh.native_handle().fd, BLKGETSIZE64, &sp.device_size.value);
+#endif
+#ifdef DKIOCGETBLOCKCOUNT
+ // OS X
+ ioctl(deviceh.native_handle().fd, DKIOCGETBLOCKCOUNT, &sp.device_size.value);
+#endif
+ }
+ catch (...)
+ {
+ return std::current_exception();
+ }
+ return make_ready_outcome<void>();
+ }
+ }
+ }
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/posix/utils.ipp b/include/boost/afio/v2/detail/impl/posix/utils.ipp
new file mode 100644
index 00000000..6341ccb6
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/posix/utils.ipp
@@ -0,0 +1,166 @@
+/* utils.hpp
+Misc utilities
+(C) 2016 Niall Douglas http://www.nedprod.com/
+File Created: Jan 2015
+
+
+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 "../../../utils.hpp"
+
+#include <sys/mman.h>
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+namespace utils
+{
+ std::vector<size_t> page_sizes(bool only_actually_available) noexcept
+ {
+ static spinlock<bool> lock;
+ static std::vector<size_t> pagesizes, pagesizes_available;
+ stl11::lock_guard<decltype(lock)> g(lock);
+ if (pagesizes.empty())
+ {
+#if 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
+#warning page_sizes() does not know this platform, so assuming getpagesize() is the best available
+ 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)
+ {
+ static spinlock<bool> lock;
+ static int randomfd=-1;
+ if(-1==randomfd)
+ {
+ stl11::lock_guard<decltype(lock)> g(lock);
+ randomfd=::open("/dev/urandom", O_RDONLY);
+ }
+ if(-1==randomfd || ::read(randomfd, buffer, bytes)<bytes)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("afio: Kernel crypto function failed");
+ std::terminate();
+ }
+ }
+
+ namespace detail
+ {
+ large_page_allocation allocate_large_pages(size_t bytes)
+ {
+ large_page_allocation ret(calculate_large_page_allocation(bytes));
+ 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;
+ }
+#ifndef NDEBUG
+ else if(ret.page_size_used>65536)
+ std::cout << "afio: Large page allocation successful" << std::endl;
+#endif
+ return ret;
+ }
+ void deallocate_large_pages(void *p, size_t bytes)
+ {
+ if(munmap(p, bytes)<0)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("afio: Freeing large pages failed");
+ std::terminate();
+ }
+ }
+ }
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/storage_profile.ipp b/include/boost/afio/v2/detail/impl/storage_profile.ipp
new file mode 100644
index 00000000..7fdfaa65
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/storage_profile.ipp
@@ -0,0 +1,584 @@
+/* storage_profile.hpp
+A profile of an OS and filing system
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "../../file_handle.hpp"
+#include "../../statfs.hpp"
+#include "../../storage_profile.hpp"
+#include "../../utils.hpp"
+
+#include <vector>
+
+#define BOOST_AFIO_STORAGE_PROFILE_TIME_DIVIDER 10
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+namespace storage_profile
+{
+
+ /* YAML's syntax is amazingly powerful ... we can express a map
+ of a map to a map using this syntax:
+
+ ?
+ direct: 0
+ sync: 0
+ :
+ concurrency:
+ atomicity:
+ min_atomic_write: 1
+ max_atomic_write: 1
+
+ Some YAML parsers appear to accept this more terse form too:
+
+ {direct: 0, sync: 0}:
+ concurrency:
+ atomicity:
+ min_atomic_write: 1
+ max_atomic_write: 1
+
+ We don't do any of this as some YAML parsers are basically JSON parsers with
+ some rules relaxed. We just use:
+
+ direct=0 sync=0:
+ concurrency:
+ atomicity:
+ min_atomic_write: 1
+ max_atomic_write: 1
+ */
+ void storage_profile::write(std::ostream &out, std::regex which, size_t _indent, bool invert_match) const
+ {
+ std::vector<std::string> lastsection;
+ auto print = [_indent, &out, &lastsection](auto &i) {
+ size_t indent = _indent;
+ if(i.value != default_value<decltype(i.value)>())
+ {
+ std::vector<std::string> thissection;
+ const char *s, *e;
+ for(s = i.name, e = i.name; *e; e++)
+ {
+ if(*e == ':')
+ {
+ thissection.push_back(std::string(s, e - s));
+ s = e + 1;
+ }
+ }
+ std::string name(s, e - s);
+ for(size_t n = 0; n < thissection.size(); n++)
+ {
+ indent += 4;
+ if(n >= lastsection.size() || thissection[n] != lastsection[n])
+ {
+ out << std::string(indent - 4, ' ') << thissection[n] << ":\n";
+ }
+ }
+ if(i.description)
+ {
+ std::string text(i.description);
+ std::vector<std::string> lines;
+ for(;;)
+ {
+ size_t idx = 78;
+ if(idx < text.size())
+ {
+ while(text[idx] != ' ')
+ --idx;
+ }
+ else
+ idx = text.size();
+ lines.push_back(text.substr(0, idx));
+ if(idx < text.size())
+ text = text.substr(idx + 1);
+ else
+ break;
+ }
+ for(auto &line : lines)
+ out << std::string(indent, ' ') << "# " << line << "\n";
+ }
+ out << std::string(indent, ' ') << name << ": " << i.value << "\n";
+ if(i.description && strlen(i.description) > 78)
+ out << "\n";
+ lastsection = std::move(thissection);
+ }
+ };
+ for(const item_erased &i : *this)
+ {
+ bool matches = std::regex_match(i.name, which);
+ if((matches && !invert_match) || (!matches && invert_match))
+ i.invoke(print);
+ }
+ }
+
+ namespace system
+ {
+ namespace detail
+ {
+ // 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;
+ }
+#undef rot
+
+ 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);
+ }
+ }
+ }
+
+ // System memory quantity, in use, max and min bandwidth
+ outcome<void> mem(storage_profile &sp, file_handle &h) noexcept
+ {
+ static unsigned long long mem_quantity, mem_max_bandwidth, mem_min_bandwidth;
+ static float mem_in_use;
+ if(mem_quantity)
+ {
+ sp.mem_quantity.value = mem_quantity;
+ sp.mem_in_use.value = mem_in_use;
+ sp.mem_max_bandwidth.value = mem_max_bandwidth;
+ sp.mem_min_bandwidth.value = mem_min_bandwidth;
+ }
+ else
+ {
+ try
+ {
+ size_t chunksize = 256 * 1024 * 1024;
+#ifdef WIN32
+ BOOST_OUTCOME_PROPAGATE_ERROR(windows::_mem(sp, h));
+#else
+ BOOST_OUTCOME_PROPAGATE_ERROR(posix::_mem(sp, h));
+#endif
+
+ if(sp.mem_quantity.value / 4 < chunksize)
+ chunksize = sp.mem_quantity.value / 4;
+ char *buffer = utils::page_allocator<char>().allocate(chunksize);
+ auto unbuffer = BOOST_AFIO_V2_NAMESPACE::detail::Undoer([buffer, chunksize] { utils::page_allocator<char>().deallocate(buffer, chunksize); });
+ // Make sure all memory is really allocated first
+ memset(buffer, 1, chunksize);
+
+ // Max bandwidth is sequential writes of min(25% of system memory or 256Mb)
+ auto begin = stl11::chrono::high_resolution_clock::now();
+ unsigned long long count;
+ for(count = 0; stl11::chrono::duration_cast<stl11::chrono::seconds>(stl11::chrono::high_resolution_clock::now() - begin).count() < (10 / BOOST_AFIO_STORAGE_PROFILE_TIME_DIVIDER); count++)
+ {
+ memset(buffer, count & 0xff, chunksize);
+ }
+ sp.mem_max_bandwidth.value = (unsigned long long) ((double) count * chunksize / 10);
+
+ // Min bandwidth is randomised 4Kb copies of the same
+ detail::ranctx ctx;
+ detail::raninit(&ctx, 78);
+ begin = stl11::chrono::high_resolution_clock::now();
+ for(count = 0; stl11::chrono::duration_cast<stl11::chrono::seconds>(stl11::chrono::high_resolution_clock::now() - begin).count() < (10 / BOOST_AFIO_STORAGE_PROFILE_TIME_DIVIDER); count++)
+ {
+ for(size_t n = 0; n < chunksize; n += 4096)
+ {
+ auto offset = detail::ranval(&ctx) * 4096;
+ offset = offset % chunksize;
+ memset(buffer + offset, count & 0xff, 4096);
+ }
+ }
+ sp.mem_min_bandwidth.value = (unsigned long long) ((double) count * chunksize / 10);
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+ mem_quantity = sp.mem_quantity.value;
+ mem_in_use = sp.mem_in_use.value;
+ mem_max_bandwidth = sp.mem_max_bandwidth.value;
+ mem_min_bandwidth = sp.mem_min_bandwidth.value;
+ }
+ return make_ready_outcome<void>();
+ }
+ }
+ namespace storage
+ {
+ // Device name, size, min i/o size
+ outcome<void> device(storage_profile &sp, file_handle &h) noexcept
+ {
+ try
+ {
+ statfs_t fsinfo;
+ BOOST_OUTCOME_PROPAGATE_ERROR(fsinfo.fill(h, statfs_t::want::iosize | statfs_t::want::mntfromname | statfs_t::want::fstypename));
+ sp.device_min_io_size.value = (unsigned) fsinfo.f_iosize;
+#ifdef WIN32
+ BOOST_OUTCOME_PROPAGATE_ERROR(windows::_device(sp, h, fsinfo.f_mntfromname, fsinfo.f_fstypename));
+#else
+ BOOST_OUTCOME_PROPAGATE_ERROR(posix::_device(sp, h, fsinfo.f_mntfromname, fsinfo.f_fstypename));
+#endif
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+ return make_ready_outcome<void>();
+ }
+ // FS name, config, size, in use
+ outcome<void> fs(storage_profile &sp, file_handle &h) noexcept
+ {
+ try
+ {
+ statfs_t fsinfo;
+ BOOST_OUTCOME_PROPAGATE_ERROR(fsinfo.fill(h));
+ sp.fs_name.value = fsinfo.f_fstypename;
+ sp.fs_config.value = "todo";
+ sp.fs_size.value = fsinfo.f_blocks * fsinfo.f_bsize;
+ sp.fs_in_use.value = (float) (fsinfo.f_blocks - fsinfo.f_bfree) / fsinfo.f_blocks;
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+ return make_ready_outcome<void>();
+ }
+ }
+
+ namespace concurrency
+ {
+ outcome<void> atomic_rewrite_quantum(storage_profile &sp, file_handle &srch) noexcept
+ {
+ try
+ {
+ using off_t = io_service::extent_type;
+ sp.max_aligned_atomic_rewrite.value = 1;
+ sp.atomic_rewrite_quantum.value = (off_t) -1;
+ for(size_t size = srch.requires_aligned_io() ? 512 : 64; size <= 1 * 1024 * 1024 && size < sp.atomic_rewrite_quantum.value; size = size * 2)
+ {
+ // Create two concurrent writer threads and as many reader threads as additional CPU cores
+ std::vector<std::thread> writers, readers;
+ std::atomic<size_t> done(2);
+ for(char no = '1'; no <= '2'; no++)
+ writers.push_back(std::thread([size, &srch, no, &done] {
+ auto _h(srch.clone());
+ if(!_h)
+ throw std::runtime_error("concurrency::atomic_rewrite_quantum: Could not open work file due to " + _h.get_error().message());
+ file_handle h(std::move(_h.get()));
+ std::vector<char> buffer(size, no);
+ file_handle::const_buffer_type _reqs[1] = {std::make_pair(buffer.data(), size)};
+ file_handle::io_request<file_handle::const_buffers_type> reqs(_reqs, 0);
+ --done;
+ while(done)
+ std::this_thread::yield();
+ while(!done)
+ {
+ h.write(reqs);
+ }
+ }));
+ // Wait till the writers launch
+ while(done)
+ std::this_thread::yield();
+ unsigned concurrency = std::thread::hardware_concurrency() - 2;
+ if(concurrency < 4)
+ concurrency = 4;
+ std::atomic<io_service::extent_type> atomic_rewrite_quantum(sp.atomic_rewrite_quantum.value);
+ std::atomic<bool> failed(false);
+ for(unsigned no = 0; no < concurrency; no++)
+ readers.push_back(std::thread([size, &srch, no, &done, &atomic_rewrite_quantum, &failed] {
+ auto _h(srch.clone());
+ if(!_h)
+ throw std::runtime_error("concurrency::atomic_rewrite_quantum: Could not open work file due to " + _h.get_error().message());
+ file_handle h(std::move(_h.get()));
+ std::vector<char> buffer(size, 0), tocmp(size, 0);
+ file_handle::buffer_type _reqs[1] = {std::make_pair(buffer.data(), size)};
+ file_handle::io_request<file_handle::buffers_type> reqs(_reqs, 0);
+ while(!done)
+ {
+ h.read(reqs);
+ // memset(tocmp.data(), buffer.front(), size);
+ // if (memcmp(buffer.data(), tocmp.data(), size))
+ {
+ const size_t *data = (size_t *) buffer.data(), *end = (size_t *) (buffer.data() + size);
+ for(const size_t *d = data; d < end; d++)
+ {
+ if(*d != *data)
+ {
+ failed = true;
+ off_t failedat = d - data;
+ if(failedat < atomic_rewrite_quantum)
+ {
+ std::cout << " Torn rewrite at offset " << failedat << std::endl;
+ atomic_rewrite_quantum = failedat;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }));
+
+ std::cout << "direct=" << !srch.are_reads_from_cache() << " sync=" << srch.are_writes_durable() << " testing atomicity of rewrites of " << size << " bytes ..." << std::endl;
+ auto begin = stl11::chrono::high_resolution_clock::now();
+ while(!failed && stl11::chrono::duration_cast<stl11::chrono::seconds>(stl11::chrono::high_resolution_clock::now() - begin).count() < (20 / BOOST_AFIO_STORAGE_PROFILE_TIME_DIVIDER))
+ {
+ stl11::this_thread::sleep_for(stl11::chrono::seconds(1));
+ }
+ done = true;
+ for(auto &writer : writers)
+ writer.join();
+ for(auto &reader : readers)
+ reader.join();
+ sp.atomic_rewrite_quantum.value = atomic_rewrite_quantum;
+ if(!failed)
+ {
+ if(size > sp.max_aligned_atomic_rewrite.value)
+ sp.max_aligned_atomic_rewrite.value = size;
+ }
+ else
+ break;
+ }
+ if(sp.atomic_rewrite_quantum.value > sp.max_aligned_atomic_rewrite.value)
+ sp.atomic_rewrite_quantum.value = sp.max_aligned_atomic_rewrite.value;
+
+ // If burst quantum exceeds rewrite quantum, make sure it does so at
+ // offsets not at the front of the file
+ if(sp.max_aligned_atomic_rewrite.value > sp.atomic_rewrite_quantum.value)
+ {
+ size_t size = sp.max_aligned_atomic_rewrite.value;
+ for(off_t offset = sp.max_aligned_atomic_rewrite.value; offset < sp.max_aligned_atomic_rewrite.value * 4; offset += sp.max_aligned_atomic_rewrite.value)
+ {
+ // Create two concurrent writer threads and as many reader threads as additional CPU cores
+ std::vector<std::thread> writers, readers;
+ std::atomic<size_t> done(2);
+ for(char no = '1'; no <= '2'; no++)
+ writers.push_back(std::thread([size, offset, &srch, no, &done] {
+ auto _h(srch.clone());
+ if(!_h)
+ throw std::runtime_error("concurrency::atomic_rewrite_quantum: Could not open work file due to " + _h.get_error().message());
+ file_handle h(std::move(_h.get()));
+ std::vector<char> buffer(size, no);
+ file_handle::const_buffer_type _reqs[1] = {std::make_pair(buffer.data(), size)};
+ file_handle::io_request<file_handle::const_buffers_type> reqs(_reqs, offset);
+ --done;
+ while(done)
+ std::this_thread::yield();
+ while(!done)
+ {
+ h.write(reqs);
+ }
+ }));
+ // Wait till the writers launch
+ while(done)
+ std::this_thread::yield();
+ unsigned concurrency = std::thread::hardware_concurrency() - 2;
+ if(concurrency < 4)
+ concurrency = 4;
+ std::atomic<io_service::extent_type> max_aligned_atomic_rewrite(sp.max_aligned_atomic_rewrite.value);
+ std::atomic<bool> failed(false);
+ for(unsigned no = 0; no < concurrency; no++)
+ readers.push_back(std::thread([size, offset, &srch, no, &done, &max_aligned_atomic_rewrite, &failed] {
+ auto _h(srch.clone());
+ if(!_h)
+ throw std::runtime_error("concurrency::atomic_rewrite_quantum: Could not open work file due to " + _h.get_error().message());
+ file_handle h(std::move(_h.get()));
+ std::vector<char> buffer(size, 0), tocmp(size, 0);
+ file_handle::buffer_type _reqs[1] = {std::make_pair(buffer.data(), size)};
+ file_handle::io_request<file_handle::buffers_type> reqs(_reqs, offset);
+ while(!done)
+ {
+ h.read(reqs);
+ // memset(tocmp.data(), buffer.front(), size);
+ // if (memcmp(buffer.data(), tocmp.data(), size))
+ {
+ const size_t *data = (size_t *) buffer.data(), *end = (size_t *) (buffer.data() + size);
+ for(const size_t *d = data; d < end; d++)
+ {
+ if(*d != *data)
+ {
+ failed = true;
+ off_t failedat = (d - data);
+ if(failedat < max_aligned_atomic_rewrite)
+ {
+ std::cout << " Torn rewrite at offset " << failedat << std::endl;
+ max_aligned_atomic_rewrite = failedat;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }));
+
+ std::cout << "direct=" << !srch.are_reads_from_cache() << " sync=" << srch.are_writes_durable() << " testing atomicity of rewrites of " << size << " bytes to offset " << offset << " ..." << std::endl;
+ auto begin = stl11::chrono::high_resolution_clock::now();
+ while(!failed && stl11::chrono::duration_cast<stl11::chrono::seconds>(stl11::chrono::high_resolution_clock::now() - begin).count() < (20 / BOOST_AFIO_STORAGE_PROFILE_TIME_DIVIDER))
+ {
+ stl11::this_thread::sleep_for(stl11::chrono::seconds(1));
+ }
+ done = true;
+ for(auto &writer : writers)
+ writer.join();
+ for(auto &reader : readers)
+ reader.join();
+ sp.max_aligned_atomic_rewrite.value = max_aligned_atomic_rewrite;
+ if(failed)
+ return make_ready_outcome<void>();
+ }
+ }
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+ return make_ready_outcome<void>();
+ }
+
+ outcome<void> atomic_rewrite_offset_boundary(storage_profile &sp, file_handle &srch) noexcept
+ {
+ try
+ {
+ using off_t = io_service::extent_type;
+ size_t size = sp.max_aligned_atomic_rewrite.value;
+ size_t maxsize = sp.max_aligned_atomic_rewrite.value;
+ if(size > 1024)
+ size = 1024;
+ if(maxsize > 8192)
+ maxsize = 8192;
+ sp.atomic_rewrite_offset_boundary.value = (off_t) -1;
+ if(size > 1)
+ {
+ for(; size <= maxsize; size = size * 2)
+ {
+ for(off_t offset = 512; offset < size; offset += 512)
+ {
+ // Create two concurrent writer threads and as many reader threads as additional CPU cores
+ std::vector<std::thread> writers, readers;
+ std::atomic<size_t> done(2);
+ for(char no = '1'; no <= '2'; no++)
+ writers.push_back(std::thread([size, offset, &srch, no, &done] {
+ auto _h(srch.clone());
+ if(!_h)
+ throw std::runtime_error("concurrency::atomic_rewrite_offset_boundary: Could not open work file due to " + _h.get_error().message());
+ file_handle h(std::move(_h.get()));
+ std::vector<char> buffer(size, no);
+ file_handle::const_buffer_type _reqs[1] = {std::make_pair(buffer.data(), size)};
+ file_handle::io_request<file_handle::const_buffers_type> reqs(_reqs, offset);
+ --done;
+ while(done)
+ std::this_thread::yield();
+ while(!done)
+ {
+ h.write(reqs);
+ }
+ }));
+ // Wait till the writers launch
+ while(done)
+ std::this_thread::yield();
+ unsigned concurrency = std::thread::hardware_concurrency() - 2;
+ if(concurrency < 4)
+ concurrency = 4;
+ std::atomic<io_service::extent_type> atomic_rewrite_offset_boundary(sp.atomic_rewrite_offset_boundary.value);
+ std::atomic<bool> failed(false);
+ for(unsigned no = 0; no < concurrency; no++)
+ readers.push_back(std::thread([size, offset, &srch, no, &done, &atomic_rewrite_offset_boundary, &failed] {
+ auto _h(srch.clone());
+ if(!_h)
+ throw std::runtime_error("concurrency::atomic_rewrite_offset_boundary: Could not open work file due to " + _h.get_error().message());
+ file_handle h(std::move(_h.get()));
+ std::vector<char> buffer(size, 0), tocmp(size, 0);
+ file_handle::buffer_type _reqs[1] = {std::make_pair(buffer.data(), size)};
+ file_handle::io_request<file_handle::buffers_type> reqs(_reqs, offset);
+ while(!done)
+ {
+ h.read(reqs);
+ // memset(tocmp.data(), buffer.front(), size);
+ // if (memcmp(buffer.data(), tocmp.data(), size))
+ {
+ const size_t *data = (size_t *) buffer.data(), *end = (size_t *) (buffer.data() + size);
+ for(const size_t *d = data; d < end; d++)
+ {
+ if(*d != *data)
+ {
+ failed = true;
+ off_t failedat = (d - data) + offset;
+ if(failedat < atomic_rewrite_offset_boundary)
+ {
+ std::cout << " Torn rewrite at offset " << failedat << std::endl;
+ atomic_rewrite_offset_boundary = failedat;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }));
+
+ std::cout << "direct=" << !srch.are_reads_from_cache() << " sync=" << srch.are_writes_durable() << " testing atomicity of rewrites of " << size << " bytes to offset " << offset << " ..." << std::endl;
+ auto begin = stl11::chrono::high_resolution_clock::now();
+ while(!failed && stl11::chrono::duration_cast<stl11::chrono::seconds>(stl11::chrono::high_resolution_clock::now() - begin).count() < (20 / BOOST_AFIO_STORAGE_PROFILE_TIME_DIVIDER))
+ {
+ stl11::this_thread::sleep_for(stl11::chrono::seconds(1));
+ }
+ done = true;
+ for(auto &writer : writers)
+ writer.join();
+ for(auto &reader : readers)
+ reader.join();
+ sp.atomic_rewrite_offset_boundary.value = atomic_rewrite_offset_boundary;
+ if(failed)
+ return make_ready_outcome<void>();
+ }
+ }
+ }
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+ return make_ready_outcome<void>();
+ }
+ }
+}
+BOOST_AFIO_V2_NAMESPACE_END
+
+#ifdef WIN32
+#include "windows/storage_profile.ipp"
+#else
+#include "posix/storage_profile.ipp"
+#endif
diff --git a/include/boost/afio/v2/detail/impl/windows/async_file_handle.ipp b/include/boost/afio/v2/detail/impl/windows/async_file_handle.ipp
new file mode 100644
index 00000000..80142d28
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/windows/async_file_handle.ipp
@@ -0,0 +1,235 @@
+/* handle.hpp
+A handle to a file
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "../../../handle.hpp"
+#include "import.hpp"
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+result<async_file_handle> async_file_handle::clone(io_service &service) const noexcept
+{
+ BOOST_OUTCOME_FILTER_ERROR(v, clone());
+ async_file_handle ret(std::move(v));
+ ret._service = &service;
+ return std::move(ret);
+}
+
+result<async_file_handle> async_file_handle::async_file(io_service &service, async_file_handle::path_type _path, async_file_handle::mode _mode, async_file_handle::creation _creation, async_file_handle::caching _caching, async_file_handle::flag flags) noexcept
+{
+ // Open it overlapped, otherwise no difference.
+ BOOST_OUTCOME_FILTER_ERROR(v, file_handle::file(std::move(_path), std::move(_mode), std::move(_creation), std::move(_caching), flags | flag::overlapped));
+ async_file_handle ret(std::move(v));
+ ret._service = &service;
+ return std::move(ret);
+}
+
+
+template <class CompletionRoutine, class BuffersType, class IORoutine>
+result<async_file_handle::io_state_ptr<CompletionRoutine, BuffersType>> async_file_handle::_begin_io(async_file_handle::operation_t operation, async_file_handle::io_request<BuffersType> reqs, CompletionRoutine &&completion, IORoutine &&ioroutine) noexcept
+{
+ // Need to keep a set of OVERLAPPED matching the scatter-gather buffers
+ struct state_type : public _io_state_type<CompletionRoutine, BuffersType>
+ {
+ OVERLAPPED ols[1];
+ state_type(async_file_handle *_parent, operation_t _operation, CompletionRoutine &&f, size_t _items)
+ : _io_state_type<CompletionRoutine, BuffersType>(_parent, _operation, std::forward<CompletionRoutine>(f), _items)
+ {
+ }
+ virtual void operator()(long errcode, long bytes_transferred, void *internal_state) noexcept override final
+ {
+ LPOVERLAPPED ol = (LPOVERLAPPED) internal_state;
+ ol->hEvent = nullptr;
+ if(this->result)
+ {
+ if(errcode)
+ this->result = make_errored_result<BuffersType>((DWORD) errcode);
+ else
+ {
+ // Figure out which i/o I am and update the buffer in question
+ size_t idx = ol - ols;
+ if(idx >= this->items)
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("async_file_handle::io_state::operator() called with invalid index " << idx);
+ std::terminate();
+ }
+ this->result.value()[idx].second = bytes_transferred;
+ }
+ }
+ this->parent->service()->_work_done();
+ // Are we done?
+ if(!--this->items_to_go)
+ this->completion(this);
+ }
+ virtual ~state_type() override final
+ {
+ // Do we need to cancel pending i/o?
+ if(this->items_to_go)
+ {
+ for(size_t n = 0; n < this->items; n++)
+ {
+ // If this is non-zero, probably this i/o still in flight
+ if(ols[n].hEvent)
+ CancelIoEx(this->parent->native_handle().h, ols + n);
+ }
+ // Pump the i/o service until all pending i/o is completed
+ while(this->items_to_go)
+ {
+ auto res = this->parent->service()->run();
+#ifndef NDEBUG
+ if(res.has_error())
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("async_file_handle: io_service failed due to '" << res.get_error().message() << "'");
+ std::terminate();
+ }
+ if(!res.get())
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("async_file_handle: io_service returns no work when i/o has not completed");
+ std::terminate();
+ }
+#endif
+ }
+ }
+ }
+ } * state;
+ extent_type offset = reqs.offset;
+ size_t statelen = sizeof(state_type) + (reqs.buffers.size() - 1) * sizeof(OVERLAPPED), items(reqs.buffers.size());
+ using return_type = io_state_ptr<CompletionRoutine, BuffersType>;
+ // On Windows i/o must be scheduled on the same thread pumping completion
+ if(GetCurrentThreadId() != service()->_threadid)
+ return make_errored_result<return_type>(EOPNOTSUPP);
+
+ void *mem = ::calloc(1, statelen);
+ if(!mem)
+ return make_errored_result<return_type>(ENOMEM);
+ return_type _state((_io_state_type<CompletionRoutine, BuffersType> *) mem);
+ new((state = (state_type *) mem)) state_type(this, operation, std::forward<CompletionRoutine>(completion), items);
+
+ // To be called once each buffer is read
+ struct handle_completion
+ {
+ static VOID CALLBACK Do(DWORD errcode, DWORD bytes_transferred, LPOVERLAPPED ol)
+ {
+ state_type *state = (state_type *) ol->hEvent;
+ (*state)(errcode, bytes_transferred, ol);
+ }
+ };
+ // Noexcept move the buffers from req into result
+ BuffersType &out = state->result.value();
+ out = std::move(reqs.buffers);
+ for(size_t n = 0; n < items; n++)
+ {
+ LPOVERLAPPED ol = state->ols + n;
+ ol->Internal = (ULONG_PTR) -1;
+ if(_v.is_append_only())
+ ol->OffsetHigh = ol->Offset = 0xffffffff;
+ else
+ {
+ ol->Offset = offset & 0xffffffff;
+ ol->OffsetHigh = (offset >> 32) & 0xffffffff;
+ }
+ // Use the unused hEvent member to pass through the state
+ ol->hEvent = (HANDLE) state;
+ offset += out[n].second;
+ ++state->items_to_go;
+ if(!ioroutine(_v.h, out[n].first, (DWORD) out[n].second, ol, handle_completion::Do))
+ {
+ --state->items_to_go;
+ state->result = make_errored_result<BuffersType>(GetLastError());
+ // Fire completion now if we didn't schedule anything
+ if(!n)
+ state->completion(state);
+ return make_result<return_type>(std::move(_state));
+ }
+ service()->_work_enqueued();
+ }
+ return make_result<return_type>(std::move(_state));
+}
+
+template <class CompletionRoutine> result<async_file_handle::io_state_ptr<CompletionRoutine, async_file_handle::buffers_type>> async_file_handle::async_read(async_file_handle::io_request<async_file_handle::buffers_type> reqs, CompletionRoutine &&completion) noexcept
+{
+ return _begin_io(operation_t::read, std::move(reqs), [completion = std::forward<CompletionRoutine>(completion)](auto *state) { completion(state->parent, state->result); }, ReadFileEx);
+}
+
+template <class CompletionRoutine> result<async_file_handle::io_state_ptr<CompletionRoutine, async_file_handle::const_buffers_type>> async_file_handle::async_write(async_file_handle::io_request<async_file_handle::const_buffers_type> reqs, CompletionRoutine &&completion) noexcept
+{
+ return _begin_io(operation_t::write, std::move(reqs), [completion = std::forward<CompletionRoutine>(completion)](auto *state) { completion(state->parent, state->result); }, WriteFileEx);
+}
+
+async_file_handle::io_result<async_file_handle::buffers_type> async_file_handle::read(async_file_handle::io_request<async_file_handle::buffers_type> reqs, deadline d) noexcept
+{
+ io_result<buffers_type> ret;
+ auto _io_state(_begin_io(operation_t::read, std::move(reqs), [&ret](auto *state) { ret = std::move(state->result); }, ReadFileEx));
+ BOOST_OUTCOME_FILTER_ERROR(io_state, _io_state);
+
+ // While i/o is not done pump i/o completion
+ while(!ret.is_ready())
+ {
+ auto t(_service->run_until(d));
+ // If i/o service pump failed or timed out, cancel outstanding i/o and return
+ if(!t)
+ return make_errored_result<buffers_type>(t.get_error());
+#ifndef NDEBUG
+ if(!ret.is_ready() && t && !t.get())
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("async_file_handle: io_service returns no work when i/o has not completed");
+ std::terminate();
+ }
+#endif
+ }
+ return ret;
+}
+
+async_file_handle::io_result<async_file_handle::const_buffers_type> async_file_handle::write(async_file_handle::io_request<async_file_handle::const_buffers_type> reqs, deadline d) noexcept
+{
+ io_result<const_buffers_type> ret;
+ auto _io_state(_begin_io(operation_t::write, std::move(reqs), [&ret](auto *state) { ret = std::move(state->result); }, WriteFileEx));
+ BOOST_OUTCOME_FILTER_ERROR(io_state, _io_state);
+
+ // While i/o is not done pump i/o completion
+ while(!ret.is_ready())
+ {
+ auto t(_service->run_until(d));
+ // If i/o service pump failed or timed out, cancel outstanding i/o and return
+ if(!t)
+ return make_errored_result<const_buffers_type>(t.get_error());
+#ifndef NDEBUG
+ if(!ret.is_ready() && t && !t.get())
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("async_file_handle: io_service returns no work when i/o has not completed");
+ std::terminate();
+ }
+#endif
+ }
+ return ret;
+}
+
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/windows/child_process.ipp b/include/boost/afio/v2/detail/impl/windows/child_process.ipp
new file mode 100644
index 00000000..017184e4
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/windows/child_process.ipp
@@ -0,0 +1,190 @@
+/* child_process.hpp
+Routines for handling child processes
+(C) 2016 Niall Douglas http://www.nedprod.com/
+File Created: Marc 2016
+
+
+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 "../../child_process.hpp"
+#include "import.hpp"
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+namespace detail
+{
+ child_process::~child_process()
+ {
+ wait();
+ if(_processh)
+ {
+ CloseHandle(_processh.h);
+ _processh.h = nullptr;
+ }
+ if(_readh)
+ {
+ CloseHandle(_readh.h);
+ _readh.h = nullptr;
+ }
+ if(_writeh)
+ {
+ CloseHandle(_writeh.h);
+ _writeh.h = nullptr;
+ }
+ if(_errh)
+ {
+ CloseHandle(_errh.h);
+ _errh.h = nullptr;
+ }
+ }
+
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<child_process> child_process::launch(stl1z::filesystem::path __path, std::vector<stl1z::filesystem::path::string_type> __args, std::map<stl1z::filesystem::path::string_type, stl1z::filesystem::path::string_type> __env) noexcept
+ {
+ using string_type = stl1z::filesystem::path::string_type;
+ using char_type = string_type::value_type;
+ child_process ret(std::move(__path), std::move(__args), std::move(__env));
+
+ STARTUPINFO si = {sizeof(STARTUPINFO)};
+ si.dwFlags = STARTF_USESTDHANDLES;
+ if(!CreatePipe(&si.hStdInput, &ret._writeh.h, nullptr, 0))
+ return make_errored_result<child_process>(GetLastError());
+ auto &unstdinput = Undoer([&si] { CloseHandle(si.hStdInput); });
+ if(!CreatePipe(&ret._readh.h, &si.hStdOutput, nullptr, 0))
+ return make_errored_result<child_process>(GetLastError());
+ auto &unstdoutput = Undoer([&si] { CloseHandle(si.hStdOutput); });
+ if(!CreatePipe(&ret._errh.h, &si.hStdError, nullptr, 0))
+ return make_errored_result<child_process>(GetLastError());
+ auto &unstderr = Undoer([&si] { CloseHandle(si.hStdError); });
+
+ if(!SetHandleInformation(si.hStdInput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
+ return make_errored_result<child_process>(GetLastError());
+ if(!SetHandleInformation(si.hStdOutput, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
+ return make_errored_result<child_process>(GetLastError());
+ if(!SetHandleInformation(si.hStdError, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
+ return make_errored_result<child_process>(GetLastError());
+
+ PROCESS_INFORMATION pi;
+ char_type argsbuffer[32768], *argsbuffere = argsbuffer;
+ for(auto &arg : ret._args)
+ {
+ if(argsbuffere - argsbuffer + arg.size() + 1 >= 32767)
+ return make_errored_result<child_process>(EOVERFLOW);
+ memcpy(argsbuffere, arg.data(), sizeof(char_type) * arg.size());
+ argsbuffere += arg.size();
+ *argsbuffere++ = ' ';
+ }
+ *argsbuffere = 0;
+ char_type envbuffer[32768], *envbuffere = envbuffer;
+ for(auto &env : ret._env)
+ {
+ if(envbuffere - envbuffer + env.first.size() + env.second.size() + 2 >= 32767)
+ return make_errored_result<child_process>(EOVERFLOW);
+ memcpy(envbuffere, env.first.data(), sizeof(char_type) * env.first.size());
+ envbuffere += env.first.size();
+ *envbuffere++ = '=';
+ memcpy(envbuffere, env.first.data(), sizeof(char_type) * (1 + env.first.size()));
+ envbuffere += env.first.size() + 1;
+ }
+ *envbuffere = 0;
+ if(!CreateProcess(ret._path.c_str(), argsbuffer, nullptr, nullptr, true, CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT, envbuffer, nullptr, &si, &pi))
+ return make_errored_result<child_process>(GetLastError());
+
+ ret._processh.h = pi.hProcess;
+ CloseHandle(pi.hThread);
+ return ret;
+ }
+
+ bool child_process::is_running() const noexcept
+ {
+ DWORD retcode = 0;
+ if(!GetExitCodeProcess(_processh.h, &retcode))
+ return false;
+ return retcode == STILL_ACTIVE;
+ }
+
+ result<intptr_t> child_process::wait_until(deadline d) noexcept
+ {
+ BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_INIT(d);
+ for(;;)
+ {
+ BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_LOOP(d);
+ HANDLE hs[2] = {_processh.h, sleep_object};
+ DWORD ret = WaitForMultipleObjectsEx(sleep_object ? 2 : 1, hs, false, sleep_interval, true);
+ switch(ret)
+ {
+ case WAIT_IO_COMPLETION:
+ // loop
+ break;
+ case WAIT_OBJECT_0:
+ {
+ DWORD retcode = 0;
+ if(!GetExitCodeProcess(_processh.h, &retcode))
+ return make_errored_result<intptr_t>(GetLastError());
+ return (intptr_t) retcode;
+ }
+ case WAIT_OBJECT_0 + 1:
+ {
+ // Really a timeout?
+ BOOST_AFIO_WIN_DEADLINE_TO_TIMEOUT(intptr_t, d);
+ break;
+ }
+ default:
+ return make_errored_result<intptr_t>(GetLastError());
+ }
+ }
+ }
+
+ stl1z::filesystem::path current_process_path()
+ {
+ stl1z::filesystem::path::string_type buffer(32768, 0);
+ DWORD len = GetModuleFileName(nullptr, const_cast<stl1z::filesystem::path::string_type::value_type *>(buffer.data()), buffer.size());
+ if(!len)
+ throw std::system_error(GetLastError(), std::system_category());
+ buffer.resize(len);
+ return stl1z::filesystem::path(std::move(buffer));
+ }
+
+ std::map<stl1z::filesystem::path::string_type, stl1z::filesystem::path::string_type> current_process_env()
+ {
+ using string_type = stl1z::filesystem::path::string_type;
+ std::map<string_type, string_type> ret;
+ string_type::value_type *strings = GetEnvironmentStrings();
+ auto &unstrings = Undoer([strings] { FreeEnvironmentStrings(strings); });
+ for(auto *s = strings, *e = strings; *s; s = (e = e + 1))
+ {
+ auto *c = s;
+ for(c = nullptr; *e; e++)
+ {
+ if(!c && *e == '=')
+ c = e;
+ }
+ ret.insert(string_type(s, c - s), string_type(c + 1, e - c - 1));
+ }
+ return ret;
+ }
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/windows/file_handle.ipp b/include/boost/afio/v2/detail/impl/windows/file_handle.ipp
new file mode 100644
index 00000000..42d2ac8b
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/windows/file_handle.ipp
@@ -0,0 +1,96 @@
+/* file_handle.hpp
+A handle to a file
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "../../../file_handle.hpp"
+#include "import.hpp"
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+result<file_handle> file_handle::file(file_handle::path_type _path, file_handle::mode _mode, file_handle::creation _creation, file_handle::caching _caching, file_handle::flag flags) noexcept
+{
+ result<file_handle> ret(file_handle(std::move(_path), native_handle_type(), _caching, flags));
+ native_handle_type &nativeh = ret.get()._v;
+ BOOST_OUTCOME_FILTER_ERROR(access, access_mask_from_handle_mode(nativeh, _mode));
+ DWORD creation = OPEN_EXISTING;
+ switch(_creation)
+ {
+ case creation::open_existing:
+ break;
+ case creation::only_if_not_exist:
+ creation = CREATE_NEW;
+ break;
+ case creation::if_needed:
+ creation = OPEN_ALWAYS;
+ break;
+ case creation::truncate:
+ creation = TRUNCATE_EXISTING;
+ break;
+ }
+ BOOST_OUTCOME_FILTER_ERROR(attribs, attributes_from_handle_caching_and_flags(nativeh, _caching, flags));
+ nativeh.behaviour |= native_handle_type::disposition::file;
+ if(INVALID_HANDLE_VALUE == (nativeh.h = CreateFile(ret.value()._path.c_str(), access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, creation, attribs, NULL)))
+ return make_errored_result<file_handle>(GetLastError());
+ if(_creation == creation::truncate && ret.value().are_safety_fsyncs_issued())
+ FlushFileBuffers(nativeh.h);
+ return ret;
+}
+
+result<file_handle> file_handle::clone() const noexcept
+{
+ result<file_handle> ret(file_handle(_path, native_handle_type(), _caching, _flags));
+ ret.value()._v.behaviour = _v.behaviour;
+ if(!DuplicateHandle(GetCurrentProcess(), _v.h, GetCurrentProcess(), &ret.value()._v.h, 0, false, DUPLICATE_SAME_ACCESS))
+ return make_errored_result<file_handle>(GetLastError());
+ return ret;
+}
+
+result<file_handle::extent_type> file_handle::length() const noexcept
+{
+ FILE_END_OF_FILE_INFO feofi;
+ if(!GetFileInformationByHandleEx(_v.h, FileEndOfFileInfo, &feofi, sizeof(feofi)))
+ return make_errored_result<extent_type>(GetLastError());
+ return feofi.EndOfFile.QuadPart;
+}
+
+result<file_handle::extent_type> file_handle::truncate(file_handle::extent_type newsize) noexcept
+{
+ FILE_END_OF_FILE_INFO feofi;
+ feofi.EndOfFile.QuadPart = newsize;
+ if(!SetFileInformationByHandle(_v.h, FileEndOfFileInfo, &feofi, sizeof(feofi)))
+ return make_errored_result<extent_type>(GetLastError());
+ if(are_safety_fsyncs_issued())
+ {
+ FlushFileBuffers(_v.h);
+ }
+ return newsize;
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/windows/handle.ipp b/include/boost/afio/v2/detail/impl/windows/handle.ipp
new file mode 100644
index 00000000..b693e8d9
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/windows/handle.ipp
@@ -0,0 +1,185 @@
+/* handle.hpp
+A handle to something
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "../../../handle.hpp"
+#include "import.hpp"
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+handle::~handle()
+{
+ if(_v)
+ {
+ // Call close() below
+ auto ret = handle::close();
+ if(ret.has_error())
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("handle::~handle() close failed with " << ret.get_error().message());
+ }
+ }
+}
+
+result<void> handle::close() noexcept
+{
+ if(_v)
+ {
+ if(are_safety_fsyncs_issued())
+ {
+ if(!FlushFileBuffers(_v.h))
+ return make_errored_result<void>(GetLastError());
+ }
+ if(!CloseHandle(_v.h))
+ return make_errored_result<void>(GetLastError());
+ _v = native_handle_type();
+ }
+ return make_result<void>();
+}
+
+result<void> handle::set_append_only(bool enable) noexcept
+{
+ // This works only due to special handling in OVERLAPPED later
+ if(enable)
+ {
+ // Set append_only
+ _v.behaviour |= native_handle_type::disposition::append_only;
+ }
+ else
+ {
+ // Remove append_only
+ _v.behaviour &= ~native_handle_type::disposition::append_only;
+ }
+ return make_result<void>();
+}
+
+result<void> handle::set_kernel_caching(caching caching) noexcept
+{
+ native_handle_type nativeh;
+ handle::mode _mode = mode::none;
+ if(is_append_only())
+ _mode = mode::append;
+ else if(is_writable())
+ _mode = mode::write;
+ else if(is_readable())
+ _mode = mode::read;
+ BOOST_OUTCOME_FILTER_ERROR(access, access_mask_from_handle_mode(nativeh, _mode));
+ BOOST_OUTCOME_FILTER_ERROR(attribs, attributes_from_handle_caching_and_flags(nativeh, caching, _flags));
+ nativeh.behaviour |= native_handle_type::disposition::file;
+ if(INVALID_HANDLE_VALUE == (nativeh.h = ReOpenFile(_v.h, access, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, attribs)))
+ return make_errored_result<void>(GetLastError());
+ _v.swap(nativeh);
+ if(!CloseHandle(nativeh.h))
+ return make_errored_result<void>(GetLastError());
+ return make_result<void>();
+}
+
+template <class BuffersType, class Syscall> inline io_handle::io_result<BuffersType> do_read_write(const native_handle_type &nativeh, Syscall &&syscall, io_handle::io_request<BuffersType> reqs, deadline d) noexcept
+{
+ if(d && !nativeh.is_overlapped())
+ return make_errored_result<BuffersType>(ENOTSUP);
+ if(reqs.buffers.size() > 64)
+ return make_errored_result<BuffersType>(E2BIG);
+
+ BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_INIT(d);
+ std::array<OVERLAPPED, 64> _ols;
+ span<OVERLAPPED> ols(_ols.data(), reqs.buffers.size());
+ auto ol_it = ols.begin();
+ DWORD transferred = 0;
+ auto cancel_io = detail::Undoer([&] {
+ if(nativeh.is_overlapped())
+ {
+ for(auto &ol : ols)
+ {
+ CancelIoEx(nativeh.h, &ol);
+ }
+ for(auto &ol : ols)
+ {
+ ntwait(nativeh.h, ol, deadline());
+ }
+ }
+ });
+ for(auto &req : reqs.buffers)
+ {
+ OVERLAPPED &ol = *ol_it++;
+ ol.Internal = (ULONG_PTR) -1;
+ if(nativeh.is_append_only())
+ ol.OffsetHigh = ol.Offset = 0xffffffff;
+ else
+ {
+ ol.OffsetHigh = (reqs.offset >> 32) & 0xffffffff;
+ ol.Offset = reqs.offset & 0xffffffff;
+ }
+ if(!syscall(nativeh.h, req.first, (DWORD) req.second, &transferred, &ol))
+ return make_errored_result<BuffersType>(GetLastError());
+ reqs.offset += req.second;
+ }
+ // If handle is overlapped, wait for completion of each i/o.
+ if(nativeh.is_overlapped())
+ {
+ for(auto &ol : ols)
+ {
+ deadline nd = d;
+ if(d && d.steady)
+ {
+ stl11::chrono::nanoseconds ns = stl11::chrono::duration_cast<stl11::chrono::nanoseconds>((began_steady + stl11::chrono::nanoseconds(d.nsecs)) - stl11::chrono::steady_clock::now());
+ if(ns.count() < 0)
+ nd.nsecs = 0;
+ else
+ nd.nsecs = ns.count();
+ }
+ if(STATUS_TIMEOUT == ntwait(nativeh.h, ol, nd))
+ {
+ BOOST_AFIO_WIN_DEADLINE_TO_TIMEOUT(BuffersType, d);
+ }
+ }
+ }
+ cancel_io.dismiss();
+ for(size_t n = 0; n < reqs.buffers.size(); n++)
+ {
+ if(ols[n].Internal != 0)
+ {
+ return make_errored_result_nt<BuffersType>((NTSTATUS) ols[n].Internal);
+ }
+ reqs.buffers[n].second = ols[n].InternalHigh;
+ }
+ return io_handle::io_result<BuffersType>(std::move(reqs.buffers));
+}
+
+io_handle::io_result<io_handle::buffers_type> io_handle::read(io_handle::io_request<io_handle::buffers_type> reqs, deadline d) noexcept
+{
+ return do_read_write(_v, &ReadFile, std::move(reqs), std::move(d));
+}
+
+io_handle::io_result<io_handle::const_buffers_type> io_handle::write(io_handle::io_request<io_handle::const_buffers_type> reqs, deadline d) noexcept
+{
+ return do_read_write(_v, &WriteFile, std::move(reqs), std::move(d));
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/windows/import.hpp b/include/boost/afio/v2/detail/impl/windows/import.hpp
new file mode 100644
index 00000000..867a8cd3
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/windows/import.hpp
@@ -0,0 +1,1031 @@
+/* windows.hpp
+Declarations for Microsoft Windows system APIs
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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_WINDOWS_H
+#define BOOST_AFIO_WINDOWS_H
+
+#include "../../../handle.hpp"
+#include <memory> // for unique_ptr
+
+#ifndef WIN32
+#error You should not include windows/import.hpp on not Windows platforms
+#endif
+
+// At some future point we will not do this, and instead import symbols manually
+// to avoid the windows.h inclusion
+#if 1
+#define WIN32_LEAN_AND_MEAN 1
+#include "windows.h"
+
+#else
+#error todo
+#endif
+
+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);
+
+ typedef NTSTATUS(NTAPI *NtDelayExecution_t)(
+ /*_In_*/ BOOLEAN Alertable,
+ /*_In_*/ LARGE_INTEGER *Interval);
+
+ // 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 NtDelayExecution_t NtDelayExecution;
+ 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 : 4706) // assignment within conditional
+#pragma warning(disable : 6387) // MSVC sanitiser warns that GetModuleHandleA() might fail (hah!)
+#endif
+ static inline void doinit()
+ {
+ if(RtlCaptureStackBackTrace)
+ return;
+ static stl11::mutex lock;
+ stl11::lock_guard<decltype(lock)> g(lock);
+ static HMODULE ntdllh = GetModuleHandleA("NTDLL.DLL");
+ if(!NtQueryObject)
+ if(!(NtQueryObject = (NtQueryObject_t) GetProcAddress(ntdllh, "NtQueryObject")))
+ abort();
+ if(!NtQueryInformationFile)
+ if(!(NtQueryInformationFile = (NtQueryInformationFile_t) GetProcAddress(ntdllh, "NtQueryInformationFile")))
+ abort();
+ if(!NtQueryVolumeInformationFile)
+ if(!(NtQueryVolumeInformationFile = (NtQueryVolumeInformationFile_t) GetProcAddress(ntdllh, "NtQueryVolumeInformationFile")))
+ abort();
+ if(!NtOpenDirectoryObject)
+ if(!(NtOpenDirectoryObject = (NtOpenDirectoryObject_t) GetProcAddress(ntdllh, "NtOpenDirectoryObject")))
+ abort();
+ if(!NtOpenFile)
+ if(!(NtOpenFile = (NtOpenFile_t) GetProcAddress(ntdllh, "NtOpenFile")))
+ abort();
+ if(!NtCreateFile)
+ if(!(NtCreateFile = (NtCreateFile_t) GetProcAddress(ntdllh, "NtCreateFile")))
+ abort();
+ if(!NtDeleteFile)
+ if(!(NtDeleteFile = (NtDeleteFile_t) GetProcAddress(ntdllh, "NtDeleteFile")))
+ abort();
+ if(!NtClose)
+ if(!(NtClose = (NtClose_t) GetProcAddress(ntdllh, "NtClose")))
+ abort();
+ if(!NtQueryDirectoryFile)
+ if(!(NtQueryDirectoryFile = (NtQueryDirectoryFile_t) GetProcAddress(ntdllh, "NtQueryDirectoryFile")))
+ abort();
+ if(!NtSetInformationFile)
+ if(!(NtSetInformationFile = (NtSetInformationFile_t) GetProcAddress(ntdllh, "NtSetInformationFile")))
+ abort();
+ if(!NtWaitForSingleObject)
+ if(!(NtWaitForSingleObject = (NtWaitForSingleObject_t) GetProcAddress(ntdllh, "NtWaitForSingleObject")))
+ abort();
+ if(!NtDelayExecution)
+ if(!(NtDelayExecution = (NtDelayExecution_t) GetProcAddress(ntdllh, "NtDelayExecution")))
+ abort();
+ if(!NtLockFile)
+ if(!(NtLockFile = (NtLockFile_t) GetProcAddress(ntdllh, "NtLockFile")))
+ abort();
+ if(!NtUnlockFile)
+ if(!(NtUnlockFile = (NtUnlockFile_t) GetProcAddress(ntdllh, "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();
+#ifdef BOOST_AFIO_OP_STACKBACKTRACEDEPTH
+ if(dbghelp)
+ {
+ HMODULE dbghelp = LoadLibraryA("DBGHELP.DLL");
+ 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(ntdllh, "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 stl1z::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 stl1z::filesystem::file_type::symlink;
+ // return stl1z::filesystem::file_type::reparse_file;
+ else if(FileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ return stl1z::filesystem::file_type::directory;
+ else
+ return stl1z::filesystem::file_type::regular;
+#endif
+ }
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 6326) // comparison of constants
+#endif
+ static inline stl11::chrono::system_clock::time_point to_timepoint(LARGE_INTEGER time)
+ {
+ // For speed we make the big assumption that the STL's system_clock is based on the time_t epoch 1st Jan 1970.
+ static constexpr unsigned long long FILETIME_OFFSET_TO_1970 = ((27111902ULL << 32U) + 3577643008ULL);
+ // Need to have this self-adapt to the STL being used
+ static constexpr unsigned long long STL_TICKS_PER_SEC = (unsigned long long) stl11::chrono::system_clock::period::den / stl11::chrono::system_clock::period::num;
+ static constexpr unsigned long long multiplier = STL_TICKS_PER_SEC >= 10000000ULL ? STL_TICKS_PER_SEC / 10000000ULL : 1;
+ static constexpr 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
+ stl11::chrono::system_clock::duration duration(ticks_since_1970 * multiplier / divider);
+ return stl11::chrono::system_clock::time_point(duration);
+ }
+ static inline LARGE_INTEGER from_timepoint(stl11::chrono::system_clock::time_point time)
+ {
+ // For speed we make the big assumption that the STL's system_clock is based on the time_t epoch 1st Jan 1970.
+ static constexpr unsigned long long FILETIME_OFFSET_TO_1970 = ((27111902ULL << 32U) + 3577643008ULL);
+ static const stl11::chrono::system_clock::time_point time_point_1970 = stl11::chrono::system_clock::from_time_t(0);
+
+ LARGE_INTEGER ret;
+ ret.QuadPart = FILETIME_OFFSET_TO_1970 + stl11::chrono::duration_cast<stl11::chrono::nanoseconds>(time - time_point_1970).count() / 100;
+ return ret;
+ }
+#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 DWORD win32_error_from_nt_status(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);
+ return GetLastError();
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+} // namespace
+
+// disable to prevent accidental usage
+template <class T> inline result<T> make_errored_result(NTSTATUS e, const char *extended = nullptr)
+{
+ static_assert(!std::is_same<T, T>::value, "Use make_errored_result_nt<T>(NTSTATUS).");
+}
+template <class T> inline outcome<T> make_errored_outcome(NTSTATUS e, const char *extended = nullptr)
+{
+ static_assert(!std::is_same<T, T>::value, "Use make_errored_outcome_nt<T>(NTSTATUS).");
+}
+template <class T> inline result<T> make_errored_result_nt(NTSTATUS e, const char *extended = nullptr)
+{
+ return result<T>(std::error_code(windows_nt_kernel::win32_error_from_nt_status(e), std::system_category()));
+}
+template <class T> inline outcome<T> make_errored_outcome_nt(NTSTATUS e, const char *extended = nullptr)
+{
+ return outcome<T>(std::error_code(windows_nt_kernel::win32_error_from_nt_status(e), std::system_category()));
+}
+
+#if 0
+static inline void fill_stat_t(stat_t &stat, BOOST_AFIO_POSIX_STAT_STRUCT s, metadata_flags wanted)
+{
+#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; }
+}
+#endif
+
+// Utility routines for implementing deadline sleeps on Windows which only provides interval sleeps
+#if 0 // This is the win32 edition. The NT kernel edition is much cleaner and lower overhead.
+struct win_handle_deleter
+{
+ void operator()(HANDLE h) { CloseHandle(h); }
+};
+static inline std::unique_ptr<void, win_handle_deleter> create_waitable_timer()
+{
+ HANDLE ret = CreateWaitableTimer(nullptr, true, nullptr);
+ if(INVALID_HANDLE_VALUE == ret)
+ throw std::system_error(GetLastError(), std::system_category());
+ return std::unique_ptr<void, win_handle_deleter>(ret);
+}
+static inline HANDLE get_thread_local_waitable_timer()
+{
+ static thread_local auto self = create_waitable_timer();
+ return self.get();
+}
+
+/*! Defines a number of variables into its scope:
+- began_steady: Set to the steady clock at the beginning of a sleep
+- end_utc: Set to the system clock when the sleep must end
+- sleep_interval: Set to the number of steady milliseconds until the sleep must end
+- sleep_object: Set to a primed deadline timer HANDLE which will signal when the system clock reaches the deadline
+*/
+#define BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_INIT(d) \
+ stl11::chrono::steady_clock::time_point began_steady; \
+ \
+stl11::chrono::system_clock::time_point end_utc; \
+ \
+if(d) \
+ \
+{ \
+ if((d).steady) \
+ began_steady = stl11::chrono::steady_clock::now(); \
+ else \
+ end_utc = (d).to_time_point(); \
+ \
+} \
+ \
+DWORD sleep_interval = INFINITE; \
+ \
+HANDLE sleep_object = nullptr;
+
+#define BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_LOOP(d) \
+ \
+if(d) \
+ \
+{ \
+ if((d).steady) \
+ { \
+ stl11::chrono::milliseconds ms; \
+ ms = stl11::chrono::duration_cast<stl11::chrono::milliseconds>((began_steady + stl11::chrono::nanoseconds(d.nsecs)) - stl11::chrono::steady_clock::now()); \
+ if(ms.count() < 0) \
+ sleep_interval = 0; \
+ else \
+ sleep_interval = (DWORD) ms.count(); \
+ } \
+ else \
+ { \
+ sleep_object = get_thread_local_waitable_timer(); \
+ LARGE_INTEGER due_time = windows_nt_kernel::from_timepoint(end_utc); \
+ if(!SetWaitableTimer(sleep_object, &due_time, 0, nullptr, nullptr, false)) \
+ throw std::system_error(GetLastError(), std::system_category()); \
+ } \
+ \
+}
+
+#define BOOST_AFIO_WIN_DEADLINE_TO_TIMEOUT(type, d) \
+ \
+if(d) \
+ \
+{ \
+ if((d).steady) \
+ { \
+ if(stl11::chrono::steady_clock::now() >= (began_steady + stl11::chrono::nanoseconds((d).nsecs))) \
+ return make_errored_result<type>(ETIMEDOUT); \
+ } \
+ else \
+ { \
+ if(stl11::chrono::system_clock::now() >= end_utc) \
+ return make_errored_result<type>(ETIMEDOUT); \
+ } \
+ \
+}
+#else
+/*! Defines a number of variables into its scope:
+- began_steady: Set to the steady clock at the beginning of a sleep
+- end_utc: Set to the system clock when the sleep must end
+- sleep_interval: Set to the number of steady milliseconds until the sleep must end
+- sleep_object: Set to a primed deadline timer HANDLE which will signal when the system clock reaches the deadline
+*/
+#define BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_INIT(d) \
+ stl11::chrono::steady_clock::time_point began_steady; \
+ \
+stl11::chrono::system_clock::time_point end_utc; \
+ \
+alignas(8) LARGE_INTEGER _timeout; \
+ \
+LARGE_INTEGER *timeout = nullptr; \
+ \
+if(d) \
+ \
+{ \
+ if((d).steady) \
+ began_steady = stl11::chrono::steady_clock::now(); \
+ else \
+ { \
+ end_utc = (d).to_time_point(); \
+ _timeout = windows_nt_kernel::from_timepoint(end_utc); \
+ } \
+ timeout = &_timeout; \
+ \
+}
+
+#define BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_LOOP(d) \
+ if((d) && (d).steady) \
+ { \
+ stl11::chrono::nanoseconds ns; \
+ ns = stl11::chrono::duration_cast<stl11::chrono::nanoseconds>((began_steady + stl11::chrono::nanoseconds(d.nsecs)) - stl11::chrono::steady_clock::now()); \
+ if(ns.count() < 0) \
+ _timeout.QuadPart = 0; \
+ else \
+ _timeout.QuadPart = ns.count() / -100; \
+ }
+
+#define BOOST_AFIO_WIN_DEADLINE_TO_TIMEOUT(type, d) \
+ \
+if(d) \
+ \
+{ \
+ if((d).steady) \
+ { \
+ if(stl11::chrono::steady_clock::now() >= (began_steady + stl11::chrono::nanoseconds((d).nsecs))) \
+ return make_errored_result<type>(ETIMEDOUT); \
+ } \
+ else \
+ { \
+ if(stl11::chrono::system_clock::now() >= end_utc) \
+ return make_errored_result<type>(ETIMEDOUT); \
+ } \
+ \
+}
+#endif
+
+// Wait for an overlapped handle to complete a specific operation
+static inline NTSTATUS ntwait(HANDLE h, windows_nt_kernel::IO_STATUS_BLOCK &isb, const deadline &d) noexcept
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_INIT(d);
+ do // needs to be a do, not while in order to flip auto reset event objects etc.
+ {
+ BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_LOOP(d);
+ // Pump alerts and APCs
+ NTSTATUS ntstat = NtWaitForSingleObject(h, true, timeout);
+ if(STATUS_TIMEOUT == ntstat)
+ {
+ DWORD expected = (DWORD) -1;
+ // Have to be very careful here, atomically swap timed out for the -1 only
+ InterlockedCompareExchange(&isb.Status, ntstat, expected);
+ // If it's no longer -1 or the i/o completes, that's fine.
+ return isb.Status;
+ }
+ } while(isb.Status == -1);
+ return isb.Status;
+}
+static inline NTSTATUS ntwait(HANDLE h, OVERLAPPED &ol, const deadline &d) noexcept
+{
+ return ntwait(h, reinterpret_cast<windows_nt_kernel::IO_STATUS_BLOCK &>(ol), d);
+}
+
+// Sleep the thread until some deadline
+static inline bool ntsleep(const deadline &d, bool return_on_alert = false) noexcept
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_INIT(d);
+ for(;;)
+ {
+ BOOST_AFIO_WIN_DEADLINE_TO_SLEEP_LOOP(d);
+ // Pump alerts and APCs
+ NTSTATUS ntstat = NtDelayExecution(true, timeout);
+ if((d).steady)
+ {
+ if(stl11::chrono::steady_clock::now() >= (began_steady + stl11::chrono::nanoseconds((d).nsecs)))
+ return false;
+ }
+ else
+ {
+ if(stl11::chrono::system_clock::now() >= end_utc)
+ return false;
+ }
+ if(return_on_alert)
+ return true;
+ }
+}
+
+
+// Utility routines for building an ACCESS_MASK from a handle::mode
+static inline result<ACCESS_MASK> access_mask_from_handle_mode(native_handle_type &nativeh, handle::mode _mode)
+{
+ ACCESS_MASK access = SYNCHRONIZE;
+ switch(_mode)
+ {
+ case handle::mode::unchanged:
+ return make_errored_result<ACCESS_MASK>(EINVAL);
+ case handle::mode::none:
+ break;
+ case handle::mode::attr_read:
+ access |= FILE_READ_ATTRIBUTES;
+ break;
+ case handle::mode::attr_write:
+ access |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES;
+ break;
+ case handle::mode::read:
+ access |= GENERIC_READ;
+ nativeh.behaviour |= native_handle_type::disposition::seekable | native_handle_type::disposition::readable;
+ break;
+ case handle::mode::write:
+ access |= GENERIC_WRITE | GENERIC_READ;
+ nativeh.behaviour |= native_handle_type::disposition::seekable | native_handle_type::disposition::readable | native_handle_type::disposition::writable;
+ break;
+ case handle::mode::append:
+ access |= FILE_APPEND_DATA;
+ nativeh.behaviour |= native_handle_type::disposition::writable | native_handle_type::disposition::append_only;
+ break;
+ }
+ return access;
+}
+
+static inline result<DWORD> attributes_from_handle_caching_and_flags(native_handle_type &nativeh, handle::caching _caching, handle::flag flags)
+{
+ DWORD attribs = 0;
+ if(flags && handle::flag::overlapped)
+ {
+ attribs |= FILE_FLAG_OVERLAPPED;
+ nativeh.behaviour |= native_handle_type::disposition::overlapped;
+ }
+ switch(_caching)
+ {
+ case handle::caching::unchanged:
+ return make_errored_result<DWORD>(EINVAL);
+ case handle::caching::none:
+ attribs |= FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH;
+ nativeh.behaviour |= native_handle_type::disposition::aligned_io;
+ break;
+ case handle::caching::only_metadata:
+ attribs |= FILE_FLAG_NO_BUFFERING;
+ nativeh.behaviour |= native_handle_type::disposition::aligned_io;
+ break;
+ case handle::caching::reads:
+ case handle::caching::reads_and_metadata:
+ attribs |= FILE_FLAG_WRITE_THROUGH;
+ break;
+ case handle::caching::all:
+ case handle::caching::safety_fsyncs:
+ break;
+ case handle::caching::temporary:
+ attribs |= FILE_ATTRIBUTE_TEMPORARY;
+ break;
+ }
+ if(flags && handle::flag::delete_on_close)
+ attribs |= FILE_FLAG_DELETE_ON_CLOSE;
+ return attribs;
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#endif
diff --git a/include/boost/afio/v2/detail/impl/windows/io_service.ipp b/include/boost/afio/v2/detail/impl/windows/io_service.ipp
new file mode 100644
index 00000000..ccf70b53
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/windows/io_service.ipp
@@ -0,0 +1,89 @@
+/* io_service.hpp
+Multiplex file i/o
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "../../../io_service.hpp"
+#include "import.hpp"
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+io_service::io_service()
+ : _work_queued(0)
+{
+ if(!DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &_threadh, 0, false, DUPLICATE_SAME_ACCESS))
+ throw std::runtime_error("Failed to create creating thread handle");
+ _threadid = GetCurrentThreadId();
+}
+
+io_service::~io_service()
+{
+ if(_work_queued)
+ {
+ std::cerr << "WARNING: ~io_service() sees work still queued, blocking until no work queued" << std::endl;
+ while(_work_queued)
+ std::this_thread::yield();
+ }
+ CloseHandle(_threadh);
+}
+
+result<bool> io_service::run_until(deadline d) noexcept
+{
+ if(!_work_queued)
+ return false;
+ if(GetCurrentThreadId() != _threadid)
+ return make_errored_result<bool>(EOPNOTSUPP);
+ ntsleep(d, true);
+ return _work_queued != 0;
+}
+
+void io_service::post(detail::function_ptr<void(io_service *)> &&f)
+{
+ void *data = nullptr;
+ {
+ post_info pi(this, std::move(f));
+ std::lock_guard<decltype(_posts_lock)> g(_posts_lock);
+ _posts.push_back(std::move(pi));
+ data = (void *) &_posts.back();
+ }
+ PAPCFUNC apcf = [](ULONG_PTR data) {
+ post_info *pi = (post_info *) data;
+ pi->f(pi->service);
+ pi->service->_post_done(pi);
+ };
+ if(QueueUserAPC(apcf, _threadh, (ULONG_PTR) data))
+ _work_enqueued();
+ else
+ {
+ post_info *pi = (post_info *) data;
+ pi->service->_post_done(pi);
+ }
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/windows/statfs.ipp b/include/boost/afio/v2/detail/impl/windows/statfs.ipp
new file mode 100644
index 00000000..ea9ed186
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/windows/statfs.ipp
@@ -0,0 +1,183 @@
+/* statfs.hpp
+Information about the volume storing a file
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "../../../handle.hpp"
+#include "../../../statfs.hpp"
+#include "import.hpp"
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<size_t> statfs_t::fill(handle &h, statfs_t::want wanted) noexcept
+{
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ alignas(8) fixme_path::value_type buffer[32769];
+ IO_STATUS_BLOCK isb = {{-1}};
+ NTSTATUS ntstat;
+ size_t ret = 0;
+ if((wanted && want::flags) || (wanted && want::namemax) || (wanted && want::fstypename))
+ {
+ FILE_FS_ATTRIBUTE_INFORMATION *ffai = (FILE_FS_ATTRIBUTE_INFORMATION *) buffer;
+ isb.Status = -1;
+ ntstat = NtQueryVolumeInformationFile(h.native_handle().h, &isb, ffai, sizeof(buffer), FileFsAttributeInformation);
+ if(STATUS_PENDING == ntstat)
+ ntstat = ntwait(h.native_handle().h, isb, deadline());
+ if(ntstat)
+ return make_errored_result_nt<size_t>(ntstat);
+ if(wanted && want::flags)
+ {
+ f_flags.rdonly = !!(ffai->FileSystemAttributes & FILE_READ_ONLY_VOLUME);
+ f_flags.acls = !!(ffai->FileSystemAttributes & FILE_PERSISTENT_ACLS);
+ f_flags.xattr = !!(ffai->FileSystemAttributes & FILE_NAMED_STREAMS);
+ f_flags.compression = !!(ffai->FileSystemAttributes & FILE_VOLUME_IS_COMPRESSED);
+ f_flags.extents = !!(ffai->FileSystemAttributes & FILE_SUPPORTS_SPARSE_FILES);
+ f_flags.filecompression = !!(ffai->FileSystemAttributes & FILE_FILE_COMPRESSION);
+ ++ret;
+ }
+ if(wanted && want::namemax)
+ {
+ f_namemax = ffai->MaximumComponentNameLength;
+ ++ret;
+ }
+ if(wanted && want::fstypename)
+ {
+ f_fstypename.resize(ffai->FileSystemNameLength / sizeof(fixme_path::value_type));
+ for(size_t n = 0; n < ffai->FileSystemNameLength / sizeof(fixme_path::value_type); n++)
+ f_fstypename[n] = (char) ffai->FileSystemName[n];
+ ++ret;
+ }
+ }
+ if((wanted && want::bsize) || (wanted && want::blocks) || (wanted && want::bfree) || (wanted && want::bavail))
+ {
+ FILE_FS_FULL_SIZE_INFORMATION *fffsi = (FILE_FS_FULL_SIZE_INFORMATION *) buffer;
+ isb.Status = -1;
+ ntstat = NtQueryVolumeInformationFile(h.native_handle().h, &isb, fffsi, sizeof(buffer), FileFsFullSizeInformation);
+ if(STATUS_PENDING == ntstat)
+ ntstat = ntwait(h.native_handle().h, isb, deadline());
+ if(ntstat)
+ return make_errored_result_nt<size_t>(ntstat);
+ if(wanted && want::bsize)
+ {
+ f_bsize = fffsi->BytesPerSector * fffsi->SectorsPerAllocationUnit;
+ ++ret;
+ }
+ if(wanted && want::blocks)
+ {
+ f_blocks = fffsi->TotalAllocationUnits.QuadPart;
+ ++ret;
+ }
+ if(wanted && want::bfree)
+ {
+ f_bfree = fffsi->ActualAvailableAllocationUnits.QuadPart;
+ ++ret;
+ }
+ if(wanted && want::bavail)
+ {
+ f_bavail = fffsi->CallerAvailableAllocationUnits.QuadPart;
+ ++ret;
+ }
+ }
+ if(wanted && want::fsid)
+ {
+ FILE_FS_OBJECTID_INFORMATION *ffoi = (FILE_FS_OBJECTID_INFORMATION *) buffer;
+ isb.Status = -1;
+ ntstat = NtQueryVolumeInformationFile(h.native_handle().h, &isb, ffoi, sizeof(buffer), FileFsObjectIdInformation);
+ if(STATUS_PENDING == ntstat)
+ ntstat = ntwait(h.native_handle().h, isb, deadline());
+ if(0 /*STATUS_SUCCESS*/ == ntstat)
+ {
+ // FAT32 doesn't support filing system id, so sink error
+ memcpy(&f_fsid, ffoi->ObjectId, sizeof(f_fsid));
+ ++ret;
+ }
+ }
+ if(wanted && want::iosize)
+ {
+ FILE_FS_SECTOR_SIZE_INFORMATION *ffssi = (FILE_FS_SECTOR_SIZE_INFORMATION *) buffer;
+ isb.Status = -1;
+ ntstat = NtQueryVolumeInformationFile(h.native_handle().h, &isb, ffssi, sizeof(buffer), FileFsSectorSizeInformation);
+ if(STATUS_PENDING == ntstat)
+ ntstat = ntwait(h.native_handle().h, isb, deadline());
+ if(ntstat)
+ return make_errored_result_nt<size_t>(ntstat);
+ f_iosize = ffssi->PhysicalBytesPerSectorForPerformance;
+ ++ret;
+ }
+ if((wanted && want::mntfromname) || (wanted && want::mntonname))
+ {
+ // Irrespective we need the path before figuring out the mounted device
+ alignas(8) fixme_path::value_type buffer2[32769];
+ for(;;)
+ {
+ DWORD pathlen = GetFinalPathNameByHandle(h.native_handle().h, buffer2, sizeof(buffer2) / sizeof(*buffer2), FILE_NAME_OPENED | VOLUME_NAME_NONE);
+ if(!pathlen || pathlen >= sizeof(buffer2) / sizeof(*buffer2))
+ return make_errored_result<size_t>(GetLastError());
+ buffer2[pathlen] = 0;
+ if(wanted && want::mntfromname)
+ {
+ DWORD len = GetFinalPathNameByHandle(h.native_handle().h, buffer, sizeof(buffer) / sizeof(*buffer), FILE_NAME_OPENED | VOLUME_NAME_NT);
+ if(!len || len >= sizeof(buffer) / sizeof(*buffer))
+ return make_errored_result<size_t>(GetLastError());
+ buffer[len] = 0;
+ if(memcmp(buffer2, buffer + len - pathlen, pathlen))
+ continue; // path changed
+ len -= pathlen;
+ // buffer should look like \Device\HarddiskVolumeX
+ if(memcmp(buffer, L"\\Device\\HarddiskVolume", 44))
+ return make_errored_result<size_t>(EILSEQ);
+ // TODO FIXME This should output the kind of path the input handle uses
+ // For now though, output a win32 compatible path
+ f_mntfromname.reserve(len + 3);
+ f_mntfromname.assign("\\\\."); // win32 escape prefix for NT kernel path \Device
+ for(size_t n = 7; n < len; n++)
+ f_mntfromname.push_back((char) buffer[n]);
+ ++ret;
+ wanted &= ~want::mntfromname;
+ }
+ if(wanted && want::mntonname)
+ {
+ DWORD len = GetFinalPathNameByHandle(h.native_handle().h, buffer, sizeof(buffer) / sizeof(*buffer), FILE_NAME_OPENED | VOLUME_NAME_DOS);
+ if(!len || len >= sizeof(buffer) / sizeof(*buffer))
+ return make_errored_result<size_t>(GetLastError());
+ buffer[len] = 0;
+ if(memcmp(buffer2, buffer + len - pathlen, pathlen))
+ continue; // path changed
+ len -= pathlen;
+ f_mntonname = decltype(f_mntonname)::string_type(buffer, len);
+ ++ret;
+ }
+ break;
+ }
+ }
+ return ret;
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/windows/storage_profile.ipp b/include/boost/afio/v2/detail/impl/windows/storage_profile.ipp
new file mode 100644
index 00000000..615ff361
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/windows/storage_profile.ipp
@@ -0,0 +1,374 @@
+/* storage_profile.hpp
+A profile of an OS and filing system
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "../../../handle.hpp"
+#include "../../../storage_profile.hpp"
+#include "import.hpp"
+
+#include <winioctl.h>
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+namespace storage_profile
+{
+ namespace system
+ {
+// OS name, version
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 6387) // MSVC sanitiser warns that GetModuleHandleA() might fail (hah!)
+#endif
+ outcome<void> os(storage_profile &sp, file_handle &h) noexcept
+ {
+ static std::string os_name, os_ver;
+ if(!os_name.empty())
+ {
+ sp.os_name.value = os_name;
+ sp.os_ver.value = os_ver;
+ }
+ else
+ {
+ try
+ {
+ RTL_OSVERSIONINFOW ovi = {sizeof(RTL_OSVERSIONINFOW)};
+ // GetVersionEx() is no longer useful since Win8.1
+ using RtlGetVersion_t = LONG (*)(PRTL_OSVERSIONINFOW);
+ static RtlGetVersion_t RtlGetVersion;
+ if(!RtlGetVersion)
+ RtlGetVersion = (RtlGetVersion_t) GetProcAddress(GetModuleHandle(L"NTDLL.DLL"), "RtlGetVersion");
+ if(!RtlGetVersion)
+ return make_errored_outcome<void>(GetLastError());
+ RtlGetVersion(&ovi);
+ sp.os_name.value = "Microsoft Windows ";
+ sp.os_name.value.append(ovi.dwPlatformId == VER_PLATFORM_WIN32_NT ? "NT" : "Unknown");
+ sp.os_ver.value.append(to_string(ovi.dwMajorVersion) + "." + to_string(ovi.dwMinorVersion) + "." + to_string(ovi.dwBuildNumber));
+ os_name = sp.os_name.value;
+ os_ver = sp.os_ver.value;
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+ }
+ return make_ready_outcome<void>();
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+ // CPU name, architecture, physical cores
+ outcome<void> cpu(storage_profile &sp, file_handle &h) noexcept
+ {
+ static std::string cpu_name, cpu_architecture;
+ static unsigned cpu_physical_cores;
+ if(!cpu_name.empty())
+ {
+ sp.cpu_name.value = cpu_name;
+ sp.cpu_architecture.value = cpu_architecture;
+ sp.cpu_physical_cores.value = cpu_physical_cores;
+ }
+ else
+ {
+ try
+ {
+ SYSTEM_INFO si = {{sizeof(SYSTEM_INFO)}};
+ GetNativeSystemInfo(&si);
+ switch(si.wProcessorArchitecture)
+ {
+ case PROCESSOR_ARCHITECTURE_AMD64:
+ sp.cpu_name.value = sp.cpu_architecture.value = "x64";
+ break;
+ case PROCESSOR_ARCHITECTURE_ARM:
+ sp.cpu_name.value = sp.cpu_architecture.value = "ARM";
+ break;
+ case PROCESSOR_ARCHITECTURE_IA64:
+ sp.cpu_name.value = sp.cpu_architecture.value = "IA64";
+ break;
+ case PROCESSOR_ARCHITECTURE_INTEL:
+ sp.cpu_name.value = sp.cpu_architecture.value = "x86";
+ break;
+ default:
+ sp.cpu_name.value = sp.cpu_architecture.value = "unknown";
+ break;
+ }
+ {
+ DWORD size = 0;
+
+ GetLogicalProcessorInformation(NULL, &size);
+ if(ERROR_INSUFFICIENT_BUFFER != GetLastError())
+ return make_errored_outcome<void>(GetLastError());
+
+ std::vector<SYSTEM_LOGICAL_PROCESSOR_INFORMATION> buffer(size);
+ if(GetLogicalProcessorInformation(&buffer.front(), &size) == FALSE)
+ return make_errored_outcome<void>(GetLastError());
+
+ const size_t Elements = size / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION);
+
+ sp.cpu_physical_cores.value = 0;
+ for(size_t i = 0; i < Elements; ++i)
+ {
+ if(buffer[i].Relationship == RelationProcessorCore)
+ ++sp.cpu_physical_cores.value;
+ }
+ }
+#if defined(__i386__) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64)
+// We can do a much better CPU name on x86/x64
+#if defined(__clang__) && !defined(_MSC_VER)
+ auto __cpuid = [](int *cpuInfo, int func) { __asm__ __volatile__("cpuid\n\t" : "=a"(cpuInfo[0]), "=b"(cpuInfo[1]), "=c"(cpuInfo[2]), "=d"(cpuInfo[3]) : "0"(func)); };
+#endif
+ sp.cpu_name.value.clear();
+ {
+ char buffer[62];
+ memset(buffer, 32, 62);
+ int nBuff[4];
+ __cpuid(nBuff, 0);
+ *(int *) &buffer[0] = nBuff[1];
+ *(int *) &buffer[4] = nBuff[3];
+ *(int *) &buffer[8] = nBuff[2];
+
+ // Do we have a brand string?
+ __cpuid(nBuff, 0x80000000);
+ if((unsigned) nBuff[0] >= 0x80000004)
+ {
+ __cpuid((int *) &buffer[14], 0x80000002);
+ __cpuid((int *) &buffer[30], 0x80000003);
+ __cpuid((int *) &buffer[46], 0x80000004);
+ }
+ else
+ strcpy(&buffer[14], "unbranded");
+
+ // Trim string
+ for(size_t n = 0; n < 62; n++)
+ {
+ if(!n || buffer[n] != 32 || buffer[n - 1] != 32)
+ if(buffer[n])
+ sp.cpu_name.value.push_back(buffer[n]);
+ }
+ }
+#endif
+ cpu_name = sp.cpu_name.value;
+ cpu_architecture = sp.cpu_architecture.value;
+ cpu_physical_cores = sp.cpu_physical_cores.value;
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+ }
+ return make_ready_outcome<void>();
+ }
+ namespace windows
+ {
+ outcome<void> _mem(storage_profile &sp, file_handle &h) noexcept
+ {
+ MEMORYSTATUSEX ms = {sizeof(MEMORYSTATUSEX)};
+ GlobalMemoryStatusEx(&ms);
+ sp.mem_quantity.value = (unsigned long long) ms.ullTotalPhys;
+ sp.mem_in_use.value = (float) (ms.ullTotalPhys - ms.ullAvailPhys) / ms.ullTotalPhys;
+ return make_ready_outcome<void>();
+ }
+ }
+ }
+ namespace storage
+ {
+ namespace windows
+ {
+ // Controller type, max transfer, max buffers. Device name, size
+ outcome<void> _device(storage_profile &sp, file_handle &h, std::string mntfromname, std::string /*fstypename*/) noexcept
+ {
+ try
+ {
+ alignas(8) fixme_path::value_type buffer[32769];
+ // Firstly open a handle to the volume
+ BOOST_OUTCOME_FILTER_ERROR(volumeh, file_handle::file(mntfromname, handle::mode::none, handle::creation::open_existing, handle::caching::only_metadata));
+ STORAGE_PROPERTY_QUERY spq = {StorageAdapterProperty, PropertyStandardQuery};
+ STORAGE_ADAPTER_DESCRIPTOR *sad = (STORAGE_ADAPTER_DESCRIPTOR *) buffer;
+ OVERLAPPED ol = {(ULONG_PTR) -1};
+ if(!DeviceIoControl(volumeh.native_handle().h, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), sad, sizeof(buffer), nullptr, &ol))
+ {
+ if(ERROR_IO_PENDING == GetLastError())
+ {
+ NTSTATUS ntstat = ntwait(volumeh.native_handle().h, ol, deadline());
+ if(ntstat)
+ return make_errored_outcome_nt<void>(ntstat);
+ }
+ if(ERROR_SUCCESS != GetLastError())
+ return make_errored_outcome<void>(GetLastError());
+ }
+ switch(sad->BusType)
+ {
+ case BusTypeScsi:
+ sp.controller_type.value = "SCSI";
+ break;
+ case BusTypeAtapi:
+ sp.controller_type.value = "ATAPI";
+ break;
+ case BusTypeAta:
+ sp.controller_type.value = "ATA";
+ break;
+ case BusType1394:
+ sp.controller_type.value = "1394";
+ break;
+ case BusTypeSsa:
+ sp.controller_type.value = "SSA";
+ break;
+ case BusTypeFibre:
+ sp.controller_type.value = "Fibre";
+ break;
+ case BusTypeUsb:
+ sp.controller_type.value = "USB";
+ break;
+ case BusTypeRAID:
+ sp.controller_type.value = "RAID";
+ break;
+ case BusTypeiScsi:
+ sp.controller_type.value = "iSCSI";
+ break;
+ case BusTypeSas:
+ sp.controller_type.value = "SAS";
+ break;
+ case BusTypeSata:
+ sp.controller_type.value = "SATA";
+ break;
+ case BusTypeSd:
+ sp.controller_type.value = "SD";
+ break;
+ case BusTypeMmc:
+ sp.controller_type.value = "MMC";
+ break;
+ case BusTypeVirtual:
+ sp.controller_type.value = "Virtual";
+ break;
+ case BusTypeFileBackedVirtual:
+ sp.controller_type.value = "File Backed Virtual";
+ break;
+ default:
+ sp.controller_type.value = "unknown";
+ break;
+ }
+ sp.controller_max_transfer.value = sad->MaximumTransferLength;
+ sp.controller_max_buffers.value = sad->MaximumPhysicalPages;
+
+ // Now ask the volume what physical disks it spans
+ VOLUME_DISK_EXTENTS *vde = (VOLUME_DISK_EXTENTS *) buffer;
+ ol.Internal = (ULONG_PTR) -1;
+ if(!DeviceIoControl(volumeh.native_handle().h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, nullptr, 0, vde, sizeof(buffer), nullptr, &ol))
+ {
+ if(ERROR_IO_PENDING == GetLastError())
+ {
+ NTSTATUS ntstat = ntwait(volumeh.native_handle().h, ol, deadline());
+ if(ntstat)
+ return make_errored_outcome_nt<void>(ntstat);
+ }
+ if(ERROR_SUCCESS != GetLastError())
+ return make_errored_outcome<void>(GetLastError());
+ }
+ DWORD disk_extents = vde->NumberOfDiskExtents;
+ sp.device_name.value.clear();
+ if(disk_extents > 0)
+ {
+ // For now we only care about the first physical device
+ alignas(8) fixme_path::value_type physicaldrivename[32769] = L"\\\\.\\PhysicalDrive", *e;
+ for(e = physicaldrivename; *e; e++)
+ ;
+ if(vde->Extents[0].DiskNumber >= 100)
+ *e++ = '0' + ((vde->Extents[0].DiskNumber / 100) % 10);
+ if(vde->Extents[0].DiskNumber >= 10)
+ *e++ = '0' + ((vde->Extents[0].DiskNumber / 10) % 10);
+ *e++ = '0' + (vde->Extents[0].DiskNumber % 10);
+ *e = 0;
+ BOOST_OUTCOME_FILTER_ERROR(diskh, file_handle::file(physicaldrivename, handle::mode::none, handle::creation::open_existing, handle::caching::only_metadata));
+ spq = {StorageDeviceProperty, PropertyStandardQuery};
+ STORAGE_DEVICE_DESCRIPTOR *sdd = (STORAGE_DEVICE_DESCRIPTOR *) buffer;
+ ol.Internal = (ULONG_PTR) -1;
+ if(!DeviceIoControl(diskh.native_handle().h, IOCTL_STORAGE_QUERY_PROPERTY, &spq, sizeof(spq), sdd, sizeof(buffer), nullptr, &ol))
+ {
+ if(ERROR_IO_PENDING == GetLastError())
+ {
+ NTSTATUS ntstat = ntwait(volumeh.native_handle().h, ol, deadline());
+ if(ntstat)
+ return make_errored_outcome_nt<void>(ntstat);
+ }
+ if(ERROR_SUCCESS != GetLastError())
+ return make_errored_outcome<void>(GetLastError());
+ }
+ if(sdd->VendorIdOffset > 0 && sdd->VendorIdOffset < sizeof(buffer))
+ {
+ for(auto n = sdd->VendorIdOffset; ((const char *) buffer)[n]; n++)
+ sp.device_name.value.push_back(((const char *) buffer)[n]);
+ sp.device_name.value.push_back(',');
+ }
+ if(sdd->ProductIdOffset > 0 && sdd->ProductIdOffset < sizeof(buffer))
+ {
+ for(auto n = sdd->ProductIdOffset; ((const char *) buffer)[n]; n++)
+ sp.device_name.value.push_back(((const char *) buffer)[n]);
+ sp.device_name.value.push_back(',');
+ }
+ if(sdd->ProductRevisionOffset > 0 && sdd->ProductRevisionOffset < sizeof(buffer))
+ {
+ for(auto n = sdd->ProductRevisionOffset; ((const char *) buffer)[n]; n++)
+ sp.device_name.value.push_back(((const char *) buffer)[n]);
+ sp.device_name.value.push_back(',');
+ }
+ if(!sp.device_name.value.empty())
+ sp.device_name.value.resize(sp.device_name.value.size() - 1);
+ if(disk_extents > 1)
+ sp.device_name.value.append(" (NOTE: plus additional devices)");
+
+ // Get device size
+ // IOCTL_STORAGE_READ_CAPACITY needs GENERIC_READ privs which requires admin privs
+ // so simply fetch the geometry
+ DISK_GEOMETRY_EX *dg = (DISK_GEOMETRY_EX *) buffer;
+ ol.Internal = (ULONG_PTR) -1;
+ if(!DeviceIoControl(diskh.native_handle().h, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, nullptr, 0, dg, sizeof(buffer), nullptr, &ol))
+ {
+ if(ERROR_IO_PENDING == GetLastError())
+ {
+ NTSTATUS ntstat = ntwait(volumeh.native_handle().h, ol, deadline());
+ if(ntstat)
+ return make_errored_outcome_nt<void>(ntstat);
+ }
+ if(ERROR_SUCCESS != GetLastError())
+ return make_errored_outcome<void>(GetLastError());
+ }
+ sp.device_size.value = dg->DiskSize.QuadPart;
+ }
+ }
+ catch(...)
+ {
+ return std::current_exception();
+ }
+ return make_ready_outcome<void>();
+ }
+ }
+ }
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/detail/impl/windows/utils.ipp b/include/boost/afio/v2/detail/impl/windows/utils.ipp
new file mode 100644
index 00000000..205be722
--- /dev/null
+++ b/include/boost/afio/v2/detail/impl/windows/utils.ipp
@@ -0,0 +1,126 @@
+/* utils.hpp
+Misc utilities
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "../../../utils.hpp"
+#include "import.hpp"
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+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;
+ stl11::lock_guard<decltype(lock)> g(lock);
+ if (pagesizes.empty())
+ {
+ 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_());
+ }
+ }
+ }
+ }
+ return only_actually_available ? pagesizes_available : pagesizes;
+ }
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+ void random_fill(char *buffer, size_t bytes)
+ {
+ windows_nt_kernel::init();
+ using namespace windows_nt_kernel;
+ if (!RtlGenRandom(buffer, (ULONG)bytes))
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("afio: Kernel crypto function failed");
+ std::terminate();
+ }
+ }
+
+ namespace detail
+ {
+ large_page_allocation allocate_large_pages(size_t bytes)
+ {
+ large_page_allocation ret(calculate_large_page_allocation(bytes));
+ DWORD type = MEM_COMMIT | MEM_RESERVE;
+ if (ret.page_size_used>65536)
+ type |= MEM_LARGE_PAGES;
+ ret.p = VirtualAlloc(nullptr, ret.actual_size, type, PAGE_READWRITE);
+ if (!ret.p)
+ {
+ if (ERROR_NOT_ENOUGH_MEMORY == GetLastError())
+ ret.p = VirtualAlloc(nullptr, ret.actual_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
+ }
+ #ifndef NDEBUG
+ else if (ret.page_size_used>65536)
+ std::cout << "afio: Large page allocation successful" << std::endl;
+ #endif
+ return ret;
+ }
+ void deallocate_large_pages(void *p, size_t bytes)
+ {
+ if (!VirtualFree(p, 0, MEM_RELEASE))
+ {
+ BOOST_AFIO_LOG_FATAL_EXIT("afio: Freeing large pages failed");
+ std::terminate();
+ }
+ }
+ }
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
diff --git a/include/boost/afio/v2/file_handle.hpp b/include/boost/afio/v2/file_handle.hpp
new file mode 100644
index 00000000..e3f631b3
--- /dev/null
+++ b/include/boost/afio/v2/file_handle.hpp
@@ -0,0 +1,152 @@
+/* file_handle.hpp
+A handle to a file
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "handle.hpp"
+
+#ifndef BOOST_AFIO_FILE_HANDLE_H
+#define BOOST_AFIO_FILE_HANDLE_H
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+class io_service;
+
+/*! \class file_handle
+\brief A handle to a regular file or device, kept data layout compatible with
+async_file_handle.
+*/
+class BOOST_AFIO_DECL file_handle : public io_handle
+{
+public:
+ using path_type = io_handle::path_type;
+ using extent_type = io_handle::extent_type;
+ using size_type = io_handle::size_type;
+ using mode = io_handle::mode;
+ using creation = io_handle::creation;
+ using caching = io_handle::caching;
+ using flag = io_handle::flag;
+ using buffer_type = io_handle::buffer_type;
+ using const_buffer_type = io_handle::const_buffer_type;
+ using buffers_type = io_handle::buffers_type;
+ using const_buffers_type = io_handle::const_buffers_type;
+ template <class T> using io_request = io_handle::io_request<T>;
+ template <class T> using io_result = io_handle::io_result<T>;
+
+protected:
+ path_type _path;
+ io_service *_service;
+
+public:
+ //! Default constructor
+ file_handle()
+ : io_handle()
+ , _service(nullptr)
+ {
+ }
+ //! Construct a handle from a supplied native handle
+ file_handle(path_type path, native_handle_type h, caching caching = caching::none, flag flags = flag::none)
+ : io_handle(std::move(h), std::move(caching), std::move(flags))
+ , _path(std::move(path))
+ , _service(nullptr)
+ {
+ }
+ //! Implicit move construction of file_handle permitted
+ file_handle(file_handle &&o) noexcept : io_handle(std::move(o)), _path(std::move(o._path)), _service(o._service) { o._service = nullptr; }
+ //! Explicit conversion from handle and io_handle permitted
+ explicit file_handle(handle &&o, path_type path) noexcept : io_handle(std::move(o)), _path(std::move(path)), _service(nullptr) {}
+ using io_handle::really_copy;
+ //! Copy the handle. Tag enabled because copying handles is expensive (fd duplication).
+ explicit file_handle(const file_handle &o, really_copy _)
+ : io_handle(o, _)
+ , _path(o._path)
+ , _service(o._service)
+ {
+ }
+ //! Move assignment of file_handle permitted
+ file_handle &operator=(file_handle &&o) noexcept
+ {
+ this->~file_handle();
+ new(this) file_handle(std::move(o));
+ return *this;
+ }
+ //! Swap with another instance
+ void swap(file_handle &o) noexcept
+ {
+ file_handle temp(std::move(*this));
+ *this = std::move(o);
+ o = std::move(temp);
+ }
+
+ /*! Create a file handle opening access to a file on path
+
+ \errors Any of the values POSIX open() or CreateFile() can return.
+ */
+ //[[bindlib::make_free]]
+ static BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<file_handle> file(path_type _path, mode _mode = mode::read, creation _creation = creation::open_existing, caching _caching = caching::all, flag flags = flag::none) noexcept;
+
+ /*! Clone this handle (copy constructor is disabled to avoid accidental copying)
+
+ \errors Any of the values POSIX dup() or DuplicateHandle() can return.
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<file_handle> clone() const noexcept;
+
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC path_type path() const noexcept override { return _path; }
+ //! The i/o service this handle is attached to
+ io_service *service() const noexcept { return _service; }
+
+ /*! Return the current maximum permitted extent of the file.
+
+ \errors Any of the values POSIX fstat() or GetFileInformationByHandleEx() can return.
+ */
+ result<extent_type> length() const noexcept;
+
+ /*! Resize the current maximum permitted extent of the file to the given extent, avoiding any
+ new allocation of physical storage where supported. Note that on extents based filing systems
+ this will succeed even if there is insufficient free space on the storage medium.
+
+ \errors Any of the values POSIX ftruncate() or SetFileInformationByHandle() can return.
+ */
+ //[[bindlib::make_free]]
+ result<extent_type> truncate(extent_type newsize) noexcept;
+};
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#define BOOST_AFIO_INCLUDED_BY_HEADER 1
+#ifdef WIN32
+#include "detail/impl/windows/file_handle.ipp"
+#else
+#include "detail/impl/posix/file_handle.ipp"
+#endif
+#undef BOOST_AFIO_INCLUDED_BY_HEADER
+#endif
+
+#endif
diff --git a/include/boost/afio/v2/handle.hpp b/include/boost/afio/v2/handle.hpp
new file mode 100644
index 00000000..673fbeab
--- /dev/null
+++ b/include/boost/afio/v2/handle.hpp
@@ -0,0 +1,370 @@
+/* handle.hpp
+A handle to something
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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 "deadline.h"
+#include "native_handle_type.hpp"
+
+#include <utility> // for pair<>
+#include <vector>
+
+#ifndef BOOST_AFIO_HANDLE_H
+#define BOOST_AFIO_HANDLE_H
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+/*! \class handle
+\brief A native_handle_type which is managed by the lifetime of this object instance.
+*/
+class BOOST_AFIO_DECL handle
+{
+public:
+ //! The path type used by this handle
+ using path_type = fixme_path;
+ //! The file extent type used by this handle
+ using extent_type = unsigned long long;
+ //! The memory extent type used by this handle
+ using size_type = size_t;
+
+ //! The behaviour of the handle: does it read, read and write, or atomic append?
+ enum class mode : unsigned char // bit 0 set means writable
+ {
+ unchanged = 0,
+ none = 2, //!< No ability to read or write anything, but can synchronise (SYNCHRONIZE or 0)
+ attr_read = 4, //!< Ability to read attributes (FILE_READ_ATTRIBUTES|SYNCHRONIZE or O_RDONLY)
+ attr_write = 5, //!< Ability to read attributes (FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES|SYNCHRONIZE or O_RDONLY)
+ read = 6, //!< Ability to read (READ_CONTROL|FILE_READ_DATA|FILE_READ_ATTRIBUTES|FILE_READ_EA|SYNCHRONISE or O_RDONLY)
+ write = 7, //!< Ability to write (READ_CONTROL|FILE_READ_DATA|FILE_READ_ATTRIBUTES|FILE_READ_EA|FILE_WRITE_DATA|FILE_WRITE_ATTRIBUTES|FILE_WRITE_EA|FILE_APPEND_DATA|SYNCHRONISE or O_RDWR)
+ append = 9 //!< All mainstream OSs and CIFS guarantee this is atomic with respect to all other appenders (FILE_APPEND_DATA|SYNCHRONISE or O_APPEND)
+ };
+ //! On opening, do we also create a new file or truncate an existing one?
+ enum class creation : unsigned char
+ {
+ open_existing = 0,
+ only_if_not_exist,
+ if_needed,
+ truncate //!< Atomically truncate on open, leaving creation date unmodified.
+ };
+ //! What i/o on the handle will complete immediately due to kernel caching
+ enum class caching : unsigned char // bit 0 set means safety fsyncs enabled
+ {
+ unchanged = 0,
+ none = 1, //!< No caching whatsoever, all reads and writes come from storage (i.e. <tt>O_DIRECT|O_SYNC</tt>). Align all i/o to 4Kb boundaries for this to work. <tt>flag_disable_safety_fsyncs</tt> can be used here.
+ only_metadata = 2, //!< Cache reads and writes of metadata but avoid caching data (<tt>O_DIRECT</tt>), thus i/o here does not affect other cached data for other handles. Align all i/o to 4Kb boundaries for this to work.
+ reads = 3, //!< Cache reads only. Writes of data and metadata do not complete until reaching storage (<tt>O_SYNC</tt>). <tt>flag_disable_safety_fsyncs</tt> can be used here.
+ reads_and_metadata = 5, //!< Cache reads and writes of metadata, but writes of data do not complete until reaching storage (<tt>O_DSYNC</tt>). <tt>flag_disable_safety_fsyncs</tt> can be used here.
+ all = 4, //!< Cache reads and writes of data and metadata so they complete immediately, sending writes to storage at some point when the kernel decides (this is the default file system caching on a system).
+ safety_fsyncs = 7, //!< Cache reads and writes of data and metadata so they complete immediately, but issue safety fsyncs at certain points. See documentation for <tt>flag_disable_safety_fsyncs</tt>.
+ temporary = 6 //!< Cache reads and writes of data and metadata so they complete immediately, only sending any updates to storage on last handle close in the system or if memory becomes tight as this file is expected to be temporary (Windows only).
+ };
+ //! Bitwise flags which can be specified
+ BOOST_AFIO_BITFIELD_BEGIN(flag)
+ {
+ none = 0, //!< No flags
+ delete_on_close = 1 << 0, //!< Delete the file on last handle close
+ /*! Some kernel caching modes have unhelpfully inconsistent behaviours
+ in getting your data onto storage, so by default unless this flag is
+ specified AFIO adds extra fsyncs to the following operations for the
+ caching modes specified below:
+ * truncation of file length either explicitly or during file open.
+ * closing of the handle either explicitly or in the destructor.
+
+ Additionally on Linux only to prevent loss of file metadata:
+ * On the parent directory whenever a file might have been created.
+ * On the parent directory on file close.
+
+ This only occurs for these kernel caching modes:
+ * caching::none
+ * caching::reads
+ * caching::reads_and_metadata
+ * caching::safety_fsyncs
+ */
+ disable_safety_fsyncs = 1 << 1,
+
+ overlapped = 1 << 28 //!< On Windows, create any new handles with OVERLAPPED semantics
+ }
+ BOOST_AFIO_BITFIELD_END(flag)
+protected:
+ caching _caching;
+ flag _flags;
+ native_handle_type _v;
+
+public:
+ //! Default constructor
+ constexpr handle()
+ : _caching(caching::none)
+ , _flags(flag::none)
+ {
+ }
+ //! Construct a handle from a supplied native handle
+ BOOST_CXX14_CONSTEXPR handle(native_handle_type h, caching caching = caching::none, flag flags = flag::none)
+ : _caching(caching)
+ , _flags(flags)
+ , _v(std::move(h))
+ {
+ }
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~handle();
+ //! Move the handle. Explicit because this will lose information in any derived source.
+ explicit handle(handle &&o) noexcept : _caching(o._caching), _flags(o._flags), _v(std::move(o._v))
+ {
+ o._caching = caching::none;
+ o._flags = flag::none;
+ o._v = native_handle_type();
+ }
+ //! Tag type to enable copy constructor
+ struct really_copy
+ {
+ };
+ //! Copy the handle. Tag enabled because copying handles is expensive (fd duplication).
+ explicit handle(const handle &o, really_copy);
+ //! No move assignment
+ handle &operator=(handle &&o) = delete;
+ //! No copy assignment
+ handle &operator=(const handle &o) = delete;
+
+ //! The path this handle refers to, if any
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC path_type path() const noexcept { return path_type(); }
+ //! Immediately close the native handle type managed by this handle
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> close() noexcept;
+ //! Release the native handle type managed by this handle
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC native_handle_type release() noexcept
+ {
+ native_handle_type ret(std::move(_v));
+ return ret;
+ }
+
+ //! True if the handle is readable
+ bool is_readable() const noexcept { return _v.is_readable(); }
+ //! True if the handle is writable
+ bool is_writable() const noexcept { return _v.is_writable(); }
+ //! True if the handle is append only
+ bool is_append_only() const noexcept { return _v.is_append_only(); }
+ /*! Changes whether this handle is append only or not.
+
+ \warning On Windows this is implemented as a bit of a hack to make it fast like on POSIX,
+ so make sure you open the handle for read/write originally. Note unlike on POSIX the
+ append_only disposition will be the only one toggled, seekable and readable will remain
+ turned on.
+
+ \errors Whatever POSIX fcntl() returns. On Windows nothing is changed on the handle.
+ \mallocs No memory allocation.
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> set_append_only(bool enable) noexcept;
+
+ //! True if overlapped
+ bool is_overlapped() const noexcept { return _v.is_overlapped(); }
+ //! True if seekable
+ bool is_seekable() const noexcept { return _v.is_seekable(); }
+ //! True if requires aligned i/o
+ bool requires_aligned_io() const noexcept { return _v.requires_aligned_io(); }
+
+ //! True if a regular file or device
+ bool is_regular() const noexcept { return _v.is_regular(); }
+ //! True if a directory
+ bool is_directory() const noexcept { return _v.is_directory(); }
+ //! True if a symlink
+ bool is_symlink() const noexcept { return _v.is_symlink(); }
+ //! True if a multiplexer like BSD kqueues, Linux epoll or Windows IOCP
+ bool is_multiplexer() const noexcept { return _v.is_multiplexer(); }
+ //! True if a process
+ bool is_process() const noexcept { return _v.is_process(); }
+
+ //! Kernel cache strategy used by this handle
+ caching kernel_caching() const noexcept { return _caching; }
+ //! True if the handle uses the kernel page cache for reads
+ bool are_reads_from_cache() const noexcept { return _caching != caching::none && _caching != caching::only_metadata; }
+ //! True if writes are safely on storage on completion
+ bool are_writes_durable() const noexcept { return _caching == caching::none || _caching == caching::reads || _caching == caching::reads_and_metadata; }
+ //! True if issuing safety fsyncs is on
+ bool are_safety_fsyncs_issued() const noexcept { return !(_flags & flag::disable_safety_fsyncs) && !!(static_cast<int>(_caching) & 1); }
+ /*! Changes the kernel cache strategy used by this handle.
+ Note most OSs impose severe restrictions on what can be changed and will error out,
+ it may be easier to simply create a new handle.
+
+ \warning On Windows this reopens the file, it is no slower than
+ opening the file fresh but equally it is vastly slower than on POSIX.
+
+ \errors Whatever POSIX fcntl() or ReOpenFile() returns.
+ \mallocs No memory allocation.
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<void> set_kernel_caching(caching caching) noexcept;
+
+ //! The flags this handle was opened with
+ flag flags() const noexcept { return _flags; }
+ //! The native handle used by this handle
+ native_handle_type native_handle() const noexcept { return _v; }
+};
+
+/*! \class io_handle
+\brief A handle to something capable of scatter-gather i/o.
+*/
+class BOOST_AFIO_DECL io_handle : public handle
+{
+public:
+ using path_type = handle::path_type;
+ using extent_type = handle::extent_type;
+ using size_type = handle::size_type;
+ using mode = handle::mode;
+ using creation = handle::creation;
+ using caching = handle::caching;
+ using flag = handle::flag;
+
+ //! The scatter buffer type used by this handle
+ using buffer_type = std::pair<char *, size_type>;
+ //! The gather buffer type used by this handle
+ using const_buffer_type = std::pair<const char *, size_type>;
+ //! The scatter buffers type used by this handle
+ using buffers_type = span<buffer_type>;
+ //! The gather buffers type used by this handle
+ using const_buffers_type = span<const_buffer_type>;
+ //! The i/o request type used by this handle
+ template <class T> struct io_request
+ {
+ T buffers;
+ extent_type offset;
+ constexpr io_request()
+ : buffers()
+ , offset(0)
+ {
+ }
+ constexpr io_request(T _buffers, extent_type _offset)
+ : buffers(std::move(_buffers))
+ , offset(_offset)
+ {
+ }
+ };
+ //! The i/o result type used by this handle
+ template <class T> class io_result : public result<T>
+ {
+ using Base = result<T>;
+ size_type _bytes_transferred;
+
+ public:
+ constexpr io_result() noexcept : _bytes_transferred((size_type) -1) {}
+ template <class... Args>
+ io_result(Args &&... args)
+ : result<T>(std::forward<Args>(args)...)
+ , _bytes_transferred((size_type) -1)
+ {
+ }
+ io_result &operator=(const io_result &) = default;
+ io_result &operator=(io_result &&) = default;
+ //! Returns bytes transferred
+ size_type bytes_transferred() noexcept
+ {
+ if(_bytes_transferred == (size_type) -1)
+ {
+ _bytes_transferred = 0;
+ for(auto &i : this->value())
+ _bytes_transferred += i.second;
+ }
+ return _bytes_transferred;
+ }
+ };
+
+public:
+ //! Default constructor
+ constexpr io_handle() = default;
+ //! Same constructors as handle
+ using handle::handle;
+ //! Explicit conversion from handle permitted
+ explicit io_handle(handle &&o) noexcept : handle(std::move(o)) {}
+ using handle::really_copy;
+ //! Copy the handle. Tag enabled because copying handles is expensive (fd duplication).
+ explicit io_handle(const io_handle &o, really_copy _)
+ : handle(o, _)
+ {
+ }
+
+ /*! \brief Read data from the open handle.
+
+ \return The buffers read, which may not be the buffers input. The size of each scatter-gather
+ buffer is updated with the number of bytes of that buffer transferred, and the pointer to
+ the data may be \em completely different to what was submitted (e.g. it may point into a
+ memory map).
+ \param reqs A scatter-gather and offset request.
+ \param deadline An optional deadline by which the i/o must complete, else it is cancelled.
+ Note function may return significantly after this deadline if the i/o takes long to cancel.
+ \errors Any of the values POSIX read() can return, ETIMEDOUT, ECANCELED. ENOTSUP may be
+ returned if deadline i/o is not possible with this particular handle configuration (e.g.
+ reading from regular files on POSIX or reading from a non-overlapped HANDLE on Windows).
+ \mallocs The default synchronous implementation in file_handle performs no memory allocation.
+ The asynchronous implementation in async_file_handle performs one calloc and one free.
+ */
+ //[[bindlib::make_free]]
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<buffers_type> read(io_request<buffers_type> reqs, deadline d = deadline()) noexcept;
+ //! \overload
+ io_result<buffer_type> read(extent_type offset, char *data, size_type bytes, deadline d = deadline()) noexcept
+ {
+ buffer_type _reqs[1] = {{data, bytes}};
+ io_request<buffers_type> reqs(buffers_type(_reqs), offset);
+ BOOST_OUTCOME_FILTER_ERROR(v, read(reqs, d));
+ return *v.data();
+ }
+
+ /*! \brief Write data to the open handle.
+
+ \return The buffers written, which may not be the buffers input. The size of each scatter-gather
+ buffer is updated with the number of bytes of that buffer transferred.
+ \param reqs A scatter-gather and offset request.
+ \param deadline An optional deadline by which the i/o must complete, else it is cancelled.
+ Note function may return significantly after this deadline if the i/o takes long to cancel.
+ \errors Any of the values POSIX write() can return, ETIMEDOUT, ECANCELED. ENOTSUP may be
+ returned if deadline i/o is not possible with this particular handle configuration (e.g.
+ writing to regular files on POSIX or writing to a non-overlapped HANDLE on Windows).
+ \mallocs The default synchronous implementation in file_handle performs no memory allocation.
+ The asynchronous implementation in async_file_handle performs one calloc and one free.
+ */
+ //[[bindlib::make_free]]
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC io_result<const_buffers_type> write(io_request<const_buffers_type> reqs, deadline d = deadline()) noexcept;
+ //! \overload
+ io_result<const_buffer_type> write(extent_type offset, const char *data, size_type bytes, deadline d = deadline()) noexcept
+ {
+ const_buffer_type _reqs[1] = {{data, bytes}};
+ io_request<const_buffers_type> reqs(const_buffers_type(_reqs), offset);
+ BOOST_OUTCOME_FILTER_ERROR(v, write(reqs, d));
+ return *v.data();
+ }
+};
+
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#define BOOST_AFIO_INCLUDED_BY_HEADER 1
+#ifdef WIN32
+#include "detail/impl/windows/handle.ipp"
+#else
+#include "detail/impl/posix/handle.ipp"
+#endif
+#undef BOOST_AFIO_INCLUDED_BY_HEADER
+#endif
+
+#endif
diff --git a/include/boost/afio/v2/io_service.hpp b/include/boost/afio/v2/io_service.hpp
new file mode 100644
index 00000000..badf0c01
--- /dev/null
+++ b/include/boost/afio/v2/io_service.hpp
@@ -0,0 +1,247 @@
+/* io_service.hpp
+Multiplex file i/o
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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_IO_SERVICE_H
+#define BOOST_AFIO_IO_SERVICE_H
+
+#include "handle.hpp"
+
+#include <deque>
+
+#undef _threadid // windows macro splosh sigh
+
+//!\def BOOST_AFIO_COMPILE_KQUEUES Undefined to autodetect, 1 to compile in BSD kqueue support, 0 to leave it out
+/*!\def BOOST_AFIO_USE_POSIX_AIO Undefined to autodetect, 1 to use POSIX AIO, 0 to not use
+
+\warning On FreeBSD the AIO kernel module needs to be loaded for POSIX AIO to work.
+Run as root 'kldload aio' or add 'aio_load=YES' in loader.conf.
+*/
+//!\def BOOST_AFIO_IO_POST_SIGNAL Undefined to autoset to first free SIGRTMIN if realtime signals available, else SIGUSR1. Only used if BOOST_AFIO_USE_KQUEUES=0.
+//!\def BOOST_AFIO_HAVE_REALTIME_SIGNALS Undefined to autodetect. 0 to use non-realtime signals. Note performance in this use case is abysmal.
+
+// Need to decide which kind of POSIX AIO to use
+#ifndef WIN32
+// Right now the only thing we support is POSIX AIO
+#if !defined(BOOST_AFIO_USE_POSIX_AIO)
+#define BOOST_AFIO_USE_POSIX_AIO 1
+#endif
+// BSD kqueues not implemented yet
+//# if defined(__FreeBSD__) && !defined(BOOST_AFIO_COMPILE_KQUEUES)
+//# define BOOST_AFIO_COMPILE_KQUEUES 1
+//# endif
+#if BOOST_AFIO_COMPILE_KQUEUES
+#if defined(BOOST_AFIO_USE_POSIX_AIO) && !BOOST_AFIO_USE_POSIX_AIO
+#error BSD kqueues must be combined with POSIX AIO!
+#endif
+#if !defined(BOOST_AFIO_USE_POSIX_AIO)
+#define BOOST_AFIO_USE_POSIX_AIO 1
+#endif
+#endif
+
+#if BOOST_AFIO_USE_POSIX_AIO
+// We'll be using POSIX AIO and signal based interruption for post()
+#include <signal.h>
+// Do we have realtime signals?
+#if !defined(BOOST_AFIO_HAVE_REALTIME_SIGNALS) && defined(_POSIX_RTSIG_MAX) && defined(SIGRTMIN)
+#ifndef BOOST_AFIO_IO_POST_SIGNAL
+#define BOOST_AFIO_IO_POST_SIGNAL -1
+#endif
+#define BOOST_AFIO_HAVE_REALTIME_SIGNALS 1
+#else
+#ifndef BOOST_AFIO_IO_POST_SIGNAL
+#define BOOST_AFIO_IO_POST_SIGNAL (SIGUSR1)
+#endif
+#define BOOST_AFIO_HAVE_REALTIME_SIGNALS 0
+#endif
+struct aiocb;
+#endif
+#endif
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+class io_service;
+class async_file_handle;
+
+/*! \class io_service
+\brief An asynchronous i/o multiplexer service.
+*/
+class BOOST_AFIO_DECL io_service
+{
+ friend class async_file_handle;
+
+public:
+ //! The file extent type used by this i/o service
+ using extent_type = io_handle::extent_type;
+ //! The memory extent type used by this i/o service
+ using size_type = io_handle::size_type;
+ //! The scatter buffer type used by this i/o service
+ using buffer_type = io_handle::buffer_type;
+ //! The gather buffer type used by this i/o service
+ using const_buffer_type = io_handle::const_buffer_type;
+ //! The scatter buffers type used by this i/o service
+ using buffers_type = io_handle::buffers_type;
+ //! The gather buffers type used by this i/o service
+ using const_buffers_type = io_handle::const_buffers_type;
+ //! The i/o request type used by this i/o service
+ template <class T> using io_request = io_handle::io_request<T>;
+ //! The i/o result type used by this i/o service
+ template <class T> using io_result = io_handle::io_result<T>;
+
+private:
+#ifdef WIN32
+ win::handle _threadh;
+ win::dword _threadid;
+#else
+ pthread_t _threadh;
+#endif
+ stl11::mutex _posts_lock;
+ struct post_info
+ {
+ io_service *service;
+ detail::function_ptr<void(io_service *)> f;
+ post_info(io_service *s, detail::function_ptr<void(io_service *)> _f)
+ : service(s)
+ , f(std::move(_f))
+ {
+ }
+ };
+ std::deque<post_info> _posts;
+ using shared_size_type = std::atomic<size_type>;
+ shared_size_type _work_queued;
+#if BOOST_AFIO_USE_POSIX_AIO
+ bool _use_kqueues;
+#if BOOST_AFIO_COMPILE_KQUEUES
+ int _kqueueh;
+#endif
+ std::vector<struct aiocb *> _aiocbsv; // for fast aio_suspend()
+#endif
+public:
+ // LOCK MUST BE HELD ON ENTRY!
+ void __post_done(post_info *pi)
+ {
+ // Find the post_info and remove it
+ for(auto &i : _posts)
+ {
+ if(&i == pi)
+ {
+ i.f.reset();
+ i.service = nullptr;
+ pi = nullptr;
+ break;
+ }
+ }
+ assert(!pi);
+ if(pi)
+ abort();
+ _work_done();
+ while(!_posts.front().service)
+ _posts.pop_front();
+ }
+ void _post_done(post_info *pi)
+ {
+ std::lock_guard<decltype(_posts_lock)> g(_posts_lock);
+ return __post_done(pi);
+ }
+ void _work_enqueued(size_type i = 1) { _work_queued += i; }
+ void _work_done() { --_work_queued; }
+ /*! Creates an i/o service for the calling thread, installing a
+ global signal handler via set_interruption_signal() if not yet installed
+ if on POSIX and BSD kqueues not in use.
+ */
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC io_service();
+ io_service(io_service &&) = delete;
+ io_service &operator=(io_service &&) = delete;
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC ~io_service();
+
+#ifdef BOOST_AFIO_IO_POST_SIGNAL
+private:
+ int _blocked_interrupt_signal;
+ std::atomic<bool> _need_signal; // false = signal not needed, true = signal needed
+ void _block_interruption() noexcept;
+ void _unblock_interruption() noexcept;
+
+public:
+ /*! Returns the signal used for interrupting run_until(). Only used on POSIX when
+ BSD kqueues are not used. Defaults to BOOST_AFIO_IO_POST_SIGNAL on platforms which use it.
+
+ \note Only present if BOOST_AFIO_IO_POST_SIGNAL is defined.
+ */
+ static int interruption_signal() noexcept;
+ /*! Sets the signal used for interrupting run_until(), returning the former signal
+ setting. Only used on POSIX when BSD kqueues are not used. Special values are
+ 0 for deinstall global signal handler, and -1 for install to first unused signal
+ between SIGRTMIN and SIGRTMAX. Changing this while any io_service instances exist
+ is a bad idea.
+
+ \note Only present if BOOST_AFIO_IO_POST_SIGNAL is defined.
+ */
+ static BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC int set_interruption_signal(int sig = BOOST_AFIO_IO_POST_SIGNAL);
+#endif
+
+#if BOOST_AFIO_USE_POSIX_AIO
+ //! True if this i/o service is using BSD kqueues
+ bool using_kqueues() const noexcept { return _use_kqueues; }
+ //! Force disable any use of BSD kqueues
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC void disable_kqueues();
+#endif
+
+ /*! Runs the i/o service for the thread owning this i/o service. Returns true if more
+ work remains and we just handled an i/o or post; false if there is no more work; ETIMEDOUT if
+ the deadline passed; EOPNOTSUPP if you try to call it from a non-owning thread; EINVAL
+ if deadline is invalid.
+ */
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC result<bool> run_until(deadline d) noexcept;
+ //! \overload
+ result<bool> run() noexcept { return run_until(deadline()); }
+
+private:
+ BOOST_AFIO_HEADERS_ONLY_VIRTUAL_SPEC void post(detail::function_ptr<void(io_service *)> &&f);
+
+public:
+ /*! Schedule the callable to be invoked by the thread owning this object at its next
+ available opportunity. Unlike any other function in this API layer, this function is thread safe.
+ */
+ template <class U> void post(U &&f) { _post(detail::make_function_ptr<void(io_service *)>(std::forward<U>(f))); }
+};
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#define BOOST_AFIO_INCLUDED_BY_HEADER 1
+#ifdef WIN32
+#include "detail/impl/windows/io_service.ipp"
+#else
+#include "detail/impl/posix/io_service.ipp"
+#endif
+#undef BOOST_AFIO_INCLUDED_BY_HEADER
+#endif
+
+#endif
diff --git a/include/boost/afio/v2/lockable_handle.hpp b/include/boost/afio/v2/lockable_handle.hpp
new file mode 100644
index 00000000..ceaab28e
--- /dev/null
+++ b/include/boost/afio/v2/lockable_handle.hpp
@@ -0,0 +1,29 @@
+
+
+
+/*! Instead of placing locks on the file directly, use a shadow file which
+allows concurrent Windows and POSIX users on a networked drive to interoperate correctly. On
+Windows this makes locks advisory instead of mandatory, hence matching POSIX
+semantics. On POSIX this enables non-insane byte range locks, so instead of
+all locks being dropped on any handle close as per POSIX requirements, every
+byte range lock is kept per thread per handle as on Windows.
+*/
+constexpr unsigned flag_use_shadow_lock_file = (1 << 2);
+/*! Will the *last* close of this handle by any process in the system delete the file?
+On Windows this asks the operating system to delete the file on last close which
+simply works. On POSIX on open this locks delete_on_close_lock_range() in the file
+(or shadow lock file if enabled) for shared access
+and tries to upgrade to exclusive on handle close - if successful, it assumes it is
+the only read-writer and deletes the file.
+*/
+constexpr unsigned flag_delete_on_close = (1 << 3);
+/*! Will byte range locking be performed around i/o? This passes through operating
+system semantics as-is, so on Windows you get per thread per handle byte range locking,
+on Linuces since 3.15 you get per handle byte range locking and on all other POSIX
+you get per file byte range locking with all locks being dropped on any handle close.
+Also specifying flag_use_shadow_lock_file can give you sane byte range lock semantics
+on all platforms.
+
+FIXME: Supply feature flags indicating the type of locking this provides via an API
+*/
+constexpr unsigned flag_lock_around_io = (1 << 4);
diff --git a/include/boost/afio/v2/native_handle_type.hpp b/include/boost/afio/v2/native_handle_type.hpp
new file mode 100644
index 00000000..09edf08e
--- /dev/null
+++ b/include/boost/afio/v2/native_handle_type.hpp
@@ -0,0 +1,139 @@
+/* native_handle_type.hpp
+Wraps the platform specific i/o reference object
+(C) 2016 Niall Douglas http://www.nedprod.com/
+File Created: March 2016
+
+
+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 "config.hpp"
+
+#ifndef BOOST_AFIO_NATIVE_HANDLE_TYPE_H
+#define BOOST_AFIO_NATIVE_HANDLE_TYPE_H
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+/*! \struct native_handle_type
+\brief A native handle type used for wrapping file descriptors, process ids or HANDLEs.
+Unmanaged, wrap in a handle object to manage.
+*/
+struct native_handle_type
+{
+ //! The type of handle.
+ BOOST_AFIO_BITFIELD_BEGIN(disposition)
+ {
+ invalid = 0, //!< Invalid handle
+
+ readable = 1 << 0, //!< Is readable
+ writable = 1 << 1, //!< Is writable
+ append_only = 1 << 2, //!< Is append only
+
+ overlapped = 1 << 4, //!< Requires additional synchronisation
+ seekable = 1 << 5, //!< Is seekable
+ aligned_io = 1 << 6, //!< Requires sector aligned i/o (typically 512 or 4096)
+
+ file = 1 << 8, //!< Is a regular file
+ directory = 1 << 9, //!< Is a directory
+ symlink = 1 << 10, //!< Is a symlink
+ multiplexer = 1 << 11, //!< Is a kqueue/epoll/iocp
+ process = 1 << 12 //! Is a child process
+ }
+ BOOST_AFIO_BITFIELD_END(disposition)
+ disposition behaviour; //! The behaviour of the handle
+ union {
+ intptr_t _init;
+ int fd; //!< A POSIX file descriptor
+ int pid; //!< A POSIX process identifier
+ win::handle h; //!< A Windows HANDLE
+ };
+ //! Constructs a default instance
+ constexpr native_handle_type() noexcept : behaviour(), _init(-1) {}
+ //! Construct from a POSIX file descriptor
+ constexpr native_handle_type(disposition _behaviour, int _fd) noexcept : behaviour(_behaviour), fd(_fd) {}
+ //! Construct from a Windows HANDLE
+ constexpr native_handle_type(disposition _behaviour, win::handle _h) noexcept : behaviour(_behaviour), h(_h) {}
+
+ //! Copy construct
+ constexpr native_handle_type(const native_handle_type &) = default;
+ //! Move construct
+ BOOST_CXX14_CONSTEXPR native_handle_type(native_handle_type &&o) noexcept : behaviour(std::move(o.behaviour)), _init(std::move(o._init))
+ {
+ o.behaviour = disposition();
+ o._init = 0;
+ }
+ //! Copy assign
+ native_handle_type &operator=(const native_handle_type &) = default;
+ //! Move assign
+ native_handle_type &operator=(native_handle_type &&o) noexcept
+ {
+ behaviour = std::move(o.behaviour);
+ _init = std::move(o._init);
+ o.behaviour = disposition();
+ o._init = 0;
+ return *this;
+ }
+ //! Swaps with another instance
+ void swap(native_handle_type &o) noexcept
+ {
+ std::swap(behaviour, o.behaviour);
+ std::swap(_init, o._init);
+ }
+
+ //! True if valid
+ explicit constexpr operator bool() const noexcept { return _init != -1 && static_cast<unsigned>(behaviour) != 0; }
+ //! True if invalid
+ constexpr bool operator!() const noexcept { return _init == -1 || static_cast<unsigned>(behaviour) == 0; }
+
+ //! True if the handle is readable
+ constexpr bool is_readable() const noexcept { return behaviour && disposition::readable; }
+ //! True if the handle is writable
+ constexpr bool is_writable() const noexcept { return behaviour && disposition::writable; }
+ //! True if the handle is append only
+ constexpr bool is_append_only() const noexcept { return behaviour && disposition::append_only; }
+
+ //! True if overlapped
+ constexpr bool is_overlapped() const noexcept { return behaviour && disposition::overlapped; }
+ //! True if seekable
+ constexpr bool is_seekable() const noexcept { return behaviour && disposition::seekable; }
+ //! True if requires aligned i/o
+ constexpr bool requires_aligned_io() const noexcept { return behaviour && disposition::aligned_io; }
+
+ //! True if a regular file or device
+ constexpr bool is_regular() const noexcept { return behaviour && disposition::file; }
+ //! True if a directory
+ constexpr bool is_directory() const noexcept { return behaviour && disposition::directory; }
+ //! True if a symlink
+ constexpr bool is_symlink() const noexcept { return behaviour && disposition::symlink; }
+ //! True if a multiplexer like BSD kqueues, Linux epoll or Windows IOCP
+ constexpr bool is_multiplexer() const noexcept { return behaviour && disposition::multiplexer; }
+ //! True if a process
+ constexpr bool is_process() const noexcept { return behaviour && disposition::process; }
+};
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+
+#endif
diff --git a/include/boost/afio/v2/statfs.hpp b/include/boost/afio/v2/statfs.hpp
new file mode 100644
index 00000000..e5c0f471
--- /dev/null
+++ b/include/boost/afio/v2/statfs.hpp
@@ -0,0 +1,123 @@
+/* statfs.hpp
+Information about the volume storing a file
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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_STATFS_H
+#define BOOST_AFIO_STATFS_H
+
+#include "config.hpp"
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+class handle;
+
+/*! \struct statfs_t
+\brief Metadata about a filing system. Unsupported entries are all bits set.
+*/
+struct BOOST_AFIO_DECL 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) */
+ fixme_path f_mntonname; /*!< directory on which mounted (Windows, POSIX) */
+
+ //! Used to indicate what metadata should be filled in
+ BOOST_AFIO_BITFIELD_BEGIN(want)
+ {
+ flags=1<<0,
+ bsize=1<<1,
+ iosize=1<<2,
+ blocks=1<<3,
+ bfree=1<<4,
+ bavail=1<<5,
+ files=1<<6,
+ ffree=1<<7,
+ namemax=1<<8,
+ owner=1<<9,
+ fsid=1<<10,
+ fstypename=1<<11,
+ mntfromname=1<<12,
+ mntonname=1<<13,
+ all=(unsigned)-1
+ }
+ BOOST_AFIO_BITFIELD_END(want)
+ //! Constructs a default initialised instance (all bits set)
+ statfs_t()
+ {
+ size_t frontbytes = ((char *)&f_fstypename) - ((char *) this);
+ memset(this, 0xff, frontbytes);
+ memset(this, 0, sizeof(f_flags));
+ }
+ //! Constructs a filled instance, throwing as an exception any error which might occur
+ statfs_t(handle &h, want wanted = want::all) : statfs_t()
+ {
+ auto v(fill(h, wanted));
+ if (v.has_error())
+ throw std::system_error(v.get_error());
+ }
+ //! Fills in the structure with metadata, returning number of items filled in
+ BOOST_AFIO_HEADERS_ONLY_MEMFUNC_SPEC result<size_t> fill(handle &h, want wanted = want::all) noexcept;
+};
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+# if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+# define BOOST_AFIO_INCLUDED_BY_HEADER 1
+# ifdef WIN32
+# include "detail/impl/windows/statfs.ipp"
+# else
+# include "detail/impl/posix/statfs.ipp"
+# endif
+# undef BOOST_AFIO_INCLUDED_BY_HEADER
+# endif
+
+#endif
diff --git a/include/boost/afio/v2/storage_profile.hpp b/include/boost/afio/v2/storage_profile.hpp
new file mode 100644
index 00000000..0cde612d
--- /dev/null
+++ b/include/boost/afio/v2/storage_profile.hpp
@@ -0,0 +1,294 @@
+/* storage_profile.hpp
+A profile of an OS and filing system
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+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_STORAGE_PROFILE_H
+#define BOOST_AFIO_STORAGE_PROFILE_H
+
+#include "io_service.hpp"
+
+#include <regex>
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+namespace storage_profile
+{
+ //! Types potentially storable in a storage profile
+ enum class storage_types
+ {
+ unknown,
+ extent_type,
+ unsigned_int,
+ unsigned_long_long,
+ float_,
+ string
+ };
+ struct storage_profile;
+
+ //! Returns the enum matching type T
+ template <class T> constexpr storage_types map_to_storage_type()
+ {
+ static_assert(0 == sizeof(T), "Unsupported storage_type");
+ return storage_types::unknown;
+ }
+ //! Specialise for a different default value for T
+ template <class T> constexpr T default_value() { return T{}; }
+
+ template <> constexpr storage_types map_to_storage_type<io_service::extent_type>() { return storage_types::extent_type; }
+ template <> constexpr io_service::extent_type default_value<io_service::extent_type>() { return (io_service::extent_type) -1; }
+ template <> constexpr storage_types map_to_storage_type<unsigned int>() { return storage_types::unsigned_int; }
+ // template<> constexpr storage_types map_to_storage_type<unsigned long long>() { return storage_types::unsigned_long_long; }
+ template <> constexpr storage_types map_to_storage_type<float>() { return storage_types::float_; }
+ template <> constexpr storage_types map_to_storage_type<std::string>() { return storage_types::string; }
+
+ //! Common base class for items
+ struct item_base
+ {
+ static constexpr size_t item_size = 128;
+ //! The type of handle used for testing
+ using handle_type = file_handle;
+
+ const char *name; //!< The name of the item in colon delimited category format
+ const char *description; //!< Some description of the item
+ storage_types type; //!< The type of the value
+ protected:
+ constexpr item_base(const char *_name, const char *_desc, storage_types _type)
+ : name(_name)
+ , description(_desc)
+ , type(_type)
+ {
+ }
+ };
+ //! A tag-value item in the storage profile where T is the type of value stored.
+ template <class T> struct item : public item_base
+ {
+ static constexpr size_t item_size = item_base::item_size;
+ using handle_type = item_base::handle_type;
+ using callable = outcome<void> (*)(storage_profile &sp, handle_type &h);
+
+ callable impl;
+ T value; //!< The storage of the item
+ char _padding[item_size - sizeof(item_base) - sizeof(callable) - sizeof(T)];
+ constexpr item(const char *_name, callable c, const char *_desc = nullptr, T _value = default_value<T>())
+ : item_base(_name, _desc, map_to_storage_type<T>())
+ , impl(c)
+ , value(_value)
+ , _padding{0}
+ {
+ static_assert(sizeof(*this) == item_size, "");
+ }
+ //! Clear this item, returning value to default
+ void clear() { value = default_value<T>(); }
+ //! Set this item if its value is default
+ outcome<void> operator()(storage_profile &sp, handle_type &h) const
+ {
+ if(value != default_value<T>())
+ return make_ready_outcome<void>();
+ return impl(sp, h);
+ }
+ };
+ //! A type erased tag-value item
+ struct item_erased : public item_base
+ {
+ static constexpr size_t item_size = item_base::item_size;
+ using handle_type = item_base::handle_type;
+ char _padding[item_size - sizeof(item_base)];
+
+ item_erased() = delete;
+ ~item_erased() = delete;
+ item_erased(const item_erased &) = delete;
+ item_erased(item_erased &&) = delete;
+ item_erased &operator=(const item_erased &) = delete;
+ item_erased &operator=(item_erased &&) = delete;
+ //! Call the callable with the unerased type
+ template <class U> auto invoke(U &&f) const
+ {
+ switch(type)
+ {
+ case storage_types::extent_type:
+ return f(*static_cast<const item<io_service::extent_type> *>(static_cast<const item_base *>(this)));
+ case storage_types::unsigned_int:
+ return f(*static_cast<const item<unsigned int> *>(static_cast<const item_base *>(this)));
+ case storage_types::unsigned_long_long:
+ return f(*static_cast<const item<unsigned long long> *>(static_cast<const item_base *>(this)));
+ case storage_types::float_:
+ return f(*static_cast<const item<float> *>(static_cast<const item_base *>(this)));
+ case storage_types::string:
+ return f(*static_cast<const item<std::string> *>(static_cast<const item_base *>(this)));
+ case storage_types::unknown:
+ break;
+ }
+ throw std::invalid_argument("No type set in item");
+ }
+ //! Set this item if its value is default
+ outcome<void> operator()(storage_profile &sp, handle_type &h) const
+ {
+ return invoke([&sp, &h](auto &item) { return item(sp, h); });
+ }
+ };
+
+ namespace system
+ {
+ // OS name, version
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<void> os(storage_profile &sp, file_handle &h) noexcept;
+ // CPU name, architecture, physical cores
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<void> cpu(storage_profile &sp, file_handle &h) noexcept;
+ // System memory quantity, in use, max and min bandwidth
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<void> mem(storage_profile &sp, file_handle &h) noexcept;
+#ifdef WIN32
+ namespace windows
+ {
+#else
+ namespace posix
+ {
+#endif
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<void> _mem(storage_profile &sp, file_handle &h) noexcept;
+ }
+ }
+ namespace storage
+ {
+ // Device name, size, min i/o size
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<void> device(storage_profile &sp, file_handle &h) noexcept;
+ // FS name, config, size, in use
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<void> fs(storage_profile &sp, file_handle &h) noexcept;
+#ifdef WIN32
+ namespace windows
+ {
+#else
+ namespace posix
+ {
+#endif
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<void> _device(storage_profile &sp, file_handle &h, std::string mntfromname, std::string fstypename) noexcept;
+ }
+ }
+ namespace concurrency
+ {
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<void> atomic_rewrite_quantum(storage_profile &sp, file_handle &h) noexcept;
+ BOOST_AFIO_HEADERS_ONLY_FUNC_SPEC outcome<void> atomic_rewrite_offset_boundary(storage_profile &sp, file_handle &h) noexcept;
+ }
+
+ //! A (possibly incomplet) profile of storage
+ struct BOOST_AFIO_DECL storage_profile
+ {
+ //! The size type
+ using size_type = size_t;
+
+ private:
+ size_type _size;
+
+ public:
+ storage_profile()
+ : _size(0)
+ {
+ }
+
+ //! Value type
+ using value_type = item_erased &;
+ //! Reference type
+ using reference = item_erased &;
+ //! Const reference type
+ using const_reference = const item_erased &;
+ //! Iterator type
+ using iterator = item_erased *;
+ //! Const iterator type
+ using const_iterator = const item_erased *;
+ //! The type of handle used for testing
+ using handle_type = item_base::handle_type;
+
+ //! True if this storage profile is empty
+ bool empty() const noexcept { return _size == 0; }
+ //! Items in this storage profile
+ size_type size() const noexcept { return _size; }
+ //! Potential items in this storage profile
+ size_type max_size() const noexcept { return (sizeof(*this) - sizeof(_size)) / item_base::item_size; }
+ //! Returns an iterator to the first item
+ iterator begin() noexcept { return static_cast<item_erased *>(static_cast<item_base *>(&os_name)); }
+ //! Returns an iterator to the last item
+ iterator end() noexcept { return begin() + max_size(); }
+ //! Returns an iterator to the first item
+ const_iterator begin() const noexcept { return static_cast<const item_erased *>(static_cast<const item_base *>(&os_name)); }
+ //! Returns an iterator to the last item
+ const_iterator end() const noexcept { return begin() + max_size(); }
+
+ //! Read the matching items in the storage profile from in as YAML
+ void read(std::istream &in, std::regex which = std::regex(".*"));
+ //! Write the matching items from storage profile as YAML to out with the given indentation
+ void write(std::ostream &out, std::regex which = std::regex(".*"), size_t _indent = 0, bool invert_which = false) const;
+
+ // System characteristics
+ item<std::string> os_name = {"system:os:name", &system::os}; // e.g. Microsoft Windows NT
+ item<std::string> os_ver = {"system:os:ver", &system::os}; // e.g. 10.0.10240
+ item<std::string> cpu_name = {"system:cpu:name", &system::cpu}; // e.g. Intel Haswell
+ item<std::string> cpu_architecture = {"system:cpu:architecture", &system::cpu}; // e.g. x64
+ item<unsigned> cpu_physical_cores = {"system:cpu:physical_cores", &system::cpu};
+ item<unsigned long long> mem_max_bandwidth = {"system:mem:max_bandwidth", system::mem, "Main memory bandwidth when accessed sequentially"};
+ item<unsigned long long> mem_min_bandwidth = {"system:mem:min_bandwidth", system::mem, "Main memory bandwidth when 4Kb pages are accessed randomly"};
+ item<unsigned long long> mem_quantity = {"system:mem:quantity", &system::mem};
+ item<float> mem_in_use = {"system:mem:in_use", &system::mem}; // not including caches etc.
+
+ // Controller characteristics
+ item<std::string> controller_type = {"storage:controller:kind", &storage::device}; // e.g. SATA
+ item<unsigned> controller_max_transfer = {"storage:controller:max_transfer", storage::device, "The maximum number of bytes the disk controller can transfer at once"};
+ item<unsigned> controller_max_buffers = {"storage:controller:max_buffers", storage::device, "The maximum number of scatter-gather buffers the disk controller can handle"};
+
+ // Storage characteristics
+ item<std::string> device_name = {"storage:device:name", &storage::device}; // e.g. WDC WD30EFRX-68EUZN0
+ item<unsigned> device_min_io_size = {"storage:device:min_io_size", &storage::device}; // e.g. 4096
+ item<io_service::extent_type> device_size = {"storage:device:size", &storage::device};
+
+ // Filing system characteristics
+ item<std::string> fs_name = {"storage:fs:name", &storage::fs};
+ item<std::string> fs_config = {"storage:fs:config", &storage::fs}; // POSIX mount options, ZFS pool properties etc
+ // item<std::string> fs_ffeatures = { "storage:fs:features" }; // Standardised features???
+ item<io_service::extent_type> fs_size = {"storage:fs:size", &storage::fs};
+ item<float> fs_in_use = {"storage:fs:in_use", &storage::fs};
+
+ // Test results on this filing system, storage and system
+ item<io_service::extent_type> atomic_rewrite_quantum = {"concurrency:atomic_rewrite_quantum", concurrency::atomic_rewrite_quantum, "The i/o modify quantum guaranteed to be atomically visible to readers irrespective of rewrite quantity"};
+ item<io_service::extent_type> max_aligned_atomic_rewrite = {"concurrency:max_aligned_atomic_rewrite", concurrency::atomic_rewrite_quantum,
+ "The maximum single aligned i/o modify quantity atomically visible to readers (can be [potentially unreliably] much larger than atomic_rewrite_quantum). "
+ "A very common value on modern hardware with direct i/o thanks to PCIe DMA is 4096, don't trust values higher than this because of potentially discontiguous memory page mapping."};
+ item<io_service::extent_type> atomic_rewrite_offset_boundary = {"concurrency:atomic_rewrite_offset_boundary", concurrency::atomic_rewrite_offset_boundary, "The multiple of offset in a file where update atomicity breaks, so if you wrote 4096 bytes at a 512 offset and "
+ "this value was 4096, your write would tear at 3584 because all writes would tear on a 4096 offset multiple. "
+ "Linux has a famously broken kernel i/o design which causes this value to be a page multiple, except on "
+ "filing systems which take special measures to work around it. Windows NT appears to lose all atomicity as soon as "
+ "an i/o straddles a 4096 file offset multiple and DMA suddenly goes into many 64 byte cache lines :(, so if "
+ "this value is less than max_aligned_atomic_rewrite and some multiple of the CPU cache line size then this is "
+ "what has happened."};
+ };
+}
+
+BOOST_AFIO_V2_NAMESPACE_END
+
+#if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+#define BOOST_AFIO_INCLUDED_BY_HEADER 1
+#include "detail/impl/storage_profile.ipp"
+#undef BOOST_AFIO_INCLUDED_BY_HEADER
+#endif
+
+#endif
diff --git a/include/boost/afio/v2/utils.hpp b/include/boost/afio/v2/utils.hpp
new file mode 100644
index 00000000..c0ca9b91
--- /dev/null
+++ b/include/boost/afio/v2/utils.hpp
@@ -0,0 +1,629 @@
+/* utils.hpp
+Misc utilities
+(C) 2015 Niall Douglas http://www.nedprod.com/
+File Created: Dec 2015
+
+
+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_UTILS_H
+#define BOOST_AFIO_UTILS_H
+
+#include "config.hpp"
+
+BOOST_AFIO_V2_NAMESPACE_BEGIN
+
+//! 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 constexpr char table[] = "0123456789abcdef";
+ if (outlen<inlen * 2)
+ 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)
+ throw std::invalid_argument("Input buffer not multiple of two.");
+ if (outlen<inlen / 2)
+ 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 constexpr 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)
+ 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 constexpr 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))
+ throw std::runtime_error("secdec_ecc: 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)
+ throw std::runtime_error("secdec_ecc: 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
+
+# if BOOST_AFIO_HEADERS_ONLY == 1 && !defined(DOXYGEN_SHOULD_SKIP_THIS)
+# define BOOST_AFIO_INCLUDED_BY_HEADER 1
+# ifdef WIN32
+# include "detail/impl/windows/utils.ipp"
+# else
+# include "detail/impl/posix/utils.ipp"
+# endif
+# undef BOOST_AFIO_INCLUDED_BY_HEADER
+# endif
+
+
+#endif
diff --git a/meta/libraries.json b/meta/libraries.json
new file mode 100644
index 00000000..a8bedf4c
--- /dev/null
+++ b/meta/libraries.json
@@ -0,0 +1,16 @@
+{
+ "key": "afio",
+ "name": "Boost.AFIO",
+ "authors": [ "Niall Douglas", "Paul Kirth" ],
+ "maintainers": [ "Niall Douglas" ],
+ "description": "A portable asynchronous file i/o library extending Boost.ASIO.",
+ "std": [ "11" ],
+ "category": [
+ "Function-objects",
+ "Concurrent",
+ "Domain Specific",
+ "IO",
+ "Programming Interfaces",
+ "System"
+ ]
+}
diff --git a/reference/11.1.pdf b/reference/11.1.pdf
new file mode 100644
index 00000000..9a36eb1b
--- /dev/null
+++ b/reference/11.1.pdf
Binary files differ
diff --git a/reference/54d0f0190cf29ca811040c8a.pdf b/reference/54d0f0190cf29ca811040c8a.pdf
new file mode 100644
index 00000000..1f703f04
--- /dev/null
+++ b/reference/54d0f0190cf29ca811040c8a.pdf
Binary files differ
diff --git a/reference/Chidambaram.pdf b/reference/Chidambaram.pdf
new file mode 100644
index 00000000..5957afda
--- /dev/null
+++ b/reference/Chidambaram.pdf
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct.html b/reference/Error Handling is Ocassionally Correct.html
new file mode 100644
index 00000000..ff3c38a1
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct.html
@@ -0,0 +1,2434 @@
+
+<!-- saved from url=(0089)https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html -->
+<html><head><meta http-equiv="Content-Type" content="text/html; charset=windows-1252"><script src="https://js-agent.newrelic.com/nr-885.min.js"></script><script type="text/javascript">window.NREUM||(NREUM={}),__nr_require=function(e,t,n){function r(n){if(!t[n]){var o=t[n]={exports:{}};e[n][0].call(o.exports,function(t){var o=e[n][1][t];return r(o||t)},o,o.exports)}return t[n].exports}if("function"==typeof __nr_require)return __nr_require;for(var o=0;o<n.length;o++)r(n[o]);return r}({QJf3ax:[function(e,t){function n(){}function r(e){function t(e){return e&&e instanceof n?e:e?a(e,i,o):o()}function s(n,r,o){e&&e(n,r,o);for(var i=t(o),a=l(n),u=a.length,f=0;u>f;f++)a[f].apply(i,r);var s=c[w[n]];return s&&s.push([h,n,r,i]),i}function p(e,t){g[e]=l(e).concat(t)}function l(e){return g[e]||[]}function d(e){return f[e]=f[e]||r(s)}function v(e,t){u(e,function(e,n){t=t||"feature",w[n]=t,t in c||(c[t]=[])})}var g={},w={},h={on:p,emit:s,get:d,listeners:l,context:t,buffer:v};return h}function o(){return new n}var i="nr@context",a=e("gos"),u=e(1),c={},f={},s=t.exports=r();s.backlog=c},{1:12,gos:"7eSDFh"}],ee:[function(e,t){t.exports=e("QJf3ax")},{}],3:[function(e,t){function n(e,t){return function(){r(e,[(new Date).getTime()].concat(i(arguments)),null,t)}}var r=e("handle"),o=e(1),i=e(2);"undefined"==typeof window.newrelic&&(newrelic=NREUM);var a=["setPageViewName","addPageAction","setCustomAttribute","finished","addToTrace","inlineHit"],u=["addPageAction"],c="api-";o(a,function(e,t){newrelic[t]=n(c+t,"api")}),o(u,function(e,t){newrelic[t]=n(c+t)}),t.exports=newrelic,newrelic.noticeError=function(e){"string"==typeof e&&(e=new Error(e)),r("err",[e,(new Date).getTime()])}},{1:12,2:13,handle:"D5DuLP"}],gos:[function(e,t){t.exports=e("7eSDFh")},{}],"7eSDFh":[function(e,t){function n(e,t,n){if(r.call(e,t))return e[t];var o=n();if(Object.defineProperty&&Object.keys)try{return Object.defineProperty(e,t,{value:o,writable:!0,enumerable:!1}),o}catch(i){}return e[t]=o,o}var r=Object.prototype.hasOwnProperty;t.exports=n},{}],handle:[function(e,t){t.exports=e("D5DuLP")},{}],D5DuLP:[function(e,t){function n(e,t,n,o){r.buffer([e],o),r.emit(e,t,n)}var r=e("ee").get("handle");t.exports=n,n.ee=r},{ee:"QJf3ax"}],XL7HBI:[function(e,t){function n(e){var t=typeof e;return!e||"object"!==t&&"function"!==t?-1:e===window?0:i(e,o,function(){return r++})}var r=1,o="nr@id",i=e("gos");t.exports=n},{gos:"7eSDFh"}],id:[function(e,t){t.exports=e("XL7HBI")},{}],G9z0Bl:[function(e,t){function n(){if(!v++){var e=d.info=NREUM.info,t=f.getElementsByTagName("script")[0];if(e&&e.licenseKey&&e.applicationID&&t){u(p,function(t,n){e[t]||(e[t]=n)});var n="https"===s.split(":")[0]||e.sslForHttp;d.proto=n?"https://":"http://",a("mark",["onload",i()],null,"api");var r=f.createElement("script");r.src=d.proto+e.agent,t.parentNode.insertBefore(r,t)}}}function r(){"complete"===f.readyState&&o()}function o(){a("mark",["domContent",i()],null,"api")}function i(){return(new Date).getTime()}var a=e("handle"),u=e(1),c=window,f=c.document;NREUM.o={ST:setTimeout,XHR:c.XMLHttpRequest,REQ:c.Request,EV:c.Event,PR:c.Promise,MO:c.MutationObserver},e(2);var s=(""+location).split("?")[0],p={beacon:"bam.nr-data.net",errorBeacon:"bam.nr-data.net",agent:"js-agent.newrelic.com/nr-885.min.js"},l=window.XMLHttpRequest&&XMLHttpRequest.prototype&&XMLHttpRequest.prototype.addEventListener&&!/CriOS/.test(navigator.userAgent),d=t.exports={offset:i(),origin:s,features:{},xhrWrappable:l};f.addEventListener?(f.addEventListener("DOMContentLoaded",o,!1),c.addEventListener("load",n,!1)):(f.attachEvent("onreadystatechange",r),c.attachEvent("onload",n)),a("mark",["firstbyte",i()],null,"api");var v=0},{1:12,2:3,handle:"D5DuLP"}],loader:[function(e,t){t.exports=e("G9z0Bl")},{}],12:[function(e,t){function n(e,t){var n=[],o="",i=0;for(o in e)r.call(e,o)&&(n[i]=t(o,e[o]),i+=1);return n}var r=Object.prototype.hasOwnProperty;t.exports=n},{}],13:[function(e,t){function n(e,t,n){t||(t=0),"undefined"==typeof n&&(n=e?e.length:0);for(var r=-1,o=n-t||0,i=Array(0>o?0:o);++r<o;)i[r]=e[t+r];return i}t.exports=n},{}]},{},["G9z0Bl"]);</script>
+<title>main</title>
+<meta name="description" content="main">
+<meta name="keywords" content="main">
+<meta name="resource-type" content="document">
+<meta name="distribution" content="global">
+
+<meta name="Generator" content="LaTeX2HTML v2002-2-1">
+<meta http-equiv="Content-Style-Type" content="text/css">
+
+<link rel="STYLESHEET" href="./Error Handling is Ocassionally Correct_files/main.css">
+
+</head>
+
+<body><a href="http://www.usenix.org/"><img src="./Error Handling is Ocassionally Correct_files/new_usenix.jpg" width="288" height="232" alt="Check out the new USENIX Web site." align="right"></a>
+
+
+
+
+
+<h1 align="CENTER">EIO: <u>E</u>rror Handling <u>i</u>s <u>O</u>ccasionally Correct</h1><div>
+
+<p align="CENTER"><strong>Haryadi S. Gunawi, Cindy Rubio-Gonzlez,</strong><br>
+<strong>Andrea C. Arpaci-Dusseau, Remzi H. Arpaci-Dusseau, Ben Liblit</strong></p>
+<p align="CENTER"><em>Computer Sciences Department, University of Wisconsin-Madison</em> </p>
+</div>
+
+
+<h1>Abstract</h1>
+
+<p>
+<em>The reliability of file systems depends in part on how well they
+propagate errors. We develop a static analysis technique, EDP, that
+analyzes how file systems and storage device drivers propagate error
+codes. Running our EDP analysis on all file systems and 3 major
+storage device drivers in Linux 2.6, we find that errors are often
+incorrectly propagated; 1153 calls (13%) drop an error code without
+handling it.
+</em>
+</p><p>
+<em>We perform a set of analyses to rank the robustness of each subsystem
+based on the completeness of its error propagation; we find that many
+popular file systems are less robust than other available choices. We
+confirm that write errors are neglected more often than read
+errors. We also find that many violations are not corner-case
+mistakes, but perhaps intentional choices. Finally, we show that
+inter-module calls play a part in incorrect error propagation, but
+that chained propagations do not. In conclusion, error propagation
+appears complex and hard to perform correctly in modern systems. </em>
+
+
+</p><h1><a name="SECTION00020000000000000000"></a>
+<a name="sec-intro"></a><br>
+1 Introduction
+</h1>
+
+<p>
+The robustness of file systems and storage systems is a major concern,
+and rightly so&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#YangEtAl04-FSErrors">32</a>]. Recent work has shown that
+file systems are especially unreliable when the underlying disk system
+does not behave as expected&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#PrabhakaranEtAl05-SOSP">20</a>].
+Specifically, many modern commodity file systems, such as Linux
+ext3&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#Tweedie98-JournalingExt2">31</a>],
+ReiserFS&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#Reiser04-ReiserFS">23</a>], IBM's JFS&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#Best00-JFS-Local">1</a>], and
+Windows NTFS&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#Solomon98-NT">27</a>], all have serious bugs and
+inconsistencies in how they handle errors from the storage system.
+However, the question remains unanswered as to why these
+fault-handling bugs are present.
+
+</p><p>
+In this paper, we investigate what we believe is one of the root
+causes of deficient fault handling: <em>incorrect error code
+propagation</em>. To be properly handled, a low-level error code (<i>e.g.</i>, an
+"I/O error" returned from a device driver) must be correctly
+propagated to the appropriate code in the file system. Further, if the
+file system is unable to recover from the fault, it may wish to pass
+the error up to the application, again requiring correct error
+propagation.
+
+</p><p>
+Without correct error propagation, any comprehensive failure policy is
+useless: recovery mechanisms and policies cannot be invoked if the
+error is not propagated. Incorrect error propagation has been a
+significant problem in many systems. For example, self-healing
+systems cannot heal themselves if error signals never reach the
+self-recovery
+modules&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#EllardMegquier05-DISP">6</a>,<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#SidiroglouEtAl05-STEM">26</a>], components
+behind an interface do not receive error
+notifications&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#KoopmanDeVale99-POSIX">16</a>], and distributed systems
+often obtain misleading error
+codes&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#KolaEtAl05-FaultInLDS">15</a>,<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#ThainLivny02-ErrorScope">30</a>], which
+turns into frustration for human debugging. In summary, if errors are
+not propagated, then the effort spent detecting and recovering from
+those
+errors&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#CandeaEtAl04-Reboot">4</a>,<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#CowanEtAl98-Stackguard">5</a>,<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#NeculaEtAl05-CCured">18</a>,<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#QinEtAl05-Safemem">21</a>,<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#QinEtAl05-Rx">22</a>,<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#SwiftEtAl03-Nooks">28</a>,<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#SwiftEtAl04-MoreNooks">29</a>]
+is worthless.
+
+</p><p>
+To analyze how errors are propagated in file and storage system code,
+we have developed a static source-code analysis technique. Our
+technique, named <em>Error Detection and Propagation (EDP)</em> analysis,
+shows how error codes flow through the file system and storage
+drivers. EDP performs a dataflow analysis by constructing a
+function-call graph showing how error codes propagate through return
+values and function parameters.
+
+</p><p>
+We have applied EDP analysis to all file systems and 3 major storage
+device drivers (SCSI, IDE, and Software RAID) implemented in Linux
+2.6. We find that <em>error handling is occasionally correct</em>.
+Specifically, we see that low-level errors are sometimes lost as they
+travel through the many layers of the storage subsystem: out of the
+9022 function calls through which the analyzed error codes
+propagate, we find that 1153 calls (13%) do not correctly save the
+propagated error codes.
+
+</p><p>
+Our detailed analysis enables us to make a number of conclusions.
+First, we find that the more complex the file system (in terms of both
+lines of code and number of function calls with error codes), the more
+likely it is to incorrectly propagate errors; thus, these more complex
+file systems are more likely to suffer from silent failures. Second,
+we observe that I/O write operations are more likely to neglect error
+codes than I/O read operations. Third, we find that many violations
+are not corner-case mistakes: the return codes of some functions are
+consistently ignored, which makes us suspect that the omissions are
+intentional. Finally, we show how inter-module calls play a major
+part in causing incorrect error propagation, but that chained
+propagations do not.
+
+</p><p>
+The rest of this paper is organized as follows. We describe our
+methodology and present our results in Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-method">2</a> and
+&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-result">3</a> respectively. To understand the root causes of the
+problem, we perform a set of deeper analyses in
+Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-analysis">4</a>. Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-future">5</a>
+and&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-related">6</a> discuss future work and related work
+respectively. Finally, Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-conclude">7</a> concludes.
+
+</p><h1><a name="SECTION00030000000000000000"></a>
+<a name="sec-method"></a><br>
+2 Methodology
+</h1>
+
+<p>
+To understand the propagation of error codes, we have developed
+a static analysis technique that we name <em>Error Detection and
+Propagation (EDP)</em>. In this section, we identify the components of
+Linux 2.6 that we will analyze and describe EDP.
+
+</p><p>
+
+</p><h2><a name="SECTION00031000000000000000"><br>
+2.1 Target Systems</a>
+</h2>
+
+<p>
+In this paper, we analyze how errors are propagated through the file
+systems and storage device drivers in Linux 2.6.15.4. We examine all
+Linux implementations of file systems that are located in 51
+directories. These file systems are of different types, including
+disk-based file systems,
+network file systems,
+file system protocols,
+and many others. Our analysis follows requests through the virtual
+file system and memory management layers as well. In addition to file
+systems, we also examine three major storage device drivers (SCSI,
+IDE, and software RAID), as well as all lower-level drivers. Beyond
+these subsystems, our tool can be used to analyze other Linux
+components as well.
+
+</p><p>
+
+</p><div align="CENTER">
+
+<p><a name="fig-method-edp"></a></p><div align="CENTER">
+<img src="./Error Handling is Ocassionally Correct_files/fig-method-edp.gif"></div>
+<br>
+<font size="-1"><i>
+Figure 1: <b>EDP Architecture.</b>The diagram shows the
+framework for Error Detection and Propagation (EDP) analysis of file
+and storage systems code.</i></font>
+<br>
+
+</div>
+
+<p>
+
+</p><h2><a name="SECTION00032000000000000000"><br>
+2.2 EDP Analysis</a>
+</h2>
+
+<p>
+The basic mechanism of EDP is a dataflow analysis: EDP constructs a
+function-call graph covering all cases in which error codes propagate
+through return values or function parameters. To build EDP, we
+harness C Intermediate Language (CIL)&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#Necula02-CIL">19</a>]. CIL
+performs source-to-source transformation of C programs and thus can be
+used in the analysis of large complex programs such as the Linux
+kernel. The EDP analysis is written as a CIL extension in 4000 lines
+of code in the OCaml language.
+
+</p><p>
+The abstraction that we introduce in EDP is that error codes flow
+along <em>channels</em>, where a channel is the set of function calls
+between where an error code is first generated and where it is
+terminated (<i>e.g.</i>, by being either handled or dropped). As shown in
+Figure&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-method-edp">1</a>, EDP contains three major components. The
+first component identifies the error codes that will be tracked. The
+second constructs the channels along which the error codes propagate.
+Finally, the third component analyzes the channels and classifies each
+as being either complete or broken.
+
+</p><p>
+<br></p><div align="CENTER">
+<table cellpadding="3" border="1" align="CENTER">
+<tbody><tr><td><font color="#FFFFFF">-</font></td>
+<td align="CENTER" colspan="1"><font size="-1"><b>Single</b></font></td>
+<td align="CENTER" colspan="1"><font size="-1"><b>Full</b></font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+Subsystem</font></td>
+</tr>
+<tr><td align="LEFT" colspan="1"><font size="-1">
+Subsystem</font></td>
+<td align="CENTER" colspan="1"><font size="-1"><b>(seconds)</b></font></td>
+<td align="CENTER" colspan="1"><font size="-1"><b>(seconds)</b></font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+Size (Kloc)</font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+
+VFS </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>4 </b> </font></td>
+<td align="CENTER"><font size="-1"> - </font></td>
+<td align="CENTER"><font size="-1"> 34 </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+Mem. Mgmt. </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>3 </b> </font></td>
+<td align="CENTER"><font size="-1"> - </font></td>
+<td align="CENTER"><font size="-1"> 20 </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+
+XFS </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>8 </b> </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>13 </b> </font></td>
+<td align="CENTER"><font size="-1"> 71 </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+ReiserFS </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>3 </b> </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>8 </b> </font></td>
+<td align="CENTER"><font size="-1"> 24 </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+ext3 </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>2 </b> </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>7 </b> </font></td>
+<td align="CENTER"><font size="-1"> 12 </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+Apple HFS </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>1 </b> </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>6 </b> </font></td>
+<td align="CENTER"><font size="-1"> 5 </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+VFAT </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>1 </b> </font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>5 </b> </font></td>
+<td align="CENTER"><font size="-1"> 1 </font></td>
+</tr>
+<tr><td align="LEFT" colspan="2"><font size="-1">
+
+All File Systems Together</font></td>
+<td align="CENTER"><font size="-1"> </font><font size="-1"><b>47 </b> </font></td>
+<td align="CENTER"><font size="-1"> 372 </font></td>
+</tr>
+</tbody></table>
+
+</div>
+<br>
+<a name="table-method-performance"></a>
+<font size="-1">
+<i>Table 1: <b>EDP Performance.</b> The table shows
+the EDP runtime for different subsystems. "Single" runtime
+represents the time to analyze each subsystem in isolation without
+interaction with other subsystems (e.g., VFS and MM).
+"Full" runtime represents the time to analyze a file system along
+with the virtual file system and the memory management. The last row
+reports the time to analyze all of the file systems together. </i></font>
+<br>
+
+<br>
+
+<p>
+Table&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#table-method-performance">1</a> reports the EDP runtime for
+different subsystems, running on a machine with 2.4 GHz Intel Pentium
+4 CPU and 512 MB of memory. Overall, EDP analysis is fast; analyzing
+all file systems together in a single run only takes 47 seconds. We
+now describe the three components of EDP in more detail.
+
+</p><p>
+
+</p><h3><a name="SECTION00032100000000000000"><br>
+2.2.1 Error Code Information</a>
+</h3>
+
+<p>
+The first component of EDP identifies the error codes to track. One
+example is <tt><font size="-1">EIO</font></tt>, a generic error code that commonly indicates I/O
+failure and is used extensively throughout the file system; for
+example, in ext3, <tt><font size="-1">EIO</font></tt> touches 266 functions and propagates through
+467 calls. Besides <tt><font size="-1">EIO</font></tt>, many kernel subsystems commonly use other
+error codes as defined in <tt><font size="-1">include/asm-generic/errno.h</font></tt>. In total,
+there are hundreds of error codes that are used for different
+purposes. We report our findings on the propagation of 34 basic error
+codes that are mostly used across all file systems and storage device
+drivers. These error codes can be found in
+<tt><font size="-1">include/asm-generic/errno-base.h</font></tt>.
+
+</p><p>
+
+</p><h3><a name="SECTION00032200000000000000"><br>
+2.2.2 Channel Construction</a>
+</h3>
+
+<p>
+The second component of EDP constructs the <em>channel</em> in which the
+specified error codes propagate. A channel can be constructed from
+function calls and asynchronous wake-up paths; in our current
+analysis, we focus only on function calls and discuss asynchronous
+paths in Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-future-channel">5.3</a>.
+
+</p><p>
+We define a channel by its two endpoints: generation and termination.
+The <em>generation endpoint</em> is the function that exposes an error
+code, either directly through a return value (<i>e.g.</i>, the function
+contains a <tt><font size="-1">return</font></tt> <tt><font size="-1">-EIO</font></tt> statement) or indirectly through a
+function argument passed by reference. After finding all generation
+endpoints, EDP marks each function that propagates the error codes;
+<em>propagating functions</em> receive error codes from the functions
+that they call and then simply propagate them in a return value or
+function parameter. The <em>termination endpoint</em> is the function in
+which an error code is no longer propagated in the return value or a
+parameter of the function.
+
+</p><p>
+One of the major challenges we address when constructing error
+channels is handling function pointers. The typical approach for
+handling function pointers is to implement a points-to
+analysis&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#Hind01-PointerAnalysis">13</a>] that identifies the set of real
+functions each function pointer might point at; however,
+field-sensitive points-to analyses can be expensive. Therefore, we
+customize our points-to analysis to exploit the systematic structure
+that these pointers exhibit.
+
+</p><p>
+First, we keep track of all structures that have function pointers. For
+example, the VFS read and write interfaces are defined as fields in
+the <tt><font size="-1">file_ops</font></tt> structure:
+
+</p><p>
+</p><pre> struct file_ops {
+ int (*read) ();
+ int (*write) ();
+ };
+</pre>
+
+<p>
+Since each file system needs to define its own <tt><font size="-1">file_ops</font></tt>, we
+automatically find all global instances of such structures, look for
+the function pointer assignments within the instances, and map
+function-pointer implementations to the function pointer interfaces.
+For example, ext2 and ext3 define their file operations like this:
+
+</p><p>
+</p><pre> struct file_ops ext2_f_ops {
+ .read = ext2_read;
+ .write = ext2_write;
+ };
+ struct file_ops ext3_f_ops {
+ .read = ext3_read;
+ .write = ext3_write;
+ };
+</pre>
+
+<p>
+Given such global structure instances, we add the interface
+implementations (<i>e.g.</i>, <tt><font size="-1">ext2_read</font></tt>) to the implementation list of
+the corresponding interfaces (<i>e.g.</i>,
+<tt><font size="-1">file_ops</font></tt>4#4<tt><font size="-1">read</font></tt>). Although this technique
+connects most of the mappings, a function pointer assignment could
+still occur in an instruction rather than in a global structure
+instance. Thus, our tool also visits all functions and finds any
+assignment that maps an implementation to an interface. For example,
+if we find an assignment such as <tt><font size="-1">f_op-&gt;read</font></tt> <tt><font size="-1">=</font></tt>
+<tt><font size="-1">ntfs_read</font></tt>, then we add <tt><font size="-1">ntfs_read</font></tt> to the list of
+<tt><font size="-1">file_ops</font></tt>4#4<tt><font size="-1">read</font></tt> implementations.
+
+</p><p>
+In the last phase, we change function pointer calls to direct
+calls. For example, if VFS makes an interface call such as
+<tt><font size="-1">(f_op-&gt;read)()</font></tt>, then we automatically rewrite such
+an assignment to:
+
+</p><p>
+</p><pre> switch (...) {
+ case ext2: ext2_read(); break;
+ case ext3: ext3_read(); break;
+ case ntfs: ntfs_read(); break;
+ ...
+ }
+</pre>
+
+<p>
+Across all Linux file systems and storage device drivers, there are
+191 structural interfaces (<i>e.g.</i>, <tt><font size="-1">file_ops</font></tt>), 904 function pointer
+fields (<i>e.g.</i>, <tt><font size="-1">read</font></tt>), 5039 implementations (<i>e.g.</i>, <tt><font size="-1">ext2_read</font></tt>),
+and 2685 function pointer calls (<i>e.g.</i>, <tt><font size="-1">(f_op-&gt;read)()</font></tt>). Out of
+2865 function pointer calls, we connect all except 564 calls (20%).
+The unconnected 20% of calls are due to indirect implementation
+assignment. For example, we cannot map assignment such as
+<tt><font size="-1">f_op-&gt;read</font></tt> <tt><font size="-1">=</font></tt> <tt><font size="-1">f</font></tt>, where <tt><font size="-1">f</font></tt> is either a local
+variable or a function parameter, and not a function name. While it
+is feasible to traceback such assignments using stronger and more
+expensive analysis, we assume that major interfaces linking modules
+together have already been connected as part of global instances. If
+all calls are connected, more error propagation chain can be analyzed,
+which means more violations are likely to be found.
+
+
+</p><h3><a name="SECTION00032300000000000000"><br>
+2.2.3 Channel Analysis</a>
+</h3>
+
+<p>
+The third component of EDP distinguishes two kinds of channels:
+error-complete and error-broken channels. An <em>error-complete</em>
+channel is a channel that minimally checks the occurrence of an
+error. An error-complete channel thus has this property at its
+termination endpoint:
+
+</p><p><i>
+&nbsp;&nbsp;&nbsp;&nbsp; &#8707; if (expr) { ... }, where <br>
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errorCodeVariable &#8838; expr
+</i>
+
+
+
+</p><p>
+which states that an error code is considered checked if there exist
+an <tt><font size="-1">if</font></tt> condition whose expression contains the variable that
+stores the error code. For example, the function
+<tt><font size="-1">goodTerminationEndpoint</font></tt> in the code segment below carries an
+error-complete channel because the function saves the returned error
+code (line 2) and checks the error code (line 3):
+
+</p><p>
+</p><pre> 1 void goodTerminationEndpoint() {
+ 2 int err = generationEndpoint();
+ 3 if (err)
+ 4 ...
+ 5 }
+ 6 int generationEndpoint() {
+ 7 return -EIO;
+ 8 }
+</pre>
+
+<p>
+Note that an error could be checked but not handled properly, <i>e.g.</i>&nbsp;no
+error handling in the <tt><font size="-1">if</font></tt> condition. Since error handling is
+usually specific to each file system, and hence there are many
+instances of it, we decided to be "generous" in the way we define
+how error is handled, <i>i.e.</i>&nbsp;by just checking it. More violations
+might be found when we incorporate all instances of error
+handling.
+
+</p><p>
+An <em>error-broken</em> channel is the inverse of an error-complete
+channel. In particular, the error code is either <em>unsaved</em>, <em>unchecked</em>, or <em>overwritten</em>. For example, the function
+<tt><font size="-1">badTerminationEndpoint</font></tt> below carries an error-broken channel of
+unchecked type because the function saves the returned error code
+(line 2) but it never checks the error before the function exits
+(line 3):
+
+</p><p>
+</p><pre> 1 void badTerminationEndpoint() {
+ 2 int err = generationEndpoint();
+ 3 return;
+ 4 }
+</pre>
+
+<p>
+An error-broken channel is a serious file system bug because it can
+lead to a silent failure. In a few cases, we inject faults in
+error-broken channels to confirm the existence of silent failures. We
+utilize our block-level fault injection
+technique&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#PrabhakaranEtAl05-SOSP">20</a>] to exercise error-broken
+channels that relate to disk I/O. In a broken channel, we look for
+two pieces of information: which workload and which failure led us to
+that channel. After finding the necessary information, we run the
+workload, inject the specific block failure, and observe the I/O
+traces and the returned error codes received in upper layers (<i>e.g.</i>, the
+application layer) to confirm whether a broken channel leads to a
+silent failure. The reader will note that our fault-injection
+technique is limited to disk I/O related channels. To exercise all
+error-broken channels, techniques such as symbolic execution and
+directed
+testing&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#EnglerDunbar07-UnderConstrained">9</a>,<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#GodefroidEtAl05-DART">10</a>]
+that simulate the environment of the component in test would be of
+great utility.
+
+</p><p>
+
+</p><h3><a name="SECTION00032400000000000000"><br>
+2.2.4 Limitations</a>
+</h3>
+
+<p>
+Error propagation has complex characteristics: correct error codes
+must be returned; each subsystem uses both generic and specific error
+codes; one error code could be mapped to another; error codes are
+stored not only in scalar variables but also in structures (<i>e.g.</i>,
+control blocks); and error codes flow not only through function calls
+but also asynchronously via interrupts and callbacks.
+In our static analysis, we have not modeled all these characteristics.
+Nevertheless, by just focusing on the propagation of basic error codes
+via function call, we have found numerous violations that need to be
+fixed. A more complete tool that covers the properties above would
+uncover even more incorrect error handling.
+
+
+</p><h1><a name="SECTION00040000000000000000"></a>
+<a name="sec-result"></a><br>
+3 Results
+</h1>
+
+<p>
+We have performed EDP analysis on all file systems and storage device
+drivers in Linux 2.6.15.4. Our analysis studies how 34 basic error
+codes (<i>e.g.</i>, <tt><font size="-1">EIO</font></tt> and <tt><font size="-1">ENOMEM</font></tt>) defined in
+<tt><font size="-1">include/asm-generic/errno-base.h</font></tt> propagate through these
+subsystems. We examine these basic error codes because they involve
+thousands of functions and propagate across thousands of calls.
+
+</p><p>
+In these results, we distinguish two types of violations that make up
+an error-broken channel: unsaved and unchecked error codes
+(overwritten codes have been deferred to future work; see
+Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-future-overwritten">5.1</a> for more information).
+An <em>unsaved error code</em> is found when a callee propagates an error
+code via the return value, but the caller does not save the return
+value (<i>i.e.</i>, it is treated as a void-returning call even though it
+actually returns an error code). Throughout the paper, we refer to
+this type of broken channel as a "<em>bad call</em>." An <em>unchecked
+error code</em> is found when a variable that may contain an error code is
+neither checked nor used in the future; we always refer to this case
+as an unchecked code.
+
+</p><p>
+
+</p><h2><a name="SECTION00041000000000000000"></a>
+<a name="sec-result-unsaved"></a>
+3.1 Unsaved Error Codes
+</h2>
+
+<p>
+First, we report the number of error-broken channels due to a caller
+simply not saving the returned error code (<i>i.e.</i>, the number of bad
+calls). The simplified HFS code below shows an example of unsaved
+error code. The function <tt><font size="-1">find_init</font></tt> accepts a new uninitialized
+<tt><font size="-1">find_data</font></tt> structure (line 2), allocates a memory space for the
+<tt><font size="-1">search_key</font></tt> field (line 3), and returns <tt><font size="-1">ENOMEM</font></tt> error code
+when the memory allocation fails (line 5). However, one of its
+callers, <tt><font size="-1">file_lookup</font></tt>, does not save the returned error code
+(line 10) but tries to access the <tt><font size="-1">search_key</font></tt> field which still
+points to <tt><font size="-1">NULL</font></tt> (line 11). Hence, a null-pointer dereference
+takes place and the system could crash or corrupt data.
+
+</p><p>
+</p><pre> 1 // hfs/bfind.c
+ 2 int find_init(find_data *fd) {
+ 3 fd-&gt;search_key = kmalloc(..)
+ 4 if (!fd-&gt;search_key)
+ 5 return -ENOMEM;
+ 6 ...
+ 7 }
+ 8 // hfs/inode.c
+ 9 int file_lookup() {
+ 10 find_init(fd); /* NOT-SAVED E.C */
+ 11 fd-&gt;search_key-&gt;cat = ...; /* BAD!! */
+ 12 ...
+ 13 }
+</pre>
+
+<p>
+To show how EDP is useful in finding error propagation bugs, we begin
+by showing a sample of EDP analysis for a simple file system, Apple
+HFS. Then, we present our findings on all subsystems that we analyze,
+and finally discuss false positives.
+
+</p><p>
+
+<!-- ------------------------------------- HFS -->
+
+</p><h3><a name="SECTION00041100000000000000"><br>
+3.1.1 EDP on Apple HFS</a>
+</h3>
+
+<p>
+Figure&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-result-big">2</a> depicts the EDP output when analyzing the
+propagation of the 34 basic error codes in the Apple HFS file system.
+There are two important elements that EDP produces in order to ease
+the debugging process. First, EDP generates an error propagation graph
+that only includes functions and function calls through which the
+analyzed error codes propagate. From the graph, one can easily catch
+all bad calls and functions that make the bad calls. Second, EDP
+provides a table that presents more detailed information for each bad
+call (<i>e.g.</i>, the location where the bad call is made).
+
+
+</p><p><a name="fig-result-big"></a></p>
+
+<!-- table-->
+<table border="0" cellspacing="0" cellpadding="0" align="center">
+<tbody><tr><td>
+
+<!-- violation -->
+<div align="CENTER">
+</div><div align="CENTER"><div align="CENTER">
+</div><table width="323">
+<tbody><tr><td>
+ <table cellpadding="3" border="1" align="CENTER">
+<tbody><tr><td align="RIGHT" colspan="1"><font size="-1">
+ </font><font size="-1"><b>Viol#</b></font></td>
+<td align="CENTER" colspan="2"><font size="-1">
+ </font><font size="-1"><b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Caller&nbsp;&nbsp;&#8594;&nbsp;&nbsp;Callee</b></font></td>
+<td align="LEFT" colspan="1"><font size="-1">
+ </font><font size="-1"><b>Filename</b></font></td>
+<td align="RIGHT" colspan="1"><font size="-1">
+ </font><font size="-1"><b>Line#</b></font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+
+</font><font size="-1"><b>A</b> </font></td>
+<td align="RIGHT"><font size="-1"> file_lookup </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> inode.c </font></td>
+<td align="RIGHT"><font size="-1"> 493 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>B</b> </font></td>
+<td align="RIGHT"><font size="-1"> fill_super </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> super.c </font></td>
+<td align="RIGHT"><font size="-1"> 385 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>C</b> </font></td>
+<td align="RIGHT"><font size="-1"> lookup </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> dir.c </font></td>
+<td align="RIGHT"><font size="-1"> 30 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>D</b> </font></td>
+<td align="RIGHT"><font size="-1"> brec_updt_prnt </font></td>
+<td align="LEFT"><font size="-1"> __brec_find </font></td>
+<td align="LEFT"><font size="-1"> brec.c </font></td>
+<td align="RIGHT"><font size="-1"> 405 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>E</b> </font></td>
+<td align="RIGHT"><font size="-1"> brec_updt_prnt </font></td>
+<td align="LEFT"><font size="-1"> __brec_find </font></td>
+<td align="LEFT"><font size="-1"> brec.c </font></td>
+<td align="RIGHT"><font size="-1"> 345 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>F</b> </font></td>
+<td align="RIGHT"><font size="-1"> cat_delete </font></td>
+<td align="LEFT"><font size="-1"> free_fork </font></td>
+<td align="LEFT"><font size="-1"> catalog.c </font></td>
+<td align="RIGHT"><font size="-1"> 228 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>G</b> </font></td>
+<td align="RIGHT"><font size="-1"> cat_delete </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> catalog.c </font></td>
+<td align="RIGHT"><font size="-1"> 213 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>H</b> </font></td>
+<td align="RIGHT"><font size="-1"> cat_create </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> catalog.c </font></td>
+<td align="RIGHT"><font size="-1"> 95 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>I</b> </font></td>
+<td align="RIGHT"><font size="-1"> file_trunc </font></td>
+<td align="LEFT"><font size="-1"> free_exts </font></td>
+<td align="LEFT"><font size="-1"> extent.c </font></td>
+<td align="RIGHT"><font size="-1"> 507 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>J</b> </font></td>
+<td align="RIGHT"><font size="-1"> file_trunc </font></td>
+<td align="LEFT"><font size="-1"> free_exts </font></td>
+<td align="LEFT"><font size="-1"> extent.c </font></td>
+<td align="RIGHT"><font size="-1"> 497 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>K</b> </font></td>
+<td align="RIGHT"><font size="-1"> file_trunc </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> extent.c </font></td>
+<td align="RIGHT"><font size="-1"> 494 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>L</b> </font></td>
+<td align="RIGHT"><font size="-1"> ext_write_ext </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> extent.c </font></td>
+<td align="RIGHT"><font size="-1"> 135 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>M</b> </font></td>
+<td align="RIGHT"><font size="-1"> ext_read_ext </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> extent.c </font></td>
+<td align="RIGHT"><font size="-1"> 188 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>N</b> </font></td>
+<td align="RIGHT"><font size="-1"> brec_rmv </font></td>
+<td align="LEFT"><font size="-1"> __brec_find </font></td>
+<td align="LEFT"><font size="-1"> brec.c </font></td>
+<td align="RIGHT"><font size="-1"> 193 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>O</b> </font></td>
+<td align="RIGHT"><font size="-1"> readdir </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> dir.c </font></td>
+<td align="RIGHT"><font size="-1"> 68 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>P</b> </font></td>
+<td align="RIGHT"><font size="-1"> cat_move </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> catalog.c </font></td>
+<td align="RIGHT"><font size="-1"> 280 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>Q</b> </font></td>
+<td align="RIGHT"><font size="-1"> brec_insert </font></td>
+<td align="LEFT"><font size="-1"> __brec_find </font></td>
+<td align="LEFT"><font size="-1"> brec.c </font></td>
+<td align="RIGHT"><font size="-1"> 145 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>R</b> </font></td>
+<td align="RIGHT"><font size="-1"> free_fork </font></td>
+<td align="LEFT"><font size="-1"> free_exts </font></td>
+<td align="LEFT"><font size="-1"> extent.c </font></td>
+<td align="RIGHT"><font size="-1"> 307 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+</font><font size="-1"><b>S</b> </font></td>
+<td align="RIGHT"><font size="-1"> free_fork </font></td>
+<td align="LEFT"><font size="-1"> find_init </font></td>
+<td align="LEFT"><font size="-1"> extent.c </font></td>
+<td align="RIGHT"><font size="-1"> 301 </font></td>
+</tr>
+</tbody></table>
+ </td></tr>
+</tbody></table>
+
+
+</div></td>
+<td>
+<img src="./Error Handling is Ocassionally Correct_files/fig-result-big-legend.gif">
+</td></tr>
+<tr>
+<td colspan="2">
+<img src="./Error Handling is Ocassionally Correct_files/fig-result-big.gif">
+</td></tr>
+</tbody></table>
+
+<br>
+<font size="-1"><i>
+Figure 2: <b>A Sample of EDP Output.</b> The lower figure
+depicts the EDP output for the HFS file system. Some function names
+have been shortened to improve readability. As summarized in the
+upper right legend, a gray node with a thicker border represents a
+function that generates an error code. The other gray node represents
+the same thing, but the function also propagates the error code
+received from its callee. A white node represents a good function,
+i.e. it either propagates the error code to its caller or if it does
+not propagate the error code it minimally checks the error code. A
+black node represents an error-broken termination endpoint, i.e. it is
+a function that commits the violation of unsaved error codes. The
+darker and thicker edge coming out from a black node implies a broken
+error channel (a bad call); an error code actually flows from its
+callee, but the caller drops the error code. For ease of debugging,
+each bad call is labeled with a violation number whose detailed
+information can be found in the upper left violation table. For
+example, violation #E found in the bottom left corner of the graph is
+a bad call made by <tt>brec_updt_prnt</tt> when calling <tt>__brec_find</tt>,
+which can be located in <tt>fs/hfs/brec.c</tt> line
+345.
+</i></font>
+<br>
+
+
+
+<p>
+Using the information that EDP provides, we found three major
+error-handling inconsistencies in HFS. First, 11 out of 14 calls to
+<tt><font size="-1">find_init</font></tt> drop the returned error codes. As described earlier in
+this section, this bug could cause the system to crash or corrupt
+data. Second, 4 out of 5 total calls to the function
+<tt><font size="-1">__brec_find</font></tt> are bad calls (as indicated by the four black
+edges, E, D, N, and Q, found in the lower left of the graph). The
+task of this function is to find a record in an HFS node that best
+matches the given key, and return <tt><font size="-1">ENOENT</font></tt> (no entry) error code if
+it fails. The only call that saves this error code is made by the
+wrapper, <tt><font size="-1">brec_find</font></tt>. Interestingly, all 18 calls to this wrapper
+propagate the error code properly (as indicated by all gray edges
+coming into the function).
+
+</p><p>
+Finally, 3 out of 4 calls to <tt><font size="-1">free_exts</font></tt> do not save the returned
+error code (labeled R, I, and J). This function traverses a list of
+extents and locates the extents to be freed. If the extents cannot be
+found, the function returns <tt><font size="-1">EIO</font></tt>. More interestingly, the
+developer wrote a comment "panic?" just before the return statement
+(maybe in the hope that in this failure case the callers will call
+panic, which will never happen if the error code is dropped). By and
+large, we found similar inconsistencies in all the subsystems we
+analyzed. The fact that the fraction of bad calls over all calls to a
+function is generally high is intriguing, and will be discussed
+further in Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-analysis-inconsistent">4.3</a>.
+
+
+
+
+<!-- ------------------------------------- all -->
+
+</p><h3><a name="SECTION00041200000000000000"><br>
+3.1.2 EDP on All File Systems and Storage Drivers</a>
+</h3>
+
+<p>
+Figure&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-result-small-1">3</a> and&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-result-small-2">4</a> show EDP
+outputs for six more file systems whose error-propagation graphs
+represent an interesting sample. EDP outputs for the rest of the file
+systems can be downloaded from our web site&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#EdpOutput">11</a>].
+A small file system such as HFS+ has simple propagation chains, yet
+bad calls are still made. More complex error propagation can be seen
+in ext3, ReiserFS, and IBM JFS; within these file systems, error-codes
+propagate throughout 180 to 340 function calls. The error propagation
+in NFS is more structured compared to other file systems. Finally,
+among all file systems we analyze, XFS has the most complex error
+propagation chain; almost 1500 function calls propagate error-codes.
+Note that each graph in Figures&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-result-small-1">3</a>
+and&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-result-small-2">4</a> was produced by analyzing each file
+system in isolation (<i>i.e.</i>, the graph only shows intra-module but not
+inter-module calls), yet they already illustrate the complexity of
+error code propagation in each file system. Manual code inspection
+would require a tremendous amount of work to find error-propagation
+bugs.
+
+
+
+
+
+</p><p>
+
+</p><p>
+
+</p><div align="CENTER">
+
+<p><a name="fig-result-small-1"></a></p><div align="CENTER">
+<font size="+1"><b>HFS+</b></font>&nbsp;&nbsp;&nbsp;[ 22 bad / 84 calls, 26%] </div>
+ <br>
+<br>
+ <div align="CENTER">
+<img src="./Error Handling is Ocassionally Correct_files/fig-result-small-hfsplus.gif">
+</div>
+ <br>
+
+ <br>
+<br>
+ <div align="CENTER">
+<font size="+1"><b>ext3</b></font>&nbsp;&nbsp;&nbsp;[ 37 bad / 188 calls, 20%] </div>
+ <br>
+<br>
+ <div align="CENTER">
+<img src="./Error Handling is Ocassionally Correct_files/fig-result-small-ext3.gif">
+</div>
+ <br>
+</div>
+
+ <br>
+<br>
+ <div align="CENTER">
+<font size="+1"><b>ReiserFS</b></font>&nbsp;&nbsp;&nbsp;[ 35 bad / 218 calls, 16% ] </div>
+ <br>
+<br>
+ <div align="CENTER">
+<img src="./Error Handling is Ocassionally Correct_files/fig-result-small-reiserfs.gif"></div>
+ <br>
+<br> <br>
+<font size="-1"><i>
+Figure 3: <b>More Samples of EDP Output.</b>
+The figures illustrate the prevalent problem of incomplete
+error-propagation across different types of file systems. Details such
+as function names and violation numbers have been removed. Gray edges
+represent calls that propagate error codes. Black edges represent bad
+calls. The number of edges are reported in [ X / Y , Z% ] format where X and
+Y represent the number of black and all (gray and black) edges
+respectively, and Z represents the fraction of X and Y. For more
+information, please see the legend in Figure 2. </i></font>
+<br>
+
+
+
+<p>
+
+</p><p>
+
+</p><div align="CENTER">
+
+<p><a name="fig-result-small-2"></a></p><div align="CENTER">
+<font size="+1"><b>IBM JFS</b></font>&nbsp;&nbsp;&nbsp;[ 61 bad / 340 calls, 18% ]</div>
+
+ <br>
+<br>
+ <div align="CENTER">
+<img src="./Error Handling is Ocassionally Correct_files/fig-result-small-jfs.gif"></div>
+ <br>
+
+ <br>
+<br>
+ <div align="CENTER">
+<font size="+1"><b>NFS Client</b></font>&nbsp;&nbsp;&nbsp;[ 54 bad / 446 calls, 12% ]</div>
+
+ <br>
+<br>
+ <div align="CENTER">
+<img src="./Error Handling is Ocassionally Correct_files/fig-result-small-nfs.gif"></div>
+ <br>
+
+ <br>
+<br>
+ <div align="CENTER">
+<font size="+1"><b>XFS</b></font>&nbsp;&nbsp;&nbsp;[ 105 bad / 1453 calls, 7% ]</div>
+
+ <br>
+<br>
+ <div align="CENTER">
+<img src="./Error Handling is Ocassionally Correct_files/fig-result-small-xfs.gif"></div>
+ <br>
+<br>
+<br>
+<font size="-1"><i>
+Figure 4: <b>More Samples of EDP Output (Cont'd).</b>
+Please see caption in Figure 3.</i></font>
+<br>
+
+</div>
+
+
+
+<!-- table all -->
+<p>
+Next, we analyzed the propagation of error codes across all file
+systems and storage device drivers as a whole. All inter-module calls
+were connected by our EDP channel constructor, which connects all
+function pointer calls; hence, we were able to catch inter-module bad
+calls in addition to intra-module ones. Table&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#table-result-all">2</a>
+summarizes our findings. Note that the number of violations reported
+is higher than the ones reported in
+Figures&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-result-big">2</a>,&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-result-small-1">3</a>,
+and&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-result-small-2">4</a> because we catch more bugs when we
+analyze each file system in conjunction with other subsystems (<i>e.g.</i>,
+ext3 with the journaling layer, VFS, and the memory management).
+
+</p><p>
+Surprisingly, out of 9022 error channels, 1153 (or nearly 13%)
+constitute bad calls. This appears to be a long-standing problem. We
+ran a partial analysis in Linux 2.4 (not shown) and found that the
+magnitude of incomplete error code propagation is essentially the
+same. In Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-analysis">4</a>, we try to dissect the root
+causes of this problem.
+
+
+
+<a name="table-result-all"></a>
+<table border="0" align="center" cellspacing="50" cellpadding="0">
+<tbody><tr><td>
+<p>
+<table cellpadding="3" cellspacing="0" border="1" align="center">
+<tbody><tr><td colspan="6" align="center" bgcolor="#FFFFCC">
+ <font size="+1"><b>File Systems</b></font></td>
+</tr>
+<tr bgcolor="#FFFFCC">
+ <td aligh="center"><font size="-1" color="white">.</font></td>
+ <td aligh="center"><b><font size="-1">Bad Calls</font></b></td>
+ <td aligh="center"><b><font size="-1">EC Calls</font></b></td>
+ <td aligh="center"><b><font size="-1">Size (kloc)</font></b></td>
+ <td aligh="center"><b><font size="-1">Frac (%)</font></b></td>
+ <td aligh="center"><b><font size="-1">Viol/kloc</font></b></td>
+</tr>
+<tr><td><font size="-1">XFS </font></td> <td align="right"><font size="-1"><b> 101 </b></font></td><td align="right"><font size="-1"> 1457 </font></td><td align="right"><font size="-1"> 71 </font></td><td align="right"><font size="-1"> 6.9 </font></td><td align="right"><font size="-1"> 1.4 </font></td></tr> <!-- fs/xfs/ -->
+<tr><td><font size="-1">Virtual FS </font></td> <td align="right"><font size="-1"><b> 96 </b></font></td><td align="right"><font size="-1"> 1149 </font></td><td align="right"><font size="-1"> 34 </font></td><td align="right"><font size="-1"> 8.4 </font></td><td align="right"><font size="-1"> 2.9 </font></td></tr> <!-- fs/vfs/ -->
+<tr><td><font size="-1">IBM JFS </font></td> <td align="right"><font size="-1"><b> 95 </b></font></td><td align="right"><font size="-1"> 390 </font></td><td align="right"><font size="-1"> 17 </font></td><td align="right"><font size="-1"> 24.4 </font></td><td align="right"><font size="-1"> 5.6 </font></td></tr> <!-- fs/jfs/ -->
+<tr><td><font size="-1">ext3 </font></td> <td align="right"><font size="-1"><b> 80 </b></font></td><td align="right"><font size="-1"> 362 </font></td><td align="right"><font size="-1"> 12 </font></td><td align="right"><font size="-1"> 22.1 </font></td><td align="right"><font size="-1"> 7.2 </font></td></tr> <!-- fs/ext3/ -->
+<tr><td><font size="-1">NFS Client </font></td> <td align="right"><font size="-1"><b> 62 </b></font></td><td align="right"><font size="-1"> 482 </font></td><td align="right"><font size="-1"> 18 </font></td><td align="right"><font size="-1"> 12.9 </font></td><td align="right"><font size="-1"> 3.6 </font></td></tr> <!-- fs/nfs/ -->
+<tr><td><font size="-1">CIFS </font></td> <td align="right"><font size="-1"><b> 43 </b></font></td><td align="right"><font size="-1"> 339 </font></td><td align="right"><font size="-1"> 21 </font></td><td align="right"><font size="-1"> 12.7 </font></td><td align="right"><font size="-1"> 2.1 </font></td></tr> <!-- fs/cifs/ -->
+<tr><td><font size="-1">ReiserFS </font></td> <td align="right"><font size="-1"><b> 42 </b></font></td><td align="right"><font size="-1"> 399 </font></td><td align="right"><font size="-1"> 24 </font></td><td align="right"><font size="-1"> 10.5 </font></td><td align="right"><font size="-1"> 1.8 </font></td></tr> <!-- fs/reiserfs/ -->
+<tr><td><font size="-1">Mem. Mgmt. </font></td> <td align="right"><font size="-1"><b> 40 </b></font></td><td align="right"><font size="-1"> 351 </font></td><td align="right"><font size="-1"> 20 </font></td><td align="right"><font size="-1"> 11.4 </font></td><td align="right"><font size="-1"> 2.0 </font></td></tr> <!-- mm/ -->
+<tr><td><font size="-1">Apple HFS+ </font></td> <td align="right"><font size="-1"><b> 25 </b></font></td><td align="right"><font size="-1"> 98 </font></td><td align="right"><font size="-1"> 7 </font></td><td align="right"><font size="-1"> 25.5 </font></td><td align="right"><font size="-1"> 3.7 </font></td></tr> <!-- fs/hfsplus/ -->
+<tr><td><font size="-1">JFFS v2 </font></td> <td align="right"><font size="-1"><b> 24 </b></font></td><td align="right"><font size="-1"> 153 </font></td><td align="right"><font size="-1"> 11 </font></td><td align="right"><font size="-1"> 15.7 </font></td><td align="right"><font size="-1"> 2.2 </font></td></tr> <!-- fs/jffs2/ --> <!-- break drivers -->
+<tr><td><font size="-1">Apple HFS </font></td> <td align="right"><font size="-1"><b> 20 </b></font></td><td align="right"><font size="-1"> 76 </font></td><td align="right"><font size="-1"> 5 </font></td><td align="right"><font size="-1"> 26.3 </font></td><td align="right"><font size="-1"> 4.8 </font></td></tr> <!-- fs/hfs/ -->
+<tr><td><font size="-1">SMB </font></td> <td align="right"><font size="-1"><b> 19 </b></font></td><td align="right"><font size="-1"> 196 </font></td><td align="right"><font size="-1"> 6 </font></td><td align="right"><font size="-1"> 9.7 </font></td><td align="right"><font size="-1"> 3.5 </font></td></tr> <!-- fs/smbfs/ -->
+<tr><td><font size="-1">ext2 </font></td> <td align="right"><font size="-1"><b> 18 </b></font></td><td align="right"><font size="-1"> 103 </font></td><td align="right"><font size="-1"> 6 </font></td><td align="right"><font size="-1"> 17.5 </font></td><td align="right"><font size="-1"> 3.3 </font></td></tr> <!-- fs/ext2/ -->
+<tr><td><font size="-1">AFS </font></td> <td align="right"><font size="-1"><b> 16 </b></font></td><td align="right"><font size="-1"> 62 </font></td><td align="right"><font size="-1"> 7 </font></td><td align="right"><font size="-1"> 25.8 </font></td><td align="right"><font size="-1"> 2.6 </font></td></tr> <!-- fs/afs/ -->
+<tr><td><font size="-1">NTFS </font></td> <td align="right"><font size="-1"><b> 15 </b></font></td><td align="right"><font size="-1"> 186 </font></td><td align="right"><font size="-1"> 18 </font></td><td align="right"><font size="-1"> 8.1 </font></td><td align="right"><font size="-1"> 0.9 </font></td></tr> <!-- fs/ntfs/ -->
+<tr><td><font size="-1">NFS Server </font></td> <td align="right"><font size="-1"><b> 15 </b></font></td><td align="right"><font size="-1"> 265 </font></td><td align="right"><font size="-1"> 14 </font></td><td align="right"><font size="-1"> 5.7 </font></td><td align="right"><font size="-1"> 1.2 </font></td></tr> <!-- fs/nfsd/ -->
+<tr><td><font size="-1">NCP </font></td> <td align="right"><font size="-1"><b> 13 </b></font></td><td align="right"><font size="-1"> 169 </font></td><td align="right"><font size="-1"> 5 </font></td><td align="right"><font size="-1"> 7.7 </font></td><td align="right"><font size="-1"> 2.6 </font></td></tr> <!-- fs/ncpfs/ -->
+<tr><td><font size="-1">UFS </font></td> <td align="right"><font size="-1"><b> 12 </b></font></td><td align="right"><font size="-1"> 44 </font></td><td align="right"><font size="-1"> 5 </font></td><td align="right"><font size="-1"> 27.3 </font></td><td align="right"><font size="-1"> 2.6 </font></td></tr> <!-- fs/ufs/ -->
+<tr><td><font size="-1">JBD </font></td> <td align="right"><font size="-1"><b> 10 </b></font></td><td align="right"><font size="-1"> 43 </font></td><td align="right"><font size="-1"> 4 </font></td><td align="right"><font size="-1"> 23.3 </font></td><td align="right"><font size="-1"> 2.6 </font></td></tr> <!-- fs/jbd/ -->
+<tr><td><font size="-1">FAT </font></td> <td align="right"><font size="-1"><b> 9 </b></font></td><td align="right"><font size="-1"> 81 </font></td><td align="right"><font size="-1"> 4 </font></td><td align="right"><font size="-1"> 11.1 </font></td><td align="right"><font size="-1"> 2.9 </font></td></tr> <!-- fs/fat/ -->
+<tr><td><font size="-1">Plan 9 </font></td> <td align="right"><font size="-1"><b> 9 </b></font></td><td align="right"><font size="-1"> 80 </font></td><td align="right"><font size="-1"> 4 </font></td><td align="right"><font size="-1"> 11.2 </font></td><td align="right"><font size="-1"> 2.4 </font></td></tr> <!-- fs/9p/ -->
+<tr><td><font size="-1">System V </font></td> <td align="right"><font size="-1"><b> 7 </b></font></td><td align="right"><font size="-1"> 30 </font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 23.3 </font></td><td align="right"><font size="-1"> 3.2 </font></td></tr> <!-- fs/sysv/ -->
+<tr><td><font size="-1">JFFS </font></td> <td align="right"><font size="-1"><b> 7 </b></font></td><td align="right"><font size="-1"> 56 </font></td><td align="right"><font size="-1"> 5 </font></td><td align="right"><font size="-1"> 12.5 </font></td><td align="right"><font size="-1"> 1.4 </font></td></tr> <!-- fs/jffs/ -->
+<tr><td><font size="-1">UDF </font></td> <td align="right"><font size="-1"><b> 6 </b></font></td><td align="right"><font size="-1"> 50 </font></td><td align="right"><font size="-1"> 9 </font></td><td align="right"><font size="-1"> 12.0 </font></td><td align="right"><font size="-1"> 0.7 </font></td></tr> <!-- fs/udf/ -->
+<tr><td><font size="-1">MSDOS </font></td> <td align="right"><font size="-1"><b> 5 </b></font></td><td align="right"><font size="-1"> 39 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 12.8 </font></td><td align="right"><font size="-1"> 9.3 </font></td></tr> <!-- fs/msdos/ -->
+<tr><td><font size="-1">VFAT </font></td> <td align="right"><font size="-1"><b> 4 </b></font></td><td align="right"><font size="-1"> 39 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 10.3 </font></td><td align="right"><font size="-1"> 5.0 </font></td></tr> <!-- fs/vfat/ -->
+<tr><td><font size="-1">Minix </font></td> <td align="right"><font size="-1"><b> 4 </b></font></td><td align="right"><font size="-1"> 31 </font></td><td align="right"><font size="-1"> 4 </font></td><td align="right"><font size="-1"> 12.9 </font></td><td align="right"><font size="-1"> 1.2 </font></td></tr> <!-- fs/minix/ -->
+<tr><td><font size="-1">FUSE </font></td> <td align="right"><font size="-1"><b> 4 </b></font></td><td align="right"><font size="-1"> 48 </font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 8.3 </font></td><td align="right"><font size="-1"> 1.5 </font></td></tr> <!-- fs/fuse/ --> <!-- break fs -->
+<tr><td><font size="-1">Automounter4 </font></td> <td align="right"><font size="-1"><b> 4 </b></font></td><td align="right"><font size="-1"> 53 </font></td><td align="right"><font size="-1"> 2 </font></td><td align="right"><font size="-1"> 7.5 </font></td><td align="right"><font size="-1"> 2.7 </font></td></tr> <!-- fs/autofs4/ -->
+<tr><td><font size="-1">NFS Lockd </font></td> <td align="right"><font size="-1"><b> 3 </b></font></td><td align="right"><font size="-1"> 21 </font></td><td align="right"><font size="-1"> 4 </font></td><td align="right"><font size="-1"> 14.3 </font></td><td align="right"><font size="-1"> 0.8 </font></td></tr> <!-- fs/lockd/ -->
+<tr><td><font size="-1">Relayfs </font></td> <td align="right"><font size="-1"><b> 2 </b></font></td><td align="right"><font size="-1"> 5 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 40.0 </font></td><td align="right"><font size="-1"> 2.7 </font></td></tr> <!-- fs/relayfs/ -->
+<tr><td><font size="-1">Partitions </font></td> <td align="right"><font size="-1"><b> 2 </b></font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 4 </font></td><td align="right"><font size="-1"> 66.7 </font></td><td align="right"><font size="-1"> 0.6 </font></td></tr> <!-- fs/partitions/ -->
+<tr><td><font size="-1">ISO </font></td> <td align="right"><font size="-1"><b> 2 </b></font></td><td align="right"><font size="-1"> 19 </font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 10.5 </font></td><td align="right"><font size="-1"> 0.7 </font></td></tr> <!-- fs/isofs/ -->
+<tr><td><font size="-1">HugeTLB Sup </font></td> <td align="right"><font size="-1"><b> 2 </b></font></td><td align="right"><font size="-1"> 10 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 20.0 </font></td><td align="right"><font size="-1"> 3.0 </font></td></tr> <!-- fs/hugetlbfs/ -->
+<tr><td><font size="-1">Compr. ROM </font></td> <td align="right"><font size="-1"><b> 2 </b></font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 66.7 </font></td><td align="right"><font size="-1"> 4.5 </font></td></tr> <!-- fs/cramfs/ -->
+<tr><td><font size="-1">ADFS </font></td> <td align="right"><font size="-1"><b> 2 </b></font></td><td align="right"><font size="-1"> 30 </font></td><td align="right"><font size="-1"> 2 </font></td><td align="right"><font size="-1"> 6.7 </font></td><td align="right"><font size="-1"> 1.3 </font></td></tr> <!-- fs/adfs/ -->
+<tr><td><font size="-1">sysfs sup. </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 29 </font></td><td align="right"><font size="-1"> 2 </font></td><td align="right"><font size="-1"> 3.4 </font></td><td align="right"><font size="-1"> 0.8 </font></td></tr> <!-- fs/sysfs/ -->
+<tr><td><font size="-1">romfs sup. </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 33.3 </font></td><td align="right"><font size="-1"> 2.4 </font></td></tr> <!-- fs/romfs/ -->
+<tr><td><font size="-1">ramfs sup. </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 6 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 16.7 </font></td><td align="right"><font size="-1"> 6.0 </font></td></tr> <!-- fs/ramfs/ -->
+<tr><td><font size="-1">QNX 4 </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 8 </font></td><td align="right"><font size="-1"> 2 </font></td><td align="right"><font size="-1"> 12.5 </font></td><td align="right"><font size="-1"> 0.9 </font></td></tr> <!-- fs/qnx4/ -->
+<tr><td><font size="-1">proc fs sup. </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 44 </font></td><td align="right"><font size="-1"> 6 </font></td><td align="right"><font size="-1"> 2.3 </font></td><td align="right"><font size="-1"> 0.2 </font></td></tr> <!-- fs/proc/ -->
+<tr><td><font size="-1">OS/2 HPFS </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 18 </font></td><td align="right"><font size="-1"> 6 </font></td><td align="right"><font size="-1"> 5.6 </font></td><td align="right"><font size="-1"> 0.2 </font></td></tr> <!-- fs/hpfs/ -->
+<tr><td><font size="-1">FreeVxFS </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 4 </font></td><td align="right"><font size="-1"> 2 </font></td><td align="right"><font size="-1"> 25.0 </font></td><td align="right"><font size="-1"> 0.7 </font></td></tr> <!-- fs/freevxfs/ -->
+<tr><td><font size="-1">EFS </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 33.3 </font></td><td align="right"><font size="-1"> 1.4 </font></td></tr> <!-- fs/efs/ -->
+<tr><td><font size="-1">devpts </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 2 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 50.0 </font></td><td align="right"><font size="-1"> 6.2 </font></td></tr> <!-- fs/devpts/ -->
+<tr><td><font size="-1">Boot FS </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 9 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 11.1 </font></td><td align="right"><font size="-1"> 1.2 </font></td></tr> <!-- fs/bfs/ -->
+<tr><td><font size="-1">BeOS </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 5 </font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 20.0 </font></td><td align="right"><font size="-1"> 0.5 </font></td></tr> <!-- fs/befs/ -->
+<tr><td><font size="-1">Automounter </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 41 </font></td><td align="right"><font size="-1"> 2 </font></td><td align="right"><font size="-1"> 2.4 </font></td><td align="right"><font size="-1"> 1.0 </font></td></tr> <!-- fs/autofs/ -->
+<tr><td><font size="-1">Amiga FFS </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 34 </font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 2.9 </font></td><td align="right"><font size="-1"> 0.3 </font></td></tr> <!-- fs/affs/ -->
+<tr><td><font size="-1">exportfs sup. </font></td> <td align="right"><font size="-1"><b> 0 </b></font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 1 </font></td><td align="right"><font size="-1"> 0.0 </font></td><td align="right"><font size="-1"> 0.0 </font></td></tr> <!-- fs/exportfs/ -->
+<tr><td><font size="-1">Coda </font></td> <td align="right"><font size="-1"><b> 0 </b></font></td><td align="right"><font size="-1"> 149 </font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 0.0 </font></td><td align="right"><font size="-1"> 0.0 </font></td></tr> <!-- fs/coda/ -->
+<tr><td><font size="-1"><b> Total</b> </font></td> <td align="right"><font size="-1"><b> 0 </b></font></td><td align="right"><font size="-1"> 7278 </font></td><td align="right"><font size="-1"> 366 </font></td><td align="right"><font size="-1"> -- </font></td><td align="right"><font size="-1"> -- </font></td></tr> <!-- TOTAL = 51 -->
+<tr><td><font size="-1"><b> Average</b> </font></td> <td align="right"><font size="-1"><b> 16.3 </b></font></td><td align="right"><font size="-1"> 142.7 </font></td><td align="right"><font size="-1"> 7.2 </font></td><td align="right"><font size="-1"><b> 17.0 </b></font></td><td align="right"><font size="-1"><b> 2.4 </b></font></td></tr> <!-- 51 -->
+</tbody></table>
+</p></td><td valign="top">
+<p>
+<table cellpadding="3" cellspacing="0" border="1" align="center">
+<tbody><tr><td colspan="6" align="center" bgcolor="#FFFFCC">
+ <font size="+1"><b>Storage Drivers</b></font></td>
+</tr>
+<tr bgcolor="#FFFFCC">
+ <td aligh="center"><font size="-1" color="white">.</font></td>
+ <td aligh="center"><b><font size="-1">Bad Calls</font></b></td>
+ <td aligh="center"><b><font size="-1">EC Calls</font></b></td>
+ <td aligh="center"><b><font size="-1">Size (kloc)</font></b></td>
+ <td aligh="center"><b><font size="-1">Frac (%)</font></b></td>
+ <td aligh="center"><b><font size="-1">Viol/kloc</font></b></td>
+</tr>
+<tr><td><font size="-1">SCSI (root) </font></td> <td align="right"><font size="-1"><b> 123 </b></font></td><td align="right"><font size="-1"> 628 </font></td><td align="right"><font size="-1"> 198 </font></td><td align="right"><font size="-1"> 19.6 </font></td><td align="right"><font size="-1"> 0.6 </font></td></tr> <!-- drivers/scsi/root/ -->
+<tr><td><font size="-1">IDE (root) </font></td> <td align="right"><font size="-1"><b> 53 </b></font></td><td align="right"><font size="-1"> 223 </font></td><td align="right"><font size="-1"> 15 </font></td><td align="right"><font size="-1"> 23.8 </font></td><td align="right"><font size="-1"> 3.5 </font></td></tr> <!-- drivers/ide/root/ -->
+<tr><td><font size="-1">Block Dev (root) </font></td> <td align="right"><font size="-1"><b> 39 </b></font></td><td align="right"><font size="-1"> 195 </font></td><td align="right"><font size="-1"> 36 </font></td><td align="right"><font size="-1"> 20.0 </font></td><td align="right"><font size="-1"> 1.1 </font></td></tr> <!-- drivers/block/root2/ -->
+<tr><td><font size="-1">Software RAID </font></td> <td align="right"><font size="-1"><b> 31 </b></font></td><td align="right"><font size="-1"> 290 </font></td><td align="right"><font size="-1"> 32 </font></td><td align="right"><font size="-1"> 10.7 </font></td><td align="right"><font size="-1"> 1.0 </font></td></tr> <!-- drivers/md/ -->
+<tr><td><font size="-1">SCSI (aacraid) </font></td> <td align="right"><font size="-1"><b> 30 </b></font></td><td align="right"><font size="-1"> 76 </font></td><td align="right"><font size="-1"> 7 </font></td><td align="right"><font size="-1"> 39.5 </font></td><td align="right"><font size="-1"> 4.8 </font></td></tr> <!-- drivers/scsi/aacraid/ -->
+<tr><td><font size="-1">SCSI (lpfc) </font></td> <td align="right"><font size="-1"><b> 14 </b></font></td><td align="right"><font size="-1"> 30 </font></td><td align="right"><font size="-1"> 16 </font></td><td align="right"><font size="-1"> 46.7 </font></td><td align="right"><font size="-1"> 0.9 </font></td></tr> <!-- drivers/scsi/lpfc/ -->
+<tr><td><font size="-1">Blk Dev (P-IDE) </font></td> <td align="right"><font size="-1"><b> 11 </b></font></td><td align="right"><font size="-1"> 17 </font></td><td align="right"><font size="-1"> 8 </font></td><td align="right"><font size="-1"> 64.7 </font></td><td align="right"><font size="-1"> 1.5 </font></td></tr> <!-- drivers/block/paride/ -->
+<tr><td><font size="-1">SCSI aic7xxx </font></td> <td align="right"><font size="-1"><b> 8 </b></font></td><td align="right"><font size="-1"> 62 </font></td><td align="right"><font size="-1"> 37 </font></td><td align="right"><font size="-1"> 12.9 </font></td><td align="right"><font size="-1"> 0.2 </font></td></tr> <!-- drivers/scsi/aic7xxx/ -->
+<tr><td><font size="-1">IDE (pci) </font></td> <td align="right"><font size="-1"><b> 5 </b></font></td><td align="right"><font size="-1"> 106 </font></td><td align="right"><font size="-1"> 12 </font></td><td align="right"><font size="-1"> 4.7 </font></td><td align="right"><font size="-1"> 0.4 </font></td></tr> <!-- drivers/ide/pci/ -->
+<tr><td><font size="-1">IDE legacy </font></td> <td align="right"><font size="-1"><b> 2 </b></font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 3 </font></td><td align="right"><font size="-1"> 66.7 </font></td><td align="right"><font size="-1"> 0.8 </font></td></tr> <!-- drivers/ide/legacy/ --> <!-- break drivers -->
+<tr><td><font size="-1">Blk Layer Core </font></td> <td align="right"><font size="-1"><b> 2 </b></font></td><td align="right"><font size="-1"> 65 </font></td><td align="right"><font size="-1"> 8 </font></td><td align="right"><font size="-1"> 3.1 </font></td><td align="right"><font size="-1"> 0.3 </font></td></tr> <!-- block/root1/ -->
+<tr><td><font size="-1">SCSI megaraid </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 30 </font></td><td align="right"><font size="-1"> 6 </font></td><td align="right"><font size="-1"> 3.3 </font></td><td align="right"><font size="-1"> 0.2 </font></td></tr> <!-- drivers/scsi/megaraid/ -->
+<tr><td><font size="-1">Blk Dev (Eth) </font></td> <td align="right"><font size="-1"><b> 1 </b></font></td><td align="right"><font size="-1"> 5 </font></td><td align="right"><font size="-1"> 2 </font></td><td align="right"><font size="-1"> 20.0 </font></td><td align="right"><font size="-1"> 0.7 </font></td></tr> <!-- drivers/block/aoe/ -->
+<tr><td><font size="-1">SCSI (sym53c8) </font></td> <td align="right"><font size="-1"><b> 0 </b></font></td><td align="right"><font size="-1"> 6 </font></td><td align="right"><font size="-1"> 10 </font></td><td align="right"><font size="-1"> 0.0 </font></td><td align="right"><font size="-1"> 0.0 </font></td></tr> <!-- drivers/scsi/sym53c8xx_2/ -->
+<tr><td><font size="-1">SCSI (qla2xxx) </font></td> <td align="right"><font size="-1"><b> 0 </b></font></td><td align="right"><font size="-1"> 8 </font></td><td align="right"><font size="-1"> 49 </font></td><td align="right"><font size="-1"> 0.0 </font></td><td align="right"><font size="-1"> 0.0 </font></td></tr> <!-- drivers/scsi/qla2xxx/ -->
+<tr><td><font size="-1"><b> Total</b> </font></td> <td align="right"><font size="-1"><b> 0 </b></font></td><td align="right"><font size="-1"> 1744 </font></td><td align="right"><font size="-1"> 430 </font></td><td align="right"><font size="-1"> -- </font></td><td align="right"><font size="-1"> -- </font></td></tr> <!-- TOTAL = 15 -->
+<tr><td><font size="-1"><b> Average</b> </font></td> <td align="right"><font size="-1"><b> 21.3 </b></font></td><td align="right"><font size="-1"> 116.3 </font></td><td align="right"><font size="-1"> 28.6 </font></td><td align="right"><font size="-1"><b> 22.4 </b></font></td><td align="right"><font size="-1"><b> 1.1 </b></font></td></tr> <!-- 15 -->
+</tbody></table>
+</p></td></tr>
+</tbody></table>
+<br>
+<font size="-1"><i>
+Table 2: <b>Error-broken channels due to unsaved
+error codes.</b> These tables report the number of bad calls found across
+all file systems and storage device drivers in Linux 2.6.15.4. In
+each table, from left to right column we report the name of
+the subsystem, the number of bad calls, the number of error channels
+(i.e., the number of calls to functions that propagate error codes),
+the size of the subsystem,
+the fraction of bad calls over all error-related calls (ratio of
+2nd and 3rd column), and finally the number of violations
+per Kloc (ratio of 2nd and 4th column).
+We categorize a directory as a subsystem. Thus, for storage
+drivers, since different SCSI device drivers exist in the first-level
+of the <tt>scsi/</tt> directory, we put all of them as one subsystem. SCSI
+device drivers that are located in different directories (e.g.,
+<tt>scsi/lpfc/</tt>, <tt>scsi/aacraid/</tt>) are categorized as different
+subsystems. The same principle is applied to IDE. }
+</i></font>
+
+</p><p>
+
+
+
+</p><p>
+
+</p><h3><a name="SECTION00041300000000000000"><br>
+3.1.3 False Positives</a>
+</h3>
+
+<p>
+It is important to note that while the number of bad calls is high,
+not all bad calls could cause damage to the system. The primary
+reason is what we call a <em>double error code</em>; some functions
+expose two or more error codes at the same time, and checking one of
+the error codes while ignoring the others can still be correct. For
+example, in the ReiserFS code below, the error code returned from
+<tt><font size="-1">sync_dirty_buffer</font></tt> does not have to be saved (line 8) <em>if
+and only if</em> the function performs the check on the second error code
+(line 9); the buffer must be checked whether it is is up-to-date.
+
+</p><p>
+</p><pre> 1 // fs/buffer.c
+ 2 int sync_dirty_buffer (buffer_head* bh) {
+ 3 ...
+ 4 return ret; // RETURN ERROR CODE
+ 5 }
+ 6 // reiserfs/journal.c
+ 7 int flush_commit_list() {
+ 8 sync_dirty_buffer(bh); // UNSAVED EC
+ 9 if (!buffer_uptodate(bh)) {
+ 10 return -EIO;
+ 11 }
+ 12 }
+</pre>
+
+<p>
+To ensure that the number of false positives we report is not overly
+large, we manually analyze all of the code snippets to check whether a
+second error code is being checked. Note that this manual process can
+be automated if we incorporate all types of error codes into EDP. We
+have found only a total of 39 false positives, which have been
+excluded from the numbers we report in this paper. Thus, the high
+numbers in Table&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#table-result-all">2</a> provide a hint to a real and
+critical problem.
+
+</p><p>
+
+</p><h2><a name="SECTION00042000000000000000"></a>
+<a name="sec-result-silent"></a><br>
+3.2 Silent Failures: Manifestations of Unsaved Error Codes
+</h2>
+
+<p>
+
+</p><p>
+
+</p><p>
+To show that unsaved error codes represent a serious problem that can
+lead to silent failures, we injected disk block failures in a few
+cases. As shown in Figure&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-result-zoom">5</a>, one serious silent
+failure arises during file system recovery: the journaling block
+device layer (JBD) does not properly propagate any block write
+failures, including inode, directory, bitmap, superblock, and other
+block write failures. EDP unearths these silent failures by
+pinpointing the <tt><font size="-1">journal_recover</font></tt> function, which is responsible
+for file system recovery, as it calls <tt><font size="-1">sync_blockdev</font></tt> to flush the
+dirty buffer pages owned by the block device. Unfortunately,
+<tt><font size="-1">journal_recover</font></tt> does not save the error code propagated by
+<tt><font size="-1">sync_blockdev</font></tt> in the case of block write failures. This is an
+example where the error code is dropped in the middle of its
+propagation chain; <tt><font size="-1">sync_blockdev</font></tt> correctly propagates the <tt><font size="-1">EIO</font></tt> error codes received from the two function calls it makes.
+
+
+
+</p><div align="CENTER">
+
+<p><a name="fig-result-zoom">
+
+<table border="0" cellpadding="0" cellspacing="0">
+<tbody><tr><td>
+<img src="./Error Handling is Ocassionally Correct_files/fig-result-zoom.gif">
+</td><td>
+
+<pre>journal_recover()
+ /* BROKEN CHANNEL */
+ sync_blockdev();
+
+sync_blockdev()
+ ret = fm_fdatawrite();
+ err = fm_fdatawait();
+ if(!ret) ret = err;
+ /* PROPAGATE EIO */
+ return ret;
+</pre>
+</td></tr></tbody></table>
+
+<br>
+<font size="-1"><i>
+Figure 5: <b>Silent error in journal recovery.</b>
+In the figure on the left, EDP marks <tt>journal_recover</tt> as a termination
+endpoint of a broken channel. The code snippet on the right shows that
+<tt>journal_recover</tt> ignores the <tt>EIO</tt> propagated by <tt>sync_blockdev</tt>.
+</i></font>
+
+<br>
+
+</a></p></div><a name="fig-result-zoom">
+
+<p>
+A similar problem occurs in the NFS server code. From a similar
+failure injection experiment, we found that the NFS client is not
+informed when a write failure occurs during a <tt><font size="-1">sync</font></tt> operation. In
+the experiment, the client updates old data and then sends a <tt><font size="-1">sync</font></tt>
+operation with the data to the NFS server. The NFS server then invokes
+the <tt><font size="-1">nfsd_dosync</font></tt> operation, which mainly performs three
+operations similar to the <tt><font size="-1">sync_blockdev</font></tt> call above. First, the
+NFS server writes dirty pages to the disk; second, it writes dirty
+inodes and the superblock to disk; third, it waits until the ongoing
+I/O data transfer terminates. All these three operations could return
+error codes, but the implementation of <tt><font size="-1">nfsd_dosync</font></tt> does not save
+any return values. As a result, the NFS client will never notice any
+disk write failures occurring in the server. Thus, even a careful,
+error-robust client cannot trust the server to inform it of errors
+that occur.
+
+</p></a><p><a name="fig-result-zoom">
+In the NFS server code, we might expect that at least one return value
+would be saved and checked properly. However, no return values are
+saved, leading one to question whether the returned error codes from
+the <tt><font size="-1">write</font></tt> or <tt><font size="-1">sync</font></tt> operations are correctly handled in
+general. It could be the case that the developers are not concerned
+about write failures. We investigate this hypothesis in
+Section&nbsp;</a><a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-analysis-neglected">4.2</a>.
+
+</p><p>
+
+</p><h2><a name="SECTION00043000000000000000"></a>
+<a name="sec-result-unchecked"></a><br>
+3.3 Unchecked Error Code
+</h2>
+
+<p>
+Lastly, we report the number of error-broken channels due to a variable
+that contains an error code not being checked or used in the future.
+For example, in the IBM JFS code below, <tt><font size="-1">rc</font></tt> carries an error code
+propagated from <tt><font size="-1">txCommit</font></tt> (line 4), but <tt><font size="-1">rc</font></tt> is never checked.
+
+</p><p>
+</p><pre> 1 // jfs/jfs_txnmgr.c
+ 2 int jfs_sync () {
+ 3 int rc;
+ 4 rc = txCommit(); // UNCHECKED 'rc'
+ 5 // No usage or check of 'rc'
+ 6 // after this line
+ 7 }
+</pre>
+
+<p>
+This analysis can also report false positives due to the double error
+code problem described previously. In addition, we also find the
+problem of <em>overloaded variables</em> that contribute as false
+positives. We define a variable to be overloaded if the variable could
+contain an error code or a data value. For instance,
+<tt><font size="-1">blknum</font></tt> in the QNX4 code below is an example of an overloaded
+variable:
+
+</p><p>
+</p><pre> 1 // qnx4/dir.c
+ 2 int qnx4_readdir () {
+ 3 int blknum;
+ 4 struct buffer_head *bh;
+ 5 blknum = qnx4_block_map();
+ 6 bh = sb_bread (blknum);
+ 7 if (bh == NULL)
+ 8 // error
+ 9 }
+</pre>
+
+<p>
+In this code, <tt><font size="-1">qnx4_block_map</font></tt> could return an error code (line
+5), which is usually a negative value. <tt><font size="-1">sb_bread</font></tt> takes a block
+number and returns a buffer head that contains the data for that
+particular block (line 6). Since a negative block number will lead to
+a <tt><font size="-1">NULL</font></tt> buffer head (line 7), the error code stored in <tt><font size="-1">blknum</font></tt>
+does not have to be explicitly checked. The developer believes that
+the other part of the code will catch this error or eventually raise
+related errors. This practice reduces the accuracy of our static
+analysis.
+
+</p><p>
+Since the number of unchecked error code reports is small, we were
+able to remove the false positives and find a total of 3 and 2
+unchecked error codes in file systems and storage drivers,
+respectively, that could lead to silent failures.
+
+</p><p>
+
+</p><h1><a name="SECTION00050000000000000000"></a>
+<a name="sec-analysis"></a><br>
+4 Analysis of Results
+</h1>
+
+<p>
+In the following sections, we present five analyses whereby we try to
+uncover the root causes and impact of incomplete error propagation.
+Since the number of unchecked and overwritten error codes is small, we
+only consider unsaved error codes (bad calls) in our analyses; thus we
+use "bad calls" and "broken channels" interchangeably from now on.
+First, we made a correlation between robustness and complexity.
+Second, we analyzed whether file systems and storage device drivers
+give different treatment to errors occurring in I/O read vs.&nbsp;I/O write
+operations. From that analysis we find that many write errors are
+neglected; hence we perform the next study in which we try to answer
+whether ignored errors are corner-case mistakes or intentional
+choices. In the final two analyses, we analyze whether chained error
+propagation and inter-module calls play major parts in causing
+incorrect error propagation.
+
+</p><p>
+
+</p><h2><a name="SECTION00051000000000000000"><br>
+4.1 Complexity and Robustness</a>
+</h2>
+
+<p>
+
+</p><p>
+<br></p><div align="CENTER">
+<table cellpadding="3" border="1" align="CENTER">
+<tbody><tr><td align="CENTER"><font size="-1">
+ </font></td>
+<td align="CENTER" colspan="2"><font size="-1"> </font><font size="-1"><b>By % Broken</b></font></td>
+<td align="CENTER" colspan="2"><font size="-1"> </font><font size="-1"><b>By Viol/Kloc</b></font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+
+Rank </font></td>
+<td align="LEFT"><font size="-1"> FS </font></td>
+<td align="RIGHT"><font size="-1"> Frac. </font></td>
+<td align="LEFT" colspan="2"><font size="-1"> FS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Viol/Kloc</font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+
+ 1 </font></td>
+<td align="LEFT"><font size="-1"> IBM JFS </font></td>
+<td align="RIGHT"><font size="-1"> 24.4 </font></td>
+<td align="LEFT"><font size="-1"> ext3 </font></td>
+<td align="RIGHT"><font size="-1"> 7.2 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+ 2 </font></td>
+<td align="LEFT"><font size="-1"> ext3 </font></td>
+<td align="RIGHT"><font size="-1"> 22.1 </font></td>
+<td align="LEFT"><font size="-1"> IBM JFS </font></td>
+<td align="RIGHT"><font size="-1"> 5.6 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+ 3 </font></td>
+<td align="LEFT"><font size="-1"> JFFS v2 </font></td>
+<td align="RIGHT"><font size="-1"> 15.7 </font></td>
+<td align="LEFT"><font size="-1"> NFS Client </font></td>
+<td align="RIGHT"><font size="-1"> 3.6 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+ 4 </font></td>
+<td align="LEFT"><font size="-1"> NFS Client </font></td>
+<td align="RIGHT"><font size="-1"> 12.9 </font></td>
+<td align="LEFT"><font size="-1"> VFS </font></td>
+<td align="RIGHT"><font size="-1"> 2.9 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+ 5 </font></td>
+<td align="LEFT"><font size="-1"> CIFS </font></td>
+<td align="RIGHT"><font size="-1"> 12.7 </font></td>
+<td align="LEFT"><font size="-1"> JFFS v2 </font></td>
+<td align="RIGHT"><font size="-1"> 2.2 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+ 6 </font></td>
+<td align="LEFT"><font size="-1"> MemMgmt </font></td>
+<td align="RIGHT"><font size="-1"> 11.4 </font></td>
+<td align="LEFT"><font size="-1"> CIFS </font></td>
+<td align="RIGHT"><font size="-1"> 2.1 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+ 7 </font></td>
+<td align="LEFT"><font size="-1"> ReiserFS </font></td>
+<td align="RIGHT"><font size="-1"> 10.5 </font></td>
+<td align="LEFT"><font size="-1"> MemMgmt </font></td>
+<td align="RIGHT"><font size="-1"> 2.0 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+ 8 </font></td>
+<td align="LEFT"><font size="-1"> VFS </font></td>
+<td align="RIGHT"><font size="-1"> 8.4 </font></td>
+<td align="LEFT"><font size="-1"> ReiserFS </font></td>
+<td align="RIGHT"><font size="-1"> 1.8 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+ 9 </font></td>
+<td align="LEFT"><font size="-1"> NTFS </font></td>
+<td align="RIGHT"><font size="-1"> 8.1 </font></td>
+<td align="LEFT"><font size="-1"> XFS </font></td>
+<td align="RIGHT"><font size="-1"> 1.4 </font></td>
+</tr>
+<tr><td align="CENTER"><font size="-1">
+10 </font></td>
+<td align="LEFT"><font size="-1"> XFS </font></td>
+<td align="RIGHT"><font size="-1"> 6.9 </font></td>
+<td align="LEFT"><font size="-1"> NFS Server </font></td>
+<td align="RIGHT"><font size="-1"> 1.2 </font></td>
+</tr>
+</tbody></table>
+
+</div>
+<br>
+<a name="table-analysis-robust"></a>
+
+<font size="-1"><i>
+Table 3: <b>Least Robust File Systems.</b> The table
+shows the ten least robust file systems using two ranking systems. In
+the first ranking system, file system robustness is ranked based on
+the fraction of broken channels over all error channels (the 5th
+column of Table 2). The second ranking system
+sorts file systems based on the number of broken channels found in
+every Kloc (the 6th column of Table 2).}
+</i></font><br>
+
+<br>
+
+<p>
+
+</p><p>
+In our first analysis, we would like to correlate the number of
+mistakes in a subsystem with the complexity of that subsystem. For
+file systems, XFS with 71 Kloc has more mistakes than other, smaller
+file systems. However, it is not necessary that XFS is seen as the
+least robust file system. Table&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#table-analysis-robust">3</a> sorts the
+robustness of each file system based on two rankings. In both
+rankings, we only account file systems that are at least 10 Kloc in
+size with at least 50 error-related calls, <i>i.e.</i>&nbsp;we only consider
+"complex" file systems.
+
+</p><p>
+A noteworthy observation is that ext3 and IBM JFS are ranked as the
+two least robust file systems. This fact affirms our earlier findings
+on the robustness of ext3 and IBM JFS&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#PrabhakaranEtAl05-SOSP">20</a>].
+In this prior work, we found that ext3 and IBM JFS are inconsistent in
+dealing with different kinds of disk failures. Thus, it might be the
+case that these inconsistent policies correlate with inconsistent
+error propagation.
+
+</p><p>
+Among storage device drivers, it is interesting to compare the
+robustness of the SCSI and IDE subsystems. If we compare SCSI and IDE
+subsystems using the first ranking system, SCSI and IDE are almost
+comparable (21% vs.&nbsp;18%). However, if we compare them based on the
+second ranking system, then the SCSI subsystem is almost four times
+more robust than IDE (0.6 vs.&nbsp;2.1 errors/Kloc). Nevertheless it seems
+the case that SCSI utilizes basic error codes much more than IDE does.
+
+</p><p>
+When the robustness of storage drivers and file systems is compared
+using the first ranking, on average storage drivers are less robust
+compared to file systems (22% vs.&nbsp;17%, as reported in the last rows
+of Table&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#table-result-all">2</a>). On the other hand, in the second
+ranking system, storage drivers are more robust compared to file
+systems (1.1 vs.&nbsp;2.4 mistakes/Kloc). From our point of view, the
+first ranking system is more valid because a subsystem could be
+comprised of submodules that do not necessarily use error codes; what
+is more important is the number of bad calls in the population of all
+error-related calls.
+
+</p><p>
+
+</p><h2><a name="SECTION00052000000000000000"></a>
+<a name="sec-analysis-neglected"></a><br>
+4.2 Neglected Write Errors
+</h2>
+
+<p>
+As mentioned in Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-result-silent">3.2</a>, we have observed that
+error codes propagated in <tt><font size="-1">write</font></tt> or <tt><font size="-1">sync</font></tt> operations are often
+ignored. Thus, we investigate how many write errors are neglected
+compared to read errors. This study is motivated by our findings in
+that section as well as by our earlier findings where we found that at
+least for ext3, read failures are detected, but write errors are often
+ignored&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#PrabhakaranEtAl05-SOSP">20</a>].
+
+</p><p>
+To perform this study, we filter out calls that do not relate to read
+and write operations. Since it is impractical to do that manually, we
+use a simple string comparison to mark calls that are relevant to our
+analysis. That is we only take a caller4#4callee pair
+where the callee contains the string <tt><font size="-1">read</font></tt>, <tt><font size="-1">write</font></tt>,
+<tt><font size="-1">sync</font></tt>, or <tt><font size="-1">wait</font></tt>. We include <tt><font size="-1">wait</font></tt>-type calls because in
+many cases <tt><font size="-1">wait</font></tt>-type callees (<i>e.g.</i>, <tt><font size="-1">filemap_datawait</font></tt>)
+represent waiting for one or more I/O operations and could return
+error information on the operation. Thus, in our study,
+<tt><font size="-1">write</font></tt>-, <tt><font size="-1">sync</font></tt>-, and <tt><font size="-1">wait</font></tt>-type calls are categorized as
+write operations.
+
+</p><p>
+<br></p><div align="CENTER">
+<table cellpadding="3" border="1" align="CENTER">
+<tbody><tr><td align="CENTER" colspan="1"><font size="-1">
+ </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ Bad </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ EC </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ </font><font size="-1"><b>Frac.</b></font></td>
+</tr>
+<tr><td align="CENTER" colspan="1"><font size="-1">
+ Callee Type</font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ Calls </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ Calls </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ </font><font size="-1"><b>(%)</b></font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+
+Read<sup>*</sup> </font></td>
+<td align="RIGHT"><font size="-1"> 26 </font></td>
+<td align="RIGHT"><font size="-1"> 603 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>4.3</b> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+Sync </font></td>
+<td align="RIGHT"><font size="-1"> 70 </font></td>
+<td align="RIGHT"><font size="-1"> 236 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>29.7</b> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+Wait </font></td>
+<td align="RIGHT"><font size="-1"> 27 </font></td>
+<td align="RIGHT"><font size="-1"> 70 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>38.6</b> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+Write </font></td>
+<td align="RIGHT"><font size="-1"> 80 </font></td>
+<td align="RIGHT"><font size="-1"> 598 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>13.4</b> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+Sync+Wait+Write </font></td>
+<td align="RIGHT"><font size="-1"> 177 </font></td>
+<td align="RIGHT"><font size="-1"> 904 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>19.6</b> </font></td>
+</tr>
+<tr><td align="CENTER" colspan="1"><font size="-1">
+
+ Specific Callee</font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+
+</font><tt><font size="-1">filemap_fdatawait</font></tt><font size="-1"> </font></td>
+<td align="RIGHT"><font size="-1"> 22 </font></td>
+<td align="RIGHT"><font size="-1"> 29 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>75.9</b> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+</font><tt><font size="-1">filemap_fdatawrite</font></tt><font size="-1"> </font></td>
+<td align="RIGHT"><font size="-1"> 30 </font></td>
+<td align="RIGHT"><font size="-1"> 47 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>63.8</b> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+</font><tt><font size="-1">sync_blockdev</font></tt><font size="-1"> </font></td>
+<td align="RIGHT"><font size="-1"> 15 </font></td>
+<td align="RIGHT"><font size="-1"> 21 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>71.4</b> </font></td>
+</tr>
+</tbody></table>
+
+</div>
+<br>
+<a name="table-ignored-writes"></a>
+
+<font size="-1"><i>
+Table 4: <b>Neglected write errors in file system code.</b>
+The table shows that read errors are handled more correctly than
+write errors. The upper table shows the fraction of bad calls over
+four category of calls: read, sync, wait, and write. The later three
+can be categorized as a write operation. The lower table shows
+neglected write errors for three specific functions. The 29 (*)
+violated read calls are all related to readahead and asynchronous
+read; in other words, all error codes returned in synchronous reads
+are being saved and checked.
+</i></font>
+<br>
+
+<br>
+
+<p>
+
+</p><p>
+The upper half of Table&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#table-ignored-writes">4</a> reports our
+findings. The last column shows how often errors are ignored in the
+file system code. Interestingly, file systems have a tendency to
+correctly handle error codes propagated from <tt><font size="-1">read</font></tt>-type calls, but
+not those from <tt><font size="-1">write</font></tt>-type calls (4.3% vs.&nbsp;19.6%). The 29
+(4.3%) unsaved read error codes are all found in readahead
+operations in the memory management subsystem; it might be acceptable
+to ignore prefetch read errors because such reads can be reissued in
+the future whenever the page is actually read.
+
+</p><p>
+As discussed in Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-result-unsaved">3.1</a>, a function could
+return more than one error code at the same time, and checking only
+one of them suffices. However, if we know that a certain function only
+returns a single error code and yet the caller does not save the
+return value properly, then we would know that such call is really a
+flaw. To find real flaws in the file system code, we examined three
+important functions that we know only return single error codes:
+<tt><font size="-1">sync_blockdev</font></tt>, <tt><font size="-1">filemap_fdatawrite</font></tt>, and
+<tt><font size="-1">filemap_fdatawait</font></tt>. A file system that does not check the
+returned error codes from these functions would obviously let failures
+go unnoticed in the upper layers.
+
+</p><p>
+The lower half of Table&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#table-ignored-writes">4</a> reports our
+findings. Many error codes returned from the three methods are simply
+not saved (&gt; 63% in all cases). Two conclusions might be drawn from
+this observation. First, this could suggest that higher-level recovery
+code does not exist (since if it exists, it will not be invoked due to
+the broken error channel), or it could be the case that errors are
+intentionally neglected. We consider this second possibility in
+greater detail in the next section.
+
+</p><p>
+
+</p><h2><a name="SECTION00053000000000000000"></a>
+<a name="sec-analysis-inconsistent"></a><br>
+4.3 Inconsistent Calls: Corner Case or Majority?
+</h2>
+
+<p>
+</p><p>
+In this section, we consider the nature of <em>inconsistent</em> calls.
+For example, we found that 1 out of 33 calls to
+<tt><font size="-1">ide_setup_pci_device</font></tt> does not save the return value. One would
+probably consider this single call as an inconsistent implementation
+because the majority of the calls to that function save the return
+value. On the other hand, we also found that 53 out of 54 calls to
+<tt><font size="-1">unregister_filesystem</font></tt> do not save the return error codes.
+Assuming that most kernel developers are essentially competent, this
+suggests that it may actually be safe to not check the error code
+returned from this particular function.
+
+</p><p>
+To quantify inconsistent calls, we define the <em>inconsistent call
+frequency</em> of a function as the ratio of bad calls over all
+error-related calls to the function, and correlate this frequency with
+the number of bad calls to the function. For example, the
+inconsistent call frequencies for <tt><font size="-1">ide_setup_pci_blockdev</font></tt> and
+<tt><font size="-1">unregister_filesystem</font></tt> are 3% (1/33) and 98% (53/54)
+respectively and the numbers of bad calls are 1 and 53 respectively.
+
+</p><p>
+Figure&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#fig-analysis-inconsistent">6</a> plots the cumulative
+distribution function of this behavior. The graph could be seen as a
+means to prioritize which bad calls to fix first. Bad calls that fall
+below the 20% mark could be treated as <em>corner cases</em>, <i>i.e.</i>&nbsp;we
+should be suspicious on one bad call in the midst of four good calls
+to the same function. On the other hand, bad calls that fall above the
+80% mark could hint that either different developers make the same
+mistake and ignore it, or it is probably safe to make such a mistake.
+
+</p><p>
+
+</p><div align="CENTER">
+
+<p><a name="fig-analysis-inconsistent"></a></p><div align="CENTER">
+<img src="./Error Handling is Ocassionally Correct_files/fig-analysis-cdf.gif">
+</div>
+ <br>
+<font size="-1"><i>
+Figure 6: <b>Inconsistent calls frequency.</b>
+The figure shows that inconsistent calls are not corner-case bugs.
+The x-axis represents the inconsistent-call frequency of a function.
+x=20% means that there is one bad call out of five total calls;
+x=80% means that there are four bad calls out of five total calls.
+The left y-axis counts the cumulative number of bad calls. For example,
+below the 20% mark, there are 80 bad calls that have an
+inconsistent-call frequency of less than 20%.
+As reported in
+Table 2, there exist a total of 1153 bad calls.
+The right y-axis shows the cumulative fraction of bad calls over
+the 1153 bad calls. </i></font>
+
+<br>
+
+</div>
+
+
+<p>
+One perplexing phenomenon visible in the graph is that around 871 bad
+calls fall above the 50% mark. In other words, they cannot be
+considered as corner-case bugs; the developers might be aware of these
+bad calls, but probably just ignore them. One thing we have learned
+from our recent work on file system code is that if a file system does
+not know how to recover from a failure, it has the tendency to just
+ignore the error code. For example, ext3 ignores write failures during
+checkpointing simply because it has no recovery mechanism (<i>e.g.</i>,
+chained transactions&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#GunawiEtAl07-IOShepherd">12</a>]) to deal with such
+failures. Thus, we suspect that there are deeper design shortcomings
+behind poor error code handling; error code mismanagement may be as
+much symptom as disease.
+
+</p><p>
+Our analysis is similar to the work of Engler&nbsp;<i>et al.</i> on findings bugs
+automatically&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#EnglerEtAl01-Bugs">8</a>]. In their work, they use
+existing implementation to imply beliefs and facts. Applying their
+analysis to our case, the bad calls that fall above the 80% mark
+might be considered as good calls.
+However, since we are analyzing the specific problem of error
+propagation, we use that semantic knowledge and demand a discipline
+that promotes checking an error code in all circumstances, rather than
+one that follows majority rules.
+
+</p><p>
+
+</p><h2><a name="SECTION00054000000000000000"></a>
+<a name="sec-analysis-characteristic"></a><br>
+4.4 Characteristics of Error Channels
+</h2>
+
+<p>
+Finally, we study whether the characteristic of an error channel has
+an impact on the robustness of error code propagation in that channel.
+In particular, we explore two characteristics of error channels: one
+based on the error propagation distance and one based on the location
+distance (inter- vs.&nbsp;intra-file calls).
+
+</p><p>
+With the first characteristic, we would like to find out whether error
+codes are lost near the generation endpoint or somewhere in the middle
+of the propagation chain. We distinguish two calls: direct-error and
+propagate-error calls. In a <em>direct-error call</em>, the callee is an
+error-generation endpoint. In a <em>propagate-error call</em>, the
+callee is not a generation endpoint; rather it is a function that
+propagates an error code from one of the functions that it calls,
+<i>i.e.</i>&nbsp;it is a function in the middle of the propagation chain. Next, we
+define a <em>bad</em> direct-error (or propagate-error) call as a
+direct-error (or propagate-error) call that does not save the returned
+error code.
+
+</p><p>
+Initially, we assumed that the frequency of bad propagate-error calls
+would be higher than that of bad direct-error calls; we assumed error
+codes tend to be dropped in the middle of the chain rather than near
+the generation endpoint. It turns out that the number of bad
+direct-error and propagate-error calls are similar for file system
+code but the other way around for storage driver code. In particular,
+for file systems, the ratio of bad over all direct-error calls is
+10%, and the ratio of bad over all propagate-error calls is 14%. For
+storage drivers, they are 20% and 15% respectively.
+
+</p><p>
+
+</p><p>
+<br></p><div align="CENTER">
+<table cellpadding="3" border="1" align="CENTER">
+<tbody><tr><td align="CENTER" colspan="1"><font size="-1">
+ </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ Bad </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ EC </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ </font><font size="-1"><b>Frac.</b> </font></td>
+</tr>
+<tr><td align="CENTER" colspan="1"><font size="-1">
+ </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ Calls </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ Calls </font></td>
+<td align="CENTER" colspan="1"><font size="-1">
+ </font><font size="-1"><b>(%)</b> </font></td>
+</tr>
+<tr><td align="CENTER" colspan="4"><font size="-1">
+
+ </font><font size="-1"><em>File Systems</em> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+
+Inter-module </font></td>
+<td align="RIGHT"><font size="-1"> 307 </font></td>
+<td align="RIGHT"><font size="-1"> 1944 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>15.8</b> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+Inter-file </font></td>
+<td align="RIGHT"><font size="-1"> 367 </font></td>
+<td align="RIGHT"><font size="-1"> 2786 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>13.2</b> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+Intra-file </font></td>
+<td align="RIGHT"><font size="-1"> 159 </font></td>
+<td align="RIGHT"><font size="-1"> 2548 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>6.2</b> </font></td>
+</tr>
+<tr><td align="CENTER" colspan="4"><font size="-1">
+
+ </font><font size="-1"><em>Storage Drivers</em> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+
+Inter-module </font></td>
+<td align="RIGHT"><font size="-1"> 48 </font></td>
+<td align="RIGHT"><font size="-1"> 199 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>24.1</b> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+Inter-file </font></td>
+<td align="RIGHT"><font size="-1"> 92 </font></td>
+<td align="RIGHT"><font size="-1"> 495 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>18.6</b> </font></td>
+</tr>
+<tr><td align="LEFT"><font size="-1">
+Intra-file </font></td>
+<td align="RIGHT"><font size="-1"> 180 </font></td>
+<td align="RIGHT"><font size="-1"> 1050 </font></td>
+<td align="RIGHT"><font size="-1"> </font><font size="-1"><b>17.1</b> </font></td>
+</tr>
+</tbody></table>
+
+</div>
+<br>
+<a name="table-inter-module"></a>
+
+<font size="-1"><i>
+Table 5: <b>Calls based on location distance.</b> The
+table shows that the fraction of bad calls in inter-module calls is
+higher than the one in inter-file calls. Similarly, inter-file calls
+are less robust than intra-file calls. Note that "inter-file"
+refers to cross-file calls within the same module. Inter-file calls across
+different modules are categorized as inter-module. </i></font>
+
+<br>
+
+<br>
+
+<p>
+
+</p><p>
+Lastly, in the second characteristic, we categorized calls based on
+the location distance between a caller and a callee. In particular, we
+distinguish three calls: inter-module, inter-file (but within the same
+module), and intra-file calls. Table&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#table-inter-module">5</a> reports
+that intra-file calls are more robust than inter-file calls, and
+inter-file calls are more robust than intra-file calls. For example,
+out of 1944 inter-module calls in which error codes propagate in file
+system, 307 (16%) of them are bad calls. However, out of 2786
+inter-file calls within the same module, there are only 367 (13%) bad
+calls. Intra-file calls only exhibit 6% bad calls. The same pattern
+occurs in storage device drivers. Thus, we conclude that the location
+distance between the caller and the callee plays a role in the
+robustness of the call.
+
+</p><p>
+
+</p><p>
+
+</p><h1><a name="SECTION00060000000000000000"></a>
+<a name="sec-future"></a><br>
+5 Future Work
+</h1>
+
+<p>
+In this section, we discuss some of the issues we previously deferred
+regarding how to build complete and accurate static error propagation
+analysis. In general, we plan to refine our static analysis with the
+intention of uncovering more violations within the file and storage
+system stack.
+
+</p><p>
+
+</p><h2><a name="SECTION00061000000000000000"></a>
+<a name="sec-future-overwritten"></a><br>
+5.1 Overwritten Error Codes
+</h2>
+
+<p>
+In this paper, we examined broken channels that are caused by unsaved
+and unchecked error codes; broken channels can also be caused by <em>overwritten error codes</em>, in which the container that holds the error
+code is overwritten with another value before the previous error is
+checked. For example, the CIFS code below overwrites (line 6) the
+previous error code received from another call (line 4).
+
+</p><p>
+</p><pre> 1 // cifs/transport.c
+ 2 int SendReceive () {
+ 3 int rc;
+ 4 rc = cifs_sign_smb(); // PROPAGATE E.C.
+ 5 ... // No use of 'rc' here
+ 6 rc = smb_send(); // OVERWRITTEN
+ 7 }
+</pre>
+
+<p>
+Currently, EDP detects overwritten error codes, but reports too many
+false positives to be useful. We are in the process of fine-tuning
+EDP so that it provides more accurate output. The biggest problem we
+have encountered is due to the nature of the error hierarchy: in many
+cases, a less critical error code is overwritten with a more critical
+one. For example, in the memory management code below, when first
+encountering a page error, the error code is set to <tt><font size="-1">EIO</font></tt> (line 6).
+Later, the function checks whether the flags of a <tt><font size="-1">map</font></tt> structure
+carry a no-space error code (line 8). If so, the <tt><font size="-1">EIO</font></tt> error code
+is overwritten (line 9) with a new error code <tt><font size="-1">ENOSPC</font></tt>.
+
+</p><p>
+</p><pre> 1 // mm/filemap.c
+ 2 int wait_on_page_writeback_range (pg, map) {
+ 3 int ret = 0;
+ 4 ...
+ 5 if (PageError(pg))
+ 6 ret = -EIO;
+ 7 ...
+ 8 if (test_bit(AS_ENOSPC, &amp;map-&gt;flags))
+ 9 ret = -ENOSPC;
+ 10 if (test_bit (AS_EIO, &amp;map-&gt;flags))
+ 11 ret = -EIO;
+ 12 return ret;
+ 13 }
+</pre>
+
+<p>
+Manually inspecting the results obtained from EDP, we have identified
+five real cases of overwritten error codes: one each in AFS and FAT,
+and three in CIFS. We believe we will find more cases as we fine-tune
+our analysis of overwritten error codes.
+
+</p><p>
+
+</p><h2><a name="SECTION00062000000000000000"></a>
+<a name="sec-future-transform"></a><br>
+5.2 Error Transformation
+</h2>
+
+<p>
+Our current EDP analysis focuses on the basic error codes that are
+stored and propagated mainly in integer containers. However, file and
+storage systems also use other specific error codes stored in complex
+structures that can be mapped to other error codes in new error
+containers; we call this issue <em>error transformation</em>. For
+example, the block layer clears the <tt>uptodate</tt> bit stored in a
+buffer structure to signal I/O failure, while the VFS layer simply
+uses generic error codes such as <tt><font size="-1">EIO</font></tt> and <tt><font size="-1">EROFS</font></tt>. We have observed a
+path where an error container changes five times, involving four
+different types of containers. A complete EDP analysis must recognize
+all transformations. With a more complete analysis, we expect to see
+even more violations.
+
+</p><p>
+
+</p><h2><a name="SECTION00063000000000000000"></a>
+<a name="sec-future-channel"></a><br>
+5.3 Asynchronous Error Channels
+</h2>
+
+<p>
+Finally, we plan to expand our definition of error channels to include
+<em>asynchronous paths</em>. We briefly describe two examples of
+asynchronous paths and their complexities. First, when a lower layer
+interrupts an upper one to notify it of the completion of an I/O, the
+low-level I/O error code is usually stored in a structure located in
+the heap; the receiver of the interrupt should grab the structure and
+check the error it carries, but tracking this propagation through the
+heap is not straightforward. Another example occurs during
+journaling: a journal daemon is woken up somewhere in the <tt><font size="-1">fsync()</font></tt>
+path and propagates a journal error code via a global journal state.
+When we consider asynchronous error channels, we also expect the
+number of violations to increase.
+
+</p><p>
+
+</p><p>
+
+</p><h1><a name="SECTION00070000000000000000"></a>
+<a name="sec-related"></a><br>
+6 Related Work
+</h1>
+
+<p>
+Previous work has used static techniques to understand variety of
+problems in software systems. For example, Meta-level compilation
+(MC)&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#EnglerEtAl00-SystemRules">7</a>,<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#EnglerEtAl01-Bugs">8</a>] enables a
+programmer to write simple, system-specific compiler extensions to
+automatically check software for rule violations. With their work, one
+can find broken channels by specifying a rule such as "a returned
+variable must be checked."
+Compared to their work, ours presents more information on how error
+propagates and convert it into graphical output for ease of analysis
+and debugging.
+
+</p><p>
+Another related project is FiSC&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#YangEtAl04-FSErrors">32</a>], which uses
+the model-checking tool CMC&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#MusuvathiEtAl02-CMC">17</a>] to find file
+system errors in the Linux kernel. Every time the file system under
+test transitions to a new state, FiSC runs a series of invariant
+checkers looking for file system errors. If an error is found, one can
+trace back the states and diagnose the sequence of actions that lead
+to the error. One aspect of our work that is similar to FiSC is that
+we unearth silent failures.
+For example, FiSC detects a bug where a system call returns success
+after it calls a resource allocation routine that fails, <i>e.g.</i>&nbsp;due to
+memory failures.
+
+</p><p>
+In recent work, Johansson analyzes run-time error propagation based on
+interface observations&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#JohanssonSuri05-ErrorProfiling">14</a>].
+Specifically, an error is injected at the OS-driver interface by
+changing the value of a data parameter. By observing the
+application-OS interface after the error injection, they reveal
+whether errors occurring in the OS environment (device drivers) will
+propagate through the OS and affect applications. This run-time
+technique is complementary to our work, especially to uncover the
+eventual bad effects of error-broken channels.
+
+</p><p>
+Solving the error propagation problem is also similar to solving the
+problem of unchecked exceptions. Sacramento <i>et al.</i> found too many
+unchecked exceptions, thus doubting programmers' assurances in
+documenting exceptions&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#SacramentoEtAl06-Exception">25</a>].
+Nevertheless, since using exceptions is not a kernel programming
+style, at least at the current state, solutions to the problem of
+unchecked exceptions might not be applicable to kernel code. Only
+recently is there an effort in employing exceptions in OS
+code&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#CabralMarques06-Exception">3</a>].
+
+</p><p>
+Our tool is also similar to
+Jex&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#RobillardMurphy00-RobustJava">24</a>]. While Jex is a static
+analysis tool that determines exception flow information in Java
+programs, our tool determines the error code flow information within
+the Linux kernel.
+
+</p><p>
+To fix the incomplete error propagation problem, developers could
+simply adopt a simple set-check-use
+methodology&nbsp;[<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#BigriggVos02-SetCheckUse">2</a>]. However, it is
+interesting to see that this simple practice has not been applied
+thoroughly in file systems and storage device drivers. As mentioned
+in Section&nbsp;<a href="https://www.usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html#sec-analysis-inconsistent">4.3</a>, we suspect that there are
+deeper design shortcomings behind poor error code handling.
+
+</p><p>
+
+</p><p>
+
+</p><h1><a name="SECTION00080000000000000000"></a>
+<a name="sec-conclude"></a><br>
+7 Conclusion
+</h1>
+
+<p>
+In this paper, we have analyzed the file and storage systems in Linux 2.6 and
+found that error codes are not consistently propagated. We conclude by
+reprinting some developer comments we found near some problematic cases:
+
+</p><p>
+
+</p><p>
+
+</p><blockquote><font size="-1">CIFS -
+<em>"Not much we can do if it fails anyway, ignore rc." </em>
+</font></blockquote>
+<p>
+
+</p><blockquote><font size="-1">CIFS -
+<em>"Should we pass any errors back?" </em>
+</font></blockquote>
+<p>
+
+</p><blockquote><font size="-1">ext3 -
+<em>"Error, skip block and hope for the best." </em>
+</font></blockquote>
+<p>
+
+</p><blockquote><font size="-1">ext3 -
+<em>"There's no way of reporting error returned from
+ext3_mark_inode_dirty() to userspace. So ignore it." </em>
+</font></blockquote>
+<p>
+
+</p><blockquote><font size="-1">IBM JFS -
+<em>"Note: todo: log error handler." </em>
+</font></blockquote>
+<p>
+
+</p><blockquote><font size="-1">ReiserFS -
+<em>"We can't do anything about an error here." </em>
+</font></blockquote>
+<p>
+
+</p><blockquote><font size="-1">XFS -
+<em>"Just ignore errors at this point. There is
+nothing we can do except to try to keep going." </em>
+</font></blockquote>
+<p>
+
+</p><blockquote><font size="-1">SCSI -
+<em>"Retval ignored?" </em>
+</font></blockquote>
+<p>
+
+</p><blockquote><font size="-1">SCSI -
+<em>"Todo: handle failure." </em>
+</font></blockquote>
+<p>
+
+
+</p><p>
+These comments from developers indicate part of the problem: even when the
+developers are aware they are not properly propagating an error, they do not
+know how to implement the correct response. Given static analysis tools to
+identify the source of bugs (such as EDP), developers may still not be able to
+fix all bugs in a straightforward manner.
+
+</p><p>
+Due to these observations, we believe it is thus time to rethink how
+failures are managed in large systems. Preaching that developers
+follow error handling conventions and hoping the resulting systems
+work as desired seems naive at best. New approaches to error
+detection, propagation, and recovery are needed; in the future, we
+plan to explore a range of error architectures, hoping to find methods
+that increase the level of robustness in the storage systems upon
+which we all rely.
+
+</p><p>
+
+</p><p>
+
+</p><h1><a name="SECTION00090000000000000000"><br>
+Acknowledgments</a>
+</h1>
+
+<p>
+We thank the members of the ADSL research group for their insightful
+comments. We would also like to thank Geoff Kuenning (our shepherd)
+and the anonymous reviewers for their excellent feedback and comments,
+many of which have greatly improved this paper.
+The second author wishes to thank the National Council on Science and
+Technology of Mexico
+and the Secretariat of Public Education
+for their financial support.
+
+</p><p>
+This work is supported by the National Science Foundation
+under the following grants:
+CCF-0621487,
+CNS-0509474,
+CCR-0133456,
+as well as by generous donations from Network Appliance and Sun Microsystems.
+
+</p><p>
+Any opinions, findings, and conclusions or recommendations expressed
+in this material are those of the authors and do not necessarily
+reflect the views of NSF or other institutions.
+
+</p><p>
+
+</p><p>
+ <font size="-1">
+ </font>
+</p><h1><a name="SECTION000100000000000000000"><br>
+Bibliography</a>
+</h1><dl compact=""><dd><p></p></dd><dt><a name="Best00-JFS-Local">1</a>
+</dt><dd>
+Steve Best.
+<br>JFS Overview.
+<br>www.ibm.com/developer works/library/l-jfs.html, 2000.
+
+<p></p></dd><dt><a name="BigriggVos02-SetCheckUse">2</a>
+</dt><dd>
+Michael&nbsp;W. Bigrigg and Jacob&nbsp;J. Vos.
+<br>The Set-Check-Use Methodology for Detecting Error Propagation
+ Failures in I/O Routines.
+<br>In <em>WDB '02</em>, Washington, DC, June 2002.
+
+<p></p></dd><dt><a name="CabralMarques06-Exception">3</a>
+</dt><dd>
+Bruno Cabral and Paulo Marques.
+<br>Making Exception Handling Work.
+<br>In <em>HotDep II</em>, Seattle, Washington, Nov 2006.
+
+<p></p></dd><dt><a name="CandeaEtAl04-Reboot">4</a>
+</dt><dd>
+George Candea, Shinichi Kawamoto, Yuichi Fujiki, Greg Friedman, and Armando
+ Fox.
+<br>Microreboot - A Technique for Cheap Recovery.
+<br>In <em>OSDI '04</em>, pages 31-44, San Francisco, CA, December 2004.
+
+<p></p></dd><dt><a name="CowanEtAl98-Stackguard">5</a>
+</dt><dd>
+Crispin Cowan, Calton Pu, Dave Maier, Heather Hinton, Jonathan Walpole, Peat
+ Bakke, Steve Beattie, Aaron Grier, Perry Wagle, and Qian Zhang.
+<br>StackGuard: Automatic adaptive detection and prevention of
+ buffer-overflow attacks.
+<br>In <em>USENIX '98 Security</em>, San Antonio, TX, January 1998.
+
+<p></p></dd><dt><a name="EllardMegquier05-DISP">6</a>
+</dt><dd>
+Daniel Ellard and James Megquier.
+<br>DISP: Practical, Efficient, Secure, and Faul-Tolerant Distributed
+ Data Storage.
+<br><em>ACM Transactions on Storage (TOS)</em>, 1(1):71-94, Feb 2005.
+
+<p></p></dd><dt><a name="EnglerEtAl00-SystemRules">7</a>
+</dt><dd>
+Dawson Engler, Benjamin Chelf, Andy Chou, and Seth Hallem.
+<br>Checking System Rules Using System-Specific, Programmer-Written
+ Compiler Extensions .
+<br>In <em>OSDI '00</em>, San Diego, CA, October 2000.
+
+<p></p></dd><dt><a name="EnglerEtAl01-Bugs">8</a>
+</dt><dd>
+Dawson Engler, David&nbsp;Yu Chen, Seth Hallem, Andy Chou, and Benjamin Chelf.
+<br>Bugs as Deviant Behavior: A General Approach to Inferring Errors in
+ Systems Code.
+<br>In <em>SOSP '01</em>, pages 57-72, Banff, Canada, October 2001.
+
+<p></p></dd><dt><a name="EnglerDunbar07-UnderConstrained">9</a>
+</dt><dd>
+Dawson&nbsp;R. Engler and Daniel Dunbar.
+<br>Under-constrained execution: making automatic code destruction easy
+ and scalable.
+<br>In <em>ISSTA '07</em>, London, United Kingdom, July 2007.
+
+<p></p></dd><dt><a name="GodefroidEtAl05-DART">10</a>
+</dt><dd>
+Patrice Godefroid, Nils Klarlund, and Koushik Sen.
+<br>DART: Directed Automated Random Testing.
+<br>In <em>PLDI '05</em>, Chicago, IL, June 2005.
+
+<p></p></dd><dt><a name="EdpOutput">11</a>
+</dt><dd>
+Haryadi&nbsp;S. Gunawi.
+<br>EDP Output for All File Systems.
+<br>www.cs.wisc.edu/adsl/Publications/eio-fast08/ readme.html.
+
+<p></p></dd><dt><a name="GunawiEtAl07-IOShepherd">12</a>
+</dt><dd>
+Haryadi&nbsp;S. Gunawi, Vijayan Prabhakaran, Swetha Krishnan, Andrea&nbsp;C.
+ Arpaci-Dusseau, and Remzi&nbsp;H. Arpaci-Dusseau.
+<br>Improving File System Reliability with I/O Shepherding.
+<br>In <em>SOSP '07</em>, pages 283-296, Stevenson, Washington, October
+ 2007.
+
+<p></p></dd><dt><a name="Hind01-PointerAnalysis">13</a>
+</dt><dd>
+Michael Hind.
+<br>Pointer Analysis: Haven't We Solved This Problem Yet?
+<br>In <em>PASTE '01</em>, Snowbird, Utah, June 2001.
+
+<p></p></dd><dt><a name="JohanssonSuri05-ErrorProfiling">14</a>
+</dt><dd>
+Andreas Johansson and Neeraj Suri.
+<br>Error Propagation Profiling of Operating Systems .
+<br>In <em>DSN '05</em>, Yokohoma, Japan, June 2005.
+
+<p></p></dd><dt><a name="KolaEtAl05-FaultInLDS">15</a>
+</dt><dd>
+George Kola, Tevfik Kosar, and Miron Livny.
+<br>Faults in Large Distributed Systems and What We Can Do About Them.
+<br>In <em>Euro-Par</em>, August 2005.
+
+<p></p></dd><dt><a name="KoopmanDeVale99-POSIX">16</a>
+</dt><dd>
+Philip Koopman and John DeVale.
+<br>Comparing the Robustness of POSIX Operating Systems.
+<br>In <em>FTCS-29</em>, Madison, Wisconsin, June 1999.
+
+<p></p></dd><dt><a name="MusuvathiEtAl02-CMC">17</a>
+</dt><dd>
+Madanlal Musuvathi, David&nbsp;Y.W. Park, Andy Chou, Dawson&nbsp;R. Engler, and David&nbsp;L.
+ Dill.
+<br>CMC: A Pragmatic Approach to Model Checking Real Code.
+<br>In <em>OSDI '02</em>, Boston, MA, December 2002.
+
+<p></p></dd><dt><a name="NeculaEtAl05-CCured">18</a>
+</dt><dd>
+George&nbsp;C. Necula, Jeremy Condit, Matthew Harren, Scott McPeak, and Westley
+ Weimer.
+<br>CCured: Type-Safe Retrofitting of Legacy Software.
+<br><em>ACM Transactions on Programming Languages and Systems</em>, 27(3),
+ May 2005.
+
+<p></p></dd><dt><a name="Necula02-CIL">19</a>
+</dt><dd>
+George&nbsp;C. Necula, Scott McPeak, S.&nbsp;P. Rahul, and Westley Weimer.
+<br>Cil: An infrastructure for c program analysis and transformation.
+<br>In <em>CC '02</em>, pages 213-228, April 2002.
+
+<p></p></dd><dt><a name="PrabhakaranEtAl05-SOSP">20</a>
+</dt><dd>
+Vijayan Prabhakaran, Lakshmi&nbsp;N. Bairavasundaram, Nitin Agrawal, Haryadi&nbsp;S.
+ Gunawi, Andrea&nbsp;C. Arpaci-Dusseau, and Remzi&nbsp;H. Arpaci-Dusseau.
+<br>IRON File Systems.
+<br>In <em>SOSP '05</em>, pages 206-220, Brighton, UK, October 2005.
+
+<p></p></dd><dt><a name="QinEtAl05-Safemem">21</a>
+</dt><dd>
+Feng Qin, Shan Lu, and Yuanyuan Zhou.
+<br>Exploiting ECC-memory for detecting memory leaks and memory
+ corruption during production runs.
+<br>In <em>HPCA-11</em>, San Francisco, California, February 2005.
+
+<p></p></dd><dt><a name="QinEtAl05-Rx">22</a>
+</dt><dd>
+Feng Qin, Joseph Tucek, Jagadeesan Sundaresan, and Yuanyuan Zhou.
+<br>Rx: Treating Bugs As Allergies.
+<br>In <em>SOSP '05</em>, Brighton, UK, October 2005.
+
+<p></p></dd><dt><a name="Reiser04-ReiserFS">23</a>
+</dt><dd>
+Hans Reiser.
+<br>ReiserFS.
+<br>www.namesys.com, 2004.
+
+<p></p></dd><dt><a name="RobillardMurphy00-RobustJava">24</a>
+</dt><dd>
+Martin&nbsp;P. Robillard and Gail&nbsp;C. Murphy.
+<br>Designing Robust Java Programs with Exceptions.
+<br>In <em>FSE '00</em>, San Diego, CA, November 2000.
+
+<p></p></dd><dt><a name="SacramentoEtAl06-Exception">25</a>
+</dt><dd>
+Paulo Sacramento, Bruno Cabral, and Paulo Marques.
+<br>Unchecked Exceptions: Can the Programmer be Trusted to Document
+ Exceptions?
+<br>In <em>IVNET '06</em>, Florianopolis, Brazil, October 2006.
+
+<p></p></dd><dt><a name="SidiroglouEtAl05-STEM">26</a>
+</dt><dd>
+Stelios Sidiroglou, Michael&nbsp;E. Locasto, Stephen&nbsp;W. Boyd, and Angelos&nbsp;D.
+ Keromytis.
+<br>Building a Reactive Immune System for Software Services.
+<br>In <em>USENIX '05</em>, Anaheim, CA, April 2005.
+
+<p></p></dd><dt><a name="Solomon98-NT">27</a>
+</dt><dd>
+David&nbsp;A. Solomon.
+<br><em>Inside Windows NT</em>.
+<br>Microsoft Programming Series. Microsoft Press, 2nd edition, May 1998.
+
+<p></p></dd><dt><a name="SwiftEtAl03-Nooks">28</a>
+</dt><dd>
+Michael&nbsp;M. Swift, Brian&nbsp;N. Bershad, and Henry&nbsp;M. Levy.
+<br>Improving the Reliability of Commodity Operating Systems.
+<br>In <em>SOSP '03</em>, Bolton Landing, NY, October 2003.
+
+<p></p></dd><dt><a name="SwiftEtAl04-MoreNooks">29</a>
+</dt><dd>
+Michael&nbsp;M. Swift, Brian&nbsp;N. Bershad, and Henry&nbsp;M. Levy.
+<br>Recovering device drivers.
+<br>In <em>OSDI '04</em>, pages 1-16, San Francisco, CA, December 2004.
+
+<p></p></dd><dt><a name="ThainLivny02-ErrorScope">30</a>
+</dt><dd>
+Douglas Thain and Miron Livny.
+<br>Error Scope on a Computational Grid: Theory and Practice.
+<br>In <em>HPDC 11</em>, Edinburgh, Scotland, July 2002.
+
+<p></p></dd><dt><a name="Tweedie98-JournalingExt2">31</a>
+</dt><dd>
+Stephen&nbsp;C. Tweedie.
+<br>Journaling the Linux ext2fs File System.
+<br>In <em>The Fourth Annual Linux Expo</em>, Durham, North Carolina, May
+ 1998.
+
+<p></p></dd><dt><a name="YangEtAl04-FSErrors">32</a>
+</dt><dd>
+Junfeng Yang, Paul Twohey, Dawson Engler, and Madanlal Musuvathi.
+<br>Using Model Checking to Find Serious File System Errors.
+<br>In <em>OSDI '04</em>, San Francisco, CA, December 2004.
+</dd></dl>
+
+
+<p>
+<font size="-1"></font>
+</p><p>
+
+</p><p>
+
+<br><br><br>
+<script type="text/javascript">window.NREUM||(NREUM={});NREUM.info={"beacon":"bam.nr-data.net","licenseKey":"d823139095","applicationID":"509444","transactionName":"YVJVZksCXkEEVhIMWFgYdlFNCl9cSkAVAFlfT2hAXAdZQABWEhZoWFhDbV8MRVwB","queueTime":0,"applicationTime":150,"ttGuid":"","agentToken":"","atts":"TRVWEAMYTU8=","errorBeacon":"bam.nr-data.net","agent":""}</script>
+
+</p></body></html> \ No newline at end of file
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-analysis-cdf.gif b/reference/Error Handling is Ocassionally Correct_files/fig-analysis-cdf.gif
new file mode 100644
index 00000000..d5ae4632
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-analysis-cdf.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-method-edp.gif b/reference/Error Handling is Ocassionally Correct_files/fig-method-edp.gif
new file mode 100644
index 00000000..1e87f539
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-method-edp.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-result-big-legend.gif b/reference/Error Handling is Ocassionally Correct_files/fig-result-big-legend.gif
new file mode 100644
index 00000000..0af0c054
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-result-big-legend.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-result-big.gif b/reference/Error Handling is Ocassionally Correct_files/fig-result-big.gif
new file mode 100644
index 00000000..26b329b1
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-result-big.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-result-small-ext3.gif b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-ext3.gif
new file mode 100644
index 00000000..0056b2a5
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-ext3.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-result-small-hfsplus.gif b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-hfsplus.gif
new file mode 100644
index 00000000..db590184
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-hfsplus.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-result-small-jfs.gif b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-jfs.gif
new file mode 100644
index 00000000..b423e830
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-jfs.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-result-small-nfs.gif b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-nfs.gif
new file mode 100644
index 00000000..d0adf7d5
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-nfs.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-result-small-reiserfs.gif b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-reiserfs.gif
new file mode 100644
index 00000000..3775d950
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-reiserfs.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-result-small-xfs.gif b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-xfs.gif
new file mode 100644
index 00000000..ee8dfcdf
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-result-small-xfs.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/fig-result-zoom.gif b/reference/Error Handling is Ocassionally Correct_files/fig-result-zoom.gif
new file mode 100644
index 00000000..4887e0ec
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/fig-result-zoom.gif
Binary files differ
diff --git a/reference/Error Handling is Ocassionally Correct_files/main.css b/reference/Error Handling is Ocassionally Correct_files/main.css
new file mode 100644
index 00000000..4ea4b224
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/main.css
@@ -0,0 +1,210 @@
+<!DOCTYPE html>
+<!--[if lt IE 7 ]> <html lang="en" dir="ltr"
+ xmlns:og="http://ogp.me/ns#"
+ xmlns:article="http://ogp.me/ns/article#"
+ xmlns:book="http://ogp.me/ns/book#"
+ xmlns:profile="http://ogp.me/ns/profile#"
+ xmlns:video="http://ogp.me/ns/video#"
+ xmlns:product="http://ogp.me/ns/product#" class="no-js ie6"> <![endif]-->
+<!--[if IE 7 ]> <html lang="en" dir="ltr"
+ xmlns:og="http://ogp.me/ns#"
+ xmlns:article="http://ogp.me/ns/article#"
+ xmlns:book="http://ogp.me/ns/book#"
+ xmlns:profile="http://ogp.me/ns/profile#"
+ xmlns:video="http://ogp.me/ns/video#"
+ xmlns:product="http://ogp.me/ns/product#" class="no-js ie7"> <![endif]-->
+<!--[if IE 8 ]> <html lang="en" dir="ltr"
+ xmlns:og="http://ogp.me/ns#"
+ xmlns:article="http://ogp.me/ns/article#"
+ xmlns:book="http://ogp.me/ns/book#"
+ xmlns:profile="http://ogp.me/ns/profile#"
+ xmlns:video="http://ogp.me/ns/video#"
+ xmlns:product="http://ogp.me/ns/product#" class="no-js ie8"> <![endif]-->
+<!--[if (gte IE 9)|!(IE)]><!-->
+
+<html lang="en" dir="ltr"
+ xmlns:og="http://ogp.me/ns#"
+ xmlns:article="http://ogp.me/ns/article#"
+ xmlns:book="http://ogp.me/ns/book#"
+ xmlns:profile="http://ogp.me/ns/profile#"
+ xmlns:video="http://ogp.me/ns/video#"
+ xmlns:product="http://ogp.me/ns/product#" class="no-js">
+<!--<![endif]-->
+
+<head profile="http://www.w3.org/1999/xhtml/vocab">
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<link rel="shortcut icon" href="https://www.usenix.org/sites/default/files/waves_favicon.ico" type="image/vnd.microsoft.icon" />
+<meta name="generator" content="Drupal 7 (http://drupal.org)" />
+<link rel="canonical" href="https://www.usenix.org/" />
+<link rel="shortlink" href="https://www.usenix.org/" />
+<meta property="og:site_name" content="USENIX" />
+<meta property="og:type" content="website" />
+<meta property="og:url" content="https://www.usenix.org/" />
+<meta property="og:title" content="USENIX" />
+<!-- TODO: add the following meta tags to $head via theme_settings -->
+ <!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
+ Remove this if you use the .htaccess -->
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"><script type="text/javascript">window.NREUM||(NREUM={}),__nr_require=function(t,e,n){function r(n){if(!e[n]){var o=e[n]={exports:{}};t[n][0].call(o.exports,function(e){var o=t[n][1][e];return r(o||e)},o,o.exports)}return e[n].exports}if("function"==typeof __nr_require)return __nr_require;for(var o=0;o<n.length;o++)r(n[o]);return r}({QJf3ax:[function(t,e){function n(){}function r(t){function e(t){return t&&t instanceof n?t:t?a(t,i,o):o()}function c(n,r,o){t&&t(n,r,o);for(var i=e(o),a=f(n),c=a.length,u=0;c>u;u++)a[u].apply(i,r);return i}function u(t,e){p[t]=f(t).concat(e)}function f(t){return p[t]||[]}function s(){return r(c)}var p={};return{on:u,emit:c,create:s,listeners:f,context:e,_events:p}}function o(){return new n}var i="nr@context",a=t("gos");e.exports=r()},{gos:"7eSDFh"}],ee:[function(t,e){e.exports=t("QJf3ax")},{}],3:[function(t,e){function n(t){return function(){r(t,[(new Date).getTime()].concat(i(arguments)))}}var r=t("handle"),o=t(1),i=t(2);"undefined"==typeof window.newrelic&&(newrelic=window.NREUM);var a=["setPageViewName","addPageAction","setCustomAttribute","finished","addToTrace","inlineHit","noticeError"];o(a,function(t,e){window.NREUM[e]=n("api-"+e)}),e.exports=window.NREUM},{1:12,2:13,handle:"D5DuLP"}],gos:[function(t,e){e.exports=t("7eSDFh")},{}],"7eSDFh":[function(t,e){function n(t,e,n){if(r.call(t,e))return t[e];var o=n();if(Object.defineProperty&&Object.keys)try{return Object.defineProperty(t,e,{value:o,writable:!0,enumerable:!1}),o}catch(i){}return t[e]=o,o}var r=Object.prototype.hasOwnProperty;e.exports=n},{}],D5DuLP:[function(t,e){function n(t,e,n){return r.listeners(t).length?r.emit(t,e,n):void(r.q&&(r.q[t]||(r.q[t]=[]),r.q[t].push(e)))}var r=t("ee").create();e.exports=n,n.ee=r,r.q={}},{ee:"QJf3ax"}],handle:[function(t,e){e.exports=t("D5DuLP")},{}],XL7HBI:[function(t,e){function n(t){var e=typeof t;return!t||"object"!==e&&"function"!==e?-1:t===window?0:i(t,o,function(){return r++})}var r=1,o="nr@id",i=t("gos");e.exports=n},{gos:"7eSDFh"}],id:[function(t,e){e.exports=t("XL7HBI")},{}],G9z0Bl:[function(t,e){function n(){if(!v++){var t=l.info=NREUM.info,e=f.getElementsByTagName("script")[0];if(t&&t.licenseKey&&t.applicationID&&e){c(p,function(e,n){t[e]||(t[e]=n)});var n="https"===s.split(":")[0]||t.sslForHttp;l.proto=n?"https://":"http://",a("mark",["onload",i()]);var r=f.createElement("script");r.src=l.proto+t.agent,e.parentNode.insertBefore(r,e)}}}function r(){"complete"===f.readyState&&o()}function o(){a("mark",["domContent",i()])}function i(){return(new Date).getTime()}var a=t("handle"),c=t(1),u=window,f=u.document;t(2);var s=(""+location).split("?")[0],p={beacon:"bam.nr-data.net",errorBeacon:"bam.nr-data.net",agent:"js-agent.newrelic.com/nr-852.min.js"},d=window.XMLHttpRequest&&XMLHttpRequest.prototype&&XMLHttpRequest.prototype.addEventListener&&!/CriOS/.test(navigator.userAgent),l=e.exports={offset:i(),origin:s,features:{},xhrWrappable:d};f.addEventListener?(f.addEventListener("DOMContentLoaded",o,!1),u.addEventListener("load",n,!1)):(f.attachEvent("onreadystatechange",r),u.attachEvent("onload",n)),a("mark",["firstbyte",i()]);var v=0},{1:12,2:3,handle:"D5DuLP"}],loader:[function(t,e){e.exports=t("G9z0Bl")},{}],12:[function(t,e){function n(t,e){var n=[],o="",i=0;for(o in t)r.call(t,o)&&(n[i]=e(o,t[o]),i+=1);return n}var r=Object.prototype.hasOwnProperty;e.exports=n},{}],13:[function(t,e){function n(t,e,n){e||(e=0),"undefined"==typeof n&&(n=t?t.length:0);for(var r=-1,o=n-e||0,i=Array(0>o?0:o);++r<o;)i[r]=t[e+r];return i}e.exports=n},{}]},{},["G9z0Bl"]);</script><!-- Mobile viewport optimized: j.mp/bplateviewport -->
+ <meta name="google-site-verification" content="NWZh1b4m1muqzcGTUY41ERnLVVU1U0nU1knFu0v-Y5g" />
+ <!-- <meta name="viewport" content="width=device-width, initial-scale=1.0"> -->
+
+ <title>Page not found | USENIX</title><link type="text/css" rel="stylesheet" href="https://www.usenix.org/sites/default/files/css/css_xAloA06fO9MfqH5oB7olBlEMNSCK_bXmI744B3VBaGU.css" media="all" />
+<link type="text/css" rel="stylesheet" href="https://www.usenix.org/sites/default/files/css/css_qzuwDX4H89pFw_qLsggVqpH75q_-Dnp-jVEe2X1-gBo.css" media="all" />
+<link type="text/css" rel="stylesheet" href="https://www.usenix.org/sites/default/files/css/css_JOfTSKVRI4TclH2vFChq7MD8Qs8R6fJcf5wE3J2fXt4.css" media="all" />
+<link type="text/css" rel="stylesheet" href="https://www.usenix.org/sites/default/files/css/css_1Cns3L1_TrifKTSgp644ViF6O9TZ1O0lZsCwyMt9kfE.css" media="screen" />
+<link type="text/css" rel="stylesheet" href="https://www.usenix.org/sites/default/files/css/css_47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU.css" media="print" />
+
+<!--[if lte IE 8]>
+<link type="text/css" rel="stylesheet" href="https://www.usenix.org/sites/all/themes/waves/css/ie.css?o3sy5o" media="all" />
+<![endif]-->
+<!-- <link href='http://fonts.googleapis.com/css?family=Open+Sans:400italic,600italic,800italic,600,400,800' rel='stylesheet' type='text/css'> -->
+<!-- <link href='http://fonts.googleapis.com/css?family=Noto+Sans' rel='stylesheet' type='text/css'> -->
+ <link href="./css/ie.css" media="screen, projection" rel="stylesheet" type="text/css">
+ <script type="text/javascript" src="https://www.usenix.org/sites/default/files/js/js_UWQINlriydSoeSiGQxToOUdv493zEa7dpsXC1OtYlZU.js"></script>
+<script type="text/javascript" src="https://www.usenix.org/sites/default/files/js/js_IE1tR0MJwQVLroCSS5Sy4yftAmfwZ4RgT9sBLauhG2o.js"></script>
+<script type="text/javascript" src="https://www.usenix.org/sites/default/files/js/js_E1JP4TspEoKLVi3t3MsXgycF9wMUk6_jE9-daEZvInI.js"></script>
+<script type="text/javascript">
+<!--//--><![CDATA[//><!--
+(function(i,s,o,g,r,a,m){i["GoogleAnalyticsObject"]=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)})(window,document,"script","https://www.usenix.org/sites/default/files/googleanalytics/analytics.js?o3sy5o","ga");ga("create", "UA-3633391-4", {"cookieDomain":"auto"});ga("set", "anonymizeIp", true);ga("set", "page", "/404.html?page=" + document.location.pathname + document.location.search + "&from=" + document.referrer);ga("send", "pageview");
+//--><!]]>
+</script>
+<script type="text/javascript" src="https://www.usenix.org/sites/default/files/js/js_RGgtt0xyoog3aEbqA3PACaZnzIbopLWdKtKDjg0Ge54.js"></script>
+<script type="text/javascript" src="https://www.usenix.org/sites/default/files/js/js_ByQqoFmos53DlLDLfNaeXBXbxiumZmAoP08wXIegdFk.js"></script>
+<script type="text/javascript">
+<!--//--><![CDATA[//><!--
+jQuery.extend(Drupal.settings, {"basePath":"\/","pathPrefix":"","ajaxPageState":{"theme":"waves","theme_token":"qoy_Y_xwpw510ookWtQkmOoLXsXaOKbpBCRYZkn11TQ","js":{"misc\/jquery.js":1,"misc\/jquery.once.js":1,"misc\/drupal.js":1,"sites\/all\/modules\/beautytips\/js\/jquery.bt.min.js":1,"sites\/all\/modules\/beautytips\/js\/beautytips.min.js":1,"sites\/all\/themes\/waves\/js\/search.js":1,"sites\/all\/themes\/waves\/js\/modernizr-1.6.min.js":1,"sites\/all\/themes\/waves\/js\/usenix.js":1,"sites\/all\/modules\/views_slideshow\/js\/views_slideshow.js":1,"sites\/all\/modules\/google_analytics\/googleanalytics.js":1,"0":1,"sites\/all\/themes\/waves\/js\/ad-blocks-leaderboard.js":1,"sites\/all\/themes\/rubik\/js\/rubik.js":1},"css":{"modules\/comment\/comment.css":1,"sites\/all\/modules\/date\/date_api\/date.css":1,"sites\/all\/modules\/date\/date_popup\/themes\/datepicker.1.7.css":1,"modules\/field\/theme\/field.css":1,"sites\/all\/modules\/mollom\/mollom.css":1,"modules\/poll\/poll.css":1,"sites\/all\/modules\/views\/css\/views.css":1,"sites\/all\/modules\/ctools\/css\/ctools.css":1,"sites\/all\/modules\/views_slideshow\/views_slideshow.css":1,"sites\/all\/modules\/biblio\/biblio.css":1,"sites\/all\/modules\/views\/css\/views-admin.seven.css":1,"sites\/all\/themes\/tao\/reset.css":1,"sites\/all\/themes\/tao\/base.css":1,"sites\/all\/themes\/waves\/css\/screen.css":1,"sites\/all\/themes\/waves\/system.css":1,"sites\/all\/themes\/waves\/views.css":1,"sites\/all\/themes\/tao\/drupal.css":1,"sites\/all\/themes\/waves\/core.css":1,"sites\/all\/themes\/waves\/icons.css":1,"sites\/all\/themes\/waves\/style.css":1,"sites\/all\/themes\/waves\/print.css":1,"sites\/all\/themes\/waves\/base.css":1,"sites\/all\/themes\/waves\/reset.css":1,"sites\/all\/themes\/waves\/css\/ie.css":1}},"beautytips":{".beautytips":{"fill":"#F4F4F4","strokeStyle":"#666666","spikeLength":20,"spikeGirth":10,"width":350,"overlap":0,"centerPointY":1,"cornerRadius":0,"cssStyles":{"fontFamily":"\u0026quot;Lucida Grande\u0026quot;,Helvetica,Arial,Verdana,sans-serif","fontSize":"12px","padding":"10px 14px"},"shadow":1,"shadowColor":"rgba(0,0,0,.5)","shadowBlur":8,"shadowOffsetX":4,"shadowOffsetY":4,"cssSelect":".beautytips","list":["fill","strokeStyle","spikeLength","spikeGirth","width","overlap","centerPointY","cornerRadius","cssStyles","shadow","shadowColor","shadowBlur","shadowOffsetX","shadowOffsetY"]}},"jcarousel":{"ajaxPath":"\/jcarousel\/ajax\/views"},"googleanalytics":{"trackOutbound":1,"trackMailto":1,"trackDownload":1,"trackDownloadExtensions":"7z|aac|arc|arj|asf|asx|avi|bin|csv|doc|epub|exe|flv|gif|gz|gzip|hqx|jar|jpe?g|js|mobi|mp(2|3|4|e?g)|mov(ie)?|msi|msp|pdf|phps|png|ppt|qtm?|ra(m|r)?|sea|sit|tar|tgz|torrent|txt|wav|wma|wmv|wpd|xls|xml|z|zip"},"urlIsAjaxTrusted":{"\/legacy\/event\/fast08\/tech\/full_papers\/gunawi\/gunawi_html\/main.css":true}});
+//--><!]]>
+</script>
+<!--[if (gte IE 6)&(lte IE 8)]><script type="text/javascript" src="sites/all/themes/waves/js/mootools-core-1.4.1-full-nocompat-yc.js"></script><script type="text/javascript" src="sites/all/themes/waves/js/selectivizr/selectivizr-min.js"></script><![endif]--><!--[if lt IE 9]><script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
+ <script type="application/ld+json">
+ { "@context" : "http://schema.org",
+ "@type" : "Organization",
+ "name" : "USENIX Association",
+ "url" : "https://www.usenix.org",
+ "sameAs" : [ "https://www.facebook.com/usenixassociation",
+ "http://www.twitter.com/usenix",
+ "https://plus.google.com/108588319090208187909/posts",
+ "http://www.linkedin.com/groups/USENIX-Association-49559/about",
+ "http://www.youtube.com/user/USENIXAssociation"]
+ }
+ </script>
+
+</head>
+
+<body class="html not-front not-logged-in page-usenix-redirects-404 tao no-sidebars" >
+ <div id="skip-link">
+ <a href="#main-content" class="element-invisible element-focusable">Skip to main content</a>
+ </div>
+<div id='top-outer'><div id='top' class='limiter'>
+ <div id='top-inner'>
+ <div class="top-left">
+ <div id="site-name"><a href="/">USENIX</a></div>
+ </div>
+ <div class="top-right">
+ <div id='header-login'>
+ <span class="header-login-first"><a href="/user?destination=legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/main.css">Sign In</a></span><a href="/user/register?destination=legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/main.css">Create Account</a> </div>
+ <div id="search-bar">
+ <form action="/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/main.css" method="post" id="search-block-form" accept-charset="UTF-8"><div><div class="container-inline">
+ <h2 class="element-invisible">Search form</h2>
+ <div class="form-item form-type-textfield form-item-search-block-form">
+ <input title="Enter the terms you wish to search for." type="text" id="edit-search-block-form--2" name="search_block_form" value="" size="15" maxlength="128" class="form-text" />
+</div>
+<div class="form-actions form-wrapper" id="edit-actions"><input type="submit" id="edit-submit" name="op" value="Go" class="form-submit" /></div><input type="hidden" name="form_build_id" value="form-mZ98hnE_3pV2aRqTe8S1Wq6ELFtnMAT1DVSxMEeJxl8" />
+<input type="hidden" name="form_id" value="search_block_form" />
+</div>
+</div></form> </div>
+ </div>
+ <nav id="primary">
+ <div id='branding' class='limiter'>
+ <div id="main-menu" class="navigation">
+ <h2 class="element-invisible">Main menu</h2><ul id="main-menu-links" class="links clearfix"><li class="menu-2685 first"><a href="/" title="">Home</a></li>
+<li class="menu-2627"><a href="/about" title="About USENIX">About</a></li>
+<li class="menu-1121"><a href="/conferences" title="">Conferences</a></li>
+<li class="menu-22927"><a href="/publications" title="Publications from USENIX">Publications</a></li>
+<li class="menu-2737"><a href="/lisa" title="LISA">LISA SIG</a></li>
+<li class="menu-2885"><a href="/membership-services" title="Membership &amp; Services">Membership &amp; Services</a></li>
+<li class="menu-2881"><a href="/students" title="Student Programs">Students</a></li>
+<li class="menu-2844 last"><a href="/store" title="">Store</a></li>
+</ul> </div> <!-- /#main-menu -->
+ </div>
+ <nav id="social-menu">
+ <div id="facebook"><a href="https://www.facebook.com/pages/USENIX-Association/124487434386" target="_blank">Facebook</a></div> <div id="googleplus"><a href="https://plus.google.com/108588319090208187909" target="_blank">Google Plus</a></div> <div id="twitter"><a href="http://twitter.com/usenix" target="_blank">Twitter</a></div> <div id="linkedin"><a href="http://www.linkedin.com/groups?home=&gid=49559" target="_blank">LinkedIn</a></div> <div id="youtube"><a href="http://www.youtube.com/user/USENIXAssociation" target="_blank">YouTube</a></div> </nav>
+ <nav id="donate-button">
+ <div id="donatebutton"><a style="border-bottom: none;" href="https://co.clickandpledge.com/advanced/default.aspx?wid=73860" target="_blank"><img src="/sites/all/themes/waves/css/images/donate-button.png"></a></div>
+ </nav>
+ </nav>
+ </div>
+</div></div>
+
+
+
+<div id="container">
+ <h2 class="element-invisible">You are here</h2><div class="breadcrumb"><a href="/">Home</a> » <strong>Page not found</strong></div>
+ <div id='page' class='clearfix limiter page-content'>
+
+ <div id='content' class=
+ 'no-sidebars'
+ >
+
+ <h1 class='page-title '>
+ Page not found </h1>
+
+
+ <div id='tabs'>
+ </div>
+
+ <div class='content-wrapper clearfix'>
+ <div class="region region-content">
+
+<div class='block-system block-page-content clearfix' id="block-system-main">
+
+
+
+
+ <div class="block-content clearfix"><p>Sorry, the page that you have requested is not available on this server. Perhaps:</p> <ul> <li>The link you are looking for has moved. If you have arrived here from another page, please back up and send mail to the page owner.</li> <li>You are looking for a conference that has happened. See the <a href="/conferences/all">Conferences</a> page.</li> <li>The link you are looking for has been withdrawn. If this has happened, and we are not providing some information that you need, please send mail to <a href="mailto:webster@usenix.org">webster@usenix.org</a>.</li> <p>&nbsp;</p> </ul></div>
+
+ </div>
+
+ </div>
+ </div>
+
+ </div> <!-- /#content -->
+ </div>
+</div>
+
+ <div id="footer-outer"><div id='footer' class='limiter clearfix'>
+
+ <div id='footer-region'>
+ <div class="region region-footer">
+
+<div class='block block-block clearfix' id="block-block-27">
+
+
+
+
+ <div class="block-content clearfix prose"><div style="float: right;">
+<p style="text-align: center;">© USENIX 2016</p>
+</div></div>
+
+ </div>
+
+
+<div class='block block-menu clearfix' id="block-menu-menu-footer">
+
+
+
+
+ <div class="block-content clearfix"><ul class="menu"><li class="first leaf"><a href="/privacy-statement">Privacy Statement</a></li>
+<li class="leaf"><a href="/contact" title="">Contact Us</a></li>
+<li class="last leaf"><a href="/blog/rss.xml" title="">USENIX Update RSS Feed</a></li>
+</ul></div>
+
+ </div>
+
+ </div>
+ </div>
+ </div></div>
+
+
+<script type="text/javascript">window.NREUM||(NREUM={});NREUM.info={"beacon":"bam.nr-data.net","licenseKey":"d823139095","applicationID":"509444","transactionName":"YVJVZksCXkEEVhIMWFgYdlFNCl9cSkAVAFlfT2hAXAdZQABWEhZoWFhDbV8MRVwB","queueTime":0,"applicationTime":197,"ttGuid":"","agentToken":"","atts":"TRVWEAMYTU8=","errorBeacon":"bam.nr-data.net","agent":""}</script></body>
+</html>
diff --git a/reference/Error Handling is Ocassionally Correct_files/new_usenix.jpg b/reference/Error Handling is Ocassionally Correct_files/new_usenix.jpg
new file mode 100644
index 00000000..5815ff36
--- /dev/null
+++ b/reference/Error Handling is Ocassionally Correct_files/new_usenix.jpg
Binary files differ
diff --git a/reference/Files are hard.html b/reference/Files are hard.html
new file mode 100644
index 00000000..d8a91640
--- /dev/null
+++ b/reference/Files are hard.html
@@ -0,0 +1,484 @@
+<!DOCTYPE html>
+<!-- saved from url=(0035)http://danluu.com/file-consistency/ -->
+<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta charset="utf-8">
+ <title>Files are hard</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+<style>
+ img { max-width: 100%; height: auto; }
+ pre { max-width: 100%; height: auto; white-space: pre-wrap }
+ .link-left {
+ float: left;
+ padding-right: 1em;
+ padding-bottom: 1em;
+ }
+ .link-right {
+ float: right;
+ padding-bottom: 1em;
+ }
+ .navi-parent {
+ display: flex
+ }
+ .navi {
+ padding-bottom: 1em;
+ }
+ .navi-right {
+ margin-left: auto;
+ padding-bottom: 1em;
+ }
+ .navi div {
+ display: inline;
+ font-style: italic;
+ padding-left: 1em;
+ }
+ .navi-right div {
+ display: inline;
+ font-style: italic;
+ padding-right: 1em;
+ }
+}
+</style>
+ <meta name="description" content="">
+ <meta name="generator" content="Hugo 0.15">
+ </head>
+ <body>
+ <div id="wrapper">
+ <div class="container">
+
+
+ <div id="article">
+ <div class="article-title"><strong>Files are hard</strong></div>
+ <hr>
+ <div class="post">
+
+
+<p>I haven’t used a desktop email client in years. None of them could handle the volume of email I get without at least occasionally corrupting my mailbox. Pine, eudora, and outlook have all corrupted my inbox, forcing me to restore from backup. How is it that desktop mail clients are less reliable than gmail, even though my gmail account not only handles more email than I ever had on desktop clients, but also allows simultaneous access from multiple locations across the globe? Distributed systems have an unfair advantage, in that they can be robust against total disk failure in a way that desktop clients can’t, but none of the file corruption issues I’ve had have been from total disk failure. Why has my experience with desktop applications been so bad?</p>
+
+<p>Well, what sort of failures can occur? Crash consistency (maintaining consistent state even if there’s a crash) is probably the easiest property to consider, since we can assume that everything, from the filesystem to the disk, works correctly; let’s consider that first.</p>
+
+<h3 id="crash-consistency">Crash Consistency</h3>
+
+<p>Pillai et al. had a <a href="https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-pillai.pdf">paper</a> and <a href="https://www.usenix.org/sites/default/files/conference/protected-files/osdi14_slides_pillai.pdf">presentation</a> at OSDI ‘14 on exactly how hard it is to save data without corruption or data loss.</p>
+
+<p>Let’s look at a simple example of what it takes to save data in a way that’s robust against a crash. Say we have a file that contains the text <code>a foo</code> and we want to update the file to contain <code>a bar</code>. The pwrite function looks like it’s designed for this exact thing. It takes a file descriptor, what we want to write, a length, and an offset. So we might try</p>
+
+<pre><code>pwrite([file], “bar”, 3, 2) // write 3 bytes at offset 2
+</code></pre>
+
+<p>What happens? If nothing goes wrong, the file will contain <code>a bar</code>, but if there’s a crash during the write, we could get <code>a boo</code>, <code>a far</code>, or any other combination. Note that you may want to consider this an example over sectors or blocks and not chars/bytes.</p>
+
+<p>If we want atomicity (so we either end up with <code>a foo</code> or <code>a bar</code> but nothing in between) one standard technique is to make a copy of the data we’re about to change in an <a href="http://www.cburch.com/cs/340/reading/log/index.html">undo log</a> file, modify the “real” file, and then delete the log file. If a crash happens, we can recover from the log. We might write something like</p>
+
+<pre><code>creat(/dir/log);
+write(/dir/log, “2,3,foo”, 7);
+pwrite(/dir/orig, “bar”, 3, 2);
+unlink(/dir/log);
+</code></pre>
+
+<p>This should allow recovery from a crash without data corruption via the undo log, at least if we’re using <code>ext3</code> and we made sure to mount our drive with <code>data=journal</code>. But we’re out of luck if, like most people, we’re using the default<sup class="footnote-ref" id="fnref:D"><a rel="footnote" href="http://danluu.com/file-consistency/#fn:D">1</a></sup> – with the default <code>data=ordered</code>, the <code>write</code> and <code>pwrite</code> syscalls can be reordered, causing the write to <code>orig</code> to happen before the write to the log, which defeats the purpose of having a log. We can fix that.</p>
+
+<pre><code>creat(/dir/log);
+write(/dir/log, “2, 3, foo”);
+fsync(/dir/log); // don’t allow write to be reordered past pwrite
+pwrite(/dir/orig, 2, “bar”);
+fsync(/dir/orig);
+unlink(/dir/log);
+</code></pre>
+
+<p>That should force things to occur in the correct order, at least if we’re using ext3 with <code>data=journal</code> or <code>data=ordered</code>. If we’re using <code>data=writeback</code>, a crash during the the <code>write</code> or <code>fsync</code> to log can leave <code>log</code> in a state where the filesize has been adjusted for the write of “bar”, but the data hasn’t been written, which means that the log will contain random garbage. This is because with <code>data=writeback</code>, metadata is <a href="https://en.wikipedia.org/wiki/Journaling_file_system">journaled</a>, but data operations aren’t, which means that data operations (like writing data to a file) aren’t ordered with respect to metadata operations (like adjusting the size of a file for a write).</p>
+
+<p>We can fix that by adding a checksum to the log file when creating it. If the contents of <code>log</code> don’t contain a valid checksum, then we’ll know that we ran into the situation described above.</p>
+
+<pre><code>creat(/dir/log);
+write(/dir/log, “2, 3, [checksum], foo”); // add checksum to log file
+fsync(/dir/log);
+pwrite(/dir/orig, 2, “bar”);
+fsync(/dir/orig);
+unlink(/dir/log);
+</code></pre>
+
+<p>That’s safe, at least on current configurations of ext3. But it’s legal for a filesystem to end up in a state where the log is never created unless we issue an fsync to the parent directory.</p>
+
+<pre><code>creat(/dir/log);
+write(/dir/log, “2, 3, [checksum], foo”);
+fsync(/dir/log);
+fsync(/dir); // fsync parent directory of log file
+pwrite(/dir/orig, 2, “bar”);
+fsync(/dir/orig);
+unlink(/dir/log);
+</code></pre>
+
+<p>That should prevent corruption on any Linux filesystem, but if we want to make sure that the file actually contains “bar”, we need another fsync at the end.</p>
+
+<pre><code>creat(/dir/log);
+write(/dir/log, “2, 3, [checksum], foo”);
+fsync(/dir/log);
+fsync(/dir);
+pwrite(/dir/orig, 2, “bar”);
+fsync(/dir/orig);
+unlink(/dir/log);
+fsync(/dir);
+</code></pre>
+
+<p>That results in consistent behavior and guarantees that our operation actually modifies the file after it’s completed, as long as we assume that <code>fsync</code> actually flushes to disk. OS X and some versions of ext3 have an fsync that doesn’t really flush to disk. OS X requires <code>fcntl(F_FULLFSYNC)</code> to flush to disk, and some versions of ext3 only flush to disk if the the <a href="https://en.wikipedia.org/wiki/Inode">inode</a> changed (which would only happen at most once a second on writes to the same file, since the inode mtime has one second granularity), as an optimization.</p>
+
+<p>Even if we assume fsync issues a flush command to the disk, some disks ignore flush directives for the same reason fsync is gimped on OS X and some versions of ext3 – to look better in benchmarks. Handling that is beyond the scope of this post, but the <a href="http://www.researchgate.net/profile/Vijay_Chidambaram/publication/220958003_Coerced_Cache_Eviction_and_discreet_mode_journaling_Dealing_with_misbehaving_disks/links/54d0f0190cf29ca811040c8a.pdf">Rajimwale et al. DSN ‘11 paper</a> and related work cover that issue.</p>
+
+<h3 id="filesystem-semantics">Filesystem semantics</h3>
+
+<p>When the authors examined ext2, ext3, ext4, btrfs, and xfs, they found that there are substantial differences in how code has to be written to preserve consistency. They wrote a tool that collects block-level filesystem traces, and used that to determine which properties don’t hold for specific filesystems. The authors are careful to note that they can only determine when properties don’t hold – if they don’t find a violation of a property, that’s not a guarantee that the property holds.</p>
+
+<p><img src="./Files are hard_files/fs_properties.png" alt="Different filesystems have very different properties"></p>
+
+<p>Xs indicate that a property is violated. The atomicity properties are basically what you’d expect, e.g., no X for single sector overwrite means that writing a single sector is atomic. The authors note that the atomicity of single sector overwrite sometimes comes from a property of the disks they’re using, and that running these filesystems on some disks won’t give you single sector atomicity. The ordering properties are also pretty much what you’d expect from their names, e.g., an X in the “Overwrite -&gt; Any op” row means that an overwrite can be reordered with some operation.</p>
+
+<p>After they created a tool to test filesystem properties, they then created a tool to check if any applications rely on any potentially incorrect filesystem properties. Because invariants are application specific, the authors wrote checkers for each application tested.</p>
+
+<p><img src="./Files are hard_files/program_bugs.png" alt="Everything is broken"></p>
+
+<p>The authors find issues with most of the applications tested, including things you’d really hope would work, like LevelDB, HDFS, Zookeeper, and git. In a talk, one of the authors noted that the developers of sqlite have a very deep understanding of these issues, but even that wasn’t enough to prevent all bugs. That speaker also noted that version control systems were particularly bad about this, and that the developers had a pretty lax attitude that made it very easy for the authors to find a lot of issues in their tools.
+The most common class of error was incorrectly assuming ordering between syscalls. The next most common class of error was assuming that syscalls were atomic<sup class="footnote-ref" id="fnref:A"><a rel="footnote" href="http://danluu.com/file-consistency/#fn:A">2</a></sup>. These are fundamentally the same issues people run into when doing multithreaded programming. Correctly reasoning about re-ordering behavior and inserting barriers correctly is hard. But even though shared memory concurrency is considered a hard problem that requires great care, writing to files isn’t treated the same way, even though it’s actually harder in a number of ways.</p>
+
+<p>Something to note here is that while btrfs’s semantics aren’t inherently less relaible than ext3/ext4, many more applications corrupt data on top of btrfs because developers aren’t used to coding against filesystems that allow directory operations to be reordered (ext2 is perhaps the most recent widely used filesystem that allowed that reordering). We’ll probably see a similar level of bug exposure when people start using NVRAM drives that have byte-level atomicity. People almost always just run some tests to see if things work, rather than making sure they’re coding against what’s legal in a POSIX filesystem.</p>
+
+<p>Hardware memory ordering semantics are usually <a href="http://danluu.com/new-cpu-features/#memory-concurrency">well documented</a> in a way that makes it simple to determine precisely which operations can be reordered with which other operations, and which operations are atomic. By contrast, here’s <a href="http://man7.org/linux/man-pages/man5/ext4.5.html">the ext manpage</a> on its three data modes:</p>
+
+<blockquote>
+<p>journal: All data is committed into the journal prior to being written into the main filesystem.
+ordered: This is the default mode. All data is forced directly out to the main file system prior to its metadata being committed to the journal.
+writeback: Data ordering is not preserved – data may be written into the main filesystem after its metadata has been committed to the journal. <strong>This is rumoured to be</strong> the highest-throughput option. It guarantees internal filesystem integrity, however it can allow old data to appear in files after a crash and journal recovery.</p>
+</blockquote>
+
+<p>The manpage literally refers to rumor. This is the level of documentation we have. If we look back at our example where we had to add an <code>fsync</code> between the <code>write(/dir/log, “2, 3, foo”)</code> and <code>pwrite(/dir/orig, 2, “bar”)</code> to prevent reordering, I don’t think the necessity of the <code>fsync</code> is obvious from the description in the manpage. If you look at the hardware memory ordering “manpage” above, it specifically defines the ordering semantics, and it certainly doesn’t rely on rumor.</p>
+
+<p>This isn’t to say that filesystem semantics aren’t documented anywhere. Between <a href="http://lwn.net/">lwn</a> and LKML, it’s possible to get a good picture of how things work. But digging through all of that is hard enough that it’s still quite common <a href="http://austingroupbugs.net/view.php?id=672">for there to be long, uncertain discussions on how things work</a>. A lot of the information out there is wrong, and even when information was right at the time it was posted, it often goes out of date.</p>
+
+<p>When digging through archives, I’ve often seen a post from 2005 cited to back up the claim that OS X <code>fsync</code> is the same as Linux <code>fsync</code>, and that OS X <code>fcntl(F_FULLFSYNC)</code> is even safer than anything available on Linux. Even at the time, I don’t think that was true for the 2.4 kernel, although it was true for the 2.6 kernel. But since 2008 or so Linux 2.6 with ext3 will do a full flush to disk for each fsync (if the disk supports it, and the filesystem hasn’t been specially configured with barriers off).</p>
+
+<p>Another issue is that you often also see exchanges <a href="http://lkml.iu.edu/hypermail/linux/kernel/0908.3/01481.html">like this one</a>:</p>
+
+<p><strong>Dev 1</strong>: Personally, I care about metadata consistency, and ext3 documentation suggests that journal protects its integrity. Except that it does not on broken storage devices, and you stil need to run fsck there.<br>
+<strong>Dev 2</strong>: as the ext3 authors have stated many times over the years, you still need to run fsck periodicly anyway.<br>
+<strong>Dev 1</strong>: Where is that documented?<br>
+<strong>Dev 2</strong>: linux-kernel mailing list archives.<br>
+<strong>Dev 3</strong>: Probably from some 6-8 years ago, in e-mail postings that I made.<br></p>
+
+<p>Where’s this documented? Oh, in some mailing list post 6-8 years ago (which makes it 12-14 years from today). I don’t mean to pick on filesystem devs. The fs devs whose posts I’ve read are quite polite compared to LKML’s reputation; they generously spend a lot of their time responding to basic questions and I’m impressed by how patient the expert fs devs are with askers, but it’s hard for outsiders to troll through a decade and a half of mailing list postings to figure out which ones are still valid and which ones have been obsoleted!</p>
+
+<p>In their OSDI 2014 talk, the authors of the paper we’re discussing noted that when they reported bugs they’d found, developers would often respond “POSIX doesn’t let filesystems do that”, without being able to point to any specific POSIX documentation to support their statement. If you’ve followed Kyle Kingsbury’s Jepsen work, this may sound familiar, except devs respond with “filesytems don’t do that” instead of “networks don’t do that”.I think this is understandable, given how much misinformation is out there. Not being a filesystem dev myself, I’d be a bit surprised if I don’t have at least one bug in this post.</p>
+
+<h3 id="filesystem-correctness">Filesystem correctness</h3>
+
+<p>We’ve already encountered a lot of complexity in saving data correctly, and this only scratches the surface of what’s involved. So far, we’ve assumed that the disk works properly, or at least that the filesystem is able to detect when the disk has an error via <a href="https://en.wikipedia.org/wiki/S.M.A.R.T.">SMART</a> or some other kind of monitoring. I’d always figured that was the case until I started looking into it, but that assumption turns out to be completely wrong.</p>
+
+<p>The <a href="http://research.cs.wisc.edu/wind/Publications/iron-sosp05.pdf">Prabhakaran et al. SOSP 05 paper</a> examined how filesystems respond to disk errors in some detail. They created a fault injection layer that allowed them to inject disk faults and then ran things like <code>chdir</code>, <code>chroot</code>, <code>stat</code>, <code>open</code>, <code>write</code>, etc. to see what would happen.</p>
+
+<p>Between ext3, reiserfs, and NTFS, reiserfs is the best at handling errors and it seems to be the only filesystem where errors were treated as first class citizens during design. It’s mostly consistent about propagating errors to the user on reads, and calling <code>panic</code> on write failures, which triggers a restart and recovery. This general policy allows the filesystem to gracefully handle read failure and avoid data corruption on write failures. However, the authors found a number of inconsistencies and bugs. For example, reiserfs doesn’t correctly handle read errors on indirect blocks and leaks space, and a specific type of write failure doesn’t prevent reiserfs from updating the journal and committing the transaction, which can result in data corruption.</p>
+
+<p>Reiserfs is the good case. The authors found that ext3 ignored write failures in most cases, and rendered the filesystem read-only in most cases for read failures. This seems like pretty much the opposite of the policy you’d want. Ignoring write failures can easily result in data corruption, and remounting the filesystem as read-only is a drastic overreaction if the read error was a transient error (transient errors are common). Additionally, ext3 did the least consistency checking of the three filesystems and was the most likely to not detect an error. In one presentation, one of the authors remarked that the ext3 code had lots of comments like “I really hope a write error doesn’t happen here” in places where errors weren’t handled.</p>
+
+<p>NTFS is somewhere in between. The authors found that it has many consistency checks built in, and is pretty good about propagating errors to the user. However, like ext3, it ignores write failures.</p>
+
+<p>The paper has much more detail on the exact failure modes, but the details are mostly of historical interest as many of the bugs have been fixed.</p>
+
+<p>It would be really great to see an updated version of the paper, and in one presentation someone in the audience asked if there was more up to date information. The presenter replied that they’d be interested in knowing what things look like now, but that it’s hard to do that kind of work in academia because grad students don’t want to repeat work that’s been done before, which is pretty reasonable given the incentives they face. Doing replications is a lot of work, often nearly as much work as the original paper, and replications usually give little to no academic credit. This is one of the many cases where the incentives align very poorly with producing real world impact.</p>
+
+<p>The <a href="http://usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html">Gunawi et al. FAST 08</a> is another paper it would be great to see replicated today. That paper follows up the paper we just looked at, and examines the error handling code in different file systems, using a simple static analysis tool to find cases where errors are being thrown away. Being thrown away is defined very loosely in the paper — code like the following</p>
+
+<pre><code>if (error) {
+ printk(“I have no idea how to handle this error\n”);
+}
+</code></pre>
+
+<p>is considered <em>not</em> throwing away the error. Errors are considered to be ignored if the execution flow of the program doesn’t depend on the error code returned from a function that returns an error code.</p>
+
+<p>With that tool, they find that most filesystems drop a lot of error codes:</p>
+
+<p></p><p></p>
+
+<p><br></p><div align="CENTER"><p></p>
+
+<p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><p></p><table cellpadding="3" border="1" align="CENTER">
+
+<tbody><tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1"></font></p></td>
+
+<td align="CENTER" colspan="2"><font size="-1"> </font><font size="-1"><b>By % Broken</b></font></td>
+
+<td align="CENTER" colspan="2"><font size="-1"> </font><font size="-1"><b>By Viol/Kloc</b></font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">Rank </font></p></td>
+
+<td align="LEFT"><font size="-1"> FS </font></td>
+
+<td align="RIGHT"><font size="-1"> Frac. </font></td>
+
+<td align="LEFT" colspan="2"><font size="-1"> FS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Viol/Kloc</font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">1 </font></p></td>
+
+<td align="LEFT"><font size="-1"> IBM JFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 24.4 </font></td>
+
+<td align="LEFT"><font size="-1"> ext3 </font></td>
+
+<td align="RIGHT"><font size="-1"> 7.2 </font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">2 </font></p></td>
+
+<td align="LEFT"><font size="-1"> ext3 </font></td>
+
+<td align="RIGHT"><font size="-1"> 22.1 </font></td>
+
+<td align="LEFT"><font size="-1"> IBM JFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 5.6 </font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">3 </font></p></td>
+
+<td align="LEFT"><font size="-1"> JFFS v2 </font></td>
+
+<td align="RIGHT"><font size="-1"> 15.7 </font></td>
+
+<td align="LEFT"><font size="-1"> NFS Client </font></td>
+
+<td align="RIGHT"><font size="-1"> 3.6 </font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">4 </font></p></td>
+
+<td align="LEFT"><font size="-1"> NFS Client </font></td>
+
+<td align="RIGHT"><font size="-1"> 12.9 </font></td>
+
+<td align="LEFT"><font size="-1"> VFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 2.9 </font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">5 </font></p></td>
+
+<td align="LEFT"><font size="-1"> CIFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 12.7 </font></td>
+
+<td align="LEFT"><font size="-1"> JFFS v2 </font></td>
+
+<td align="RIGHT"><font size="-1"> 2.2 </font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">6 </font></p></td>
+
+<td align="LEFT"><font size="-1"> MemMgmt </font></td>
+
+<td align="RIGHT"><font size="-1"> 11.4 </font></td>
+
+<td align="LEFT"><font size="-1"> CIFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 2.1 </font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">7 </font></p></td>
+
+<td align="LEFT"><font size="-1"> ReiserFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 10.5 </font></td>
+
+<td align="LEFT"><font size="-1"> MemMgmt </font></td>
+
+<td align="RIGHT"><font size="-1"> 2.0 </font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">8 </font></p></td>
+
+<td align="LEFT"><font size="-1"> VFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 8.4 </font></td>
+
+<td align="LEFT"><font size="-1"> ReiserFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 1.8 </font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">9 </font></p></td>
+
+<td align="LEFT"><font size="-1"> NTFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 8.1 </font></td>
+
+<td align="LEFT"><font size="-1"> XFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 1.4 </font></td>
+
+</tr>
+
+<tr><td align="CENTER"><font size="-1"><p></p>
+
+</font><p><font size="-1">10 </font></p></td>
+
+<td align="LEFT"><font size="-1"> XFS </font></td>
+
+<td align="RIGHT"><font size="-1"> 6.9 </font></td>
+
+<td align="LEFT"><font size="-1"> NFS Server </font></td>
+
+<td align="RIGHT"><font size="-1"> 1.2 </font></td>
+
+</tr>
+
+</tbody></table><p></p>
+
+<p></p></div><p></p>
+
+<p><br></p>
+
+<p><a name="table-analysis-robust"></a></p>
+
+<p>Comments they found next to ignored errors include: “Should we pass any errors back?”, “Error, skip block and hope for the best.”, “There’s no way of reporting error returned from ext3_mark_inode_dirty() to userspace. So ignore it.“, “Note: todo: log error handler.“, “We can’t do anything about an error here.”, “Just ignore errors at this point. There is nothing we can do except to try to keep going.”, “Retval ignored?”, and “Todo: handle failure.”</p>
+
+<p>One thing to note is that in a lot of cases, ignoring an error is more of a symptom of an architectural issue than a bug per se (e.g., ext3 ignored write errors during checkpointing because it didn’t have any kind of recovery mechanism). But even so, the authors of the papers found many real bugs.</p>
+
+<h3 id="error-recovery">Error recovery</h3>
+
+<p>Every widely used filesystem has bugs that will cause problems on error conditions, which brings up two questions. Can recovery tools robustly fix errors, and how often do errors occur? How do they handle recovery from those problems? The <a href="http://usenix.org/legacy/events/osdi08/tech/full_papers/gunawi/gunawi_html/index.html">Gunawi et al. OSDI 08 paper</a> looks at that and finds that fsck, a standard utility for checking and repairing file systems, “checks and repairs certain pointers in an incorrect order … the file system can even be unmountable after”.</p>
+
+<p>At this point, we know that it’s quite hard to write files in a way that ensures their robustness even when the underlying filesystem is correct, the underlying filesystem will have bugs, and that attempting to repair corruption to the filesystem may damage it further or destroy it. How often do errors happen?</p>
+
+<h3 id="error-frequency">Error frequency</h3>
+
+<p>The <a href="http://bnrg.eecs.berkeley.edu/~randy/Courses/CS294.F07/11.1.pdf">Bairavasundaram et al. SIGMETRICS ‘07 paper</a> found that, depending on the exact model of disk, between 5% and 20% of would have at least one error over a two year period. Interestingly, many of these were isolated errors – 38% of disks with errors had only a single error, and 80% had fewer than 50 errors. <a href="https://www.usenix.org/legacy/events/fast08/tech/full_papers/bairavasundaram/bairavasundaram_html/main.html">A follow-up study</a> looked at corruption and found that silent data corruption that was only detected by checksumming happened on .5% of disks per year, with one extremely bad model showing corruption on 4% of disks in a year.</p>
+
+<p>It’s also worth noting that they found very high locality in error rates between disks on some models of disk. For example, there was one model of disk that had a very high error rate in one specific sector, making many forms of RAID nearly useless for redundancy.</p>
+
+<p>That’s another study it would be nice to see replicated. <a href="https://www.backblaze.com/blog/hard-drive-reliability-q3-2015/">Most studies on disk focus on the failure rate of the entire disk</a>, but if what you’re woried about is data corruption, errors in non-failed disks are more worrying than disk failure, which is easy to detect and mitigate.</p>
+
+<h3 id="conclusion">Conclusion</h3>
+
+<p>Files are hard. <a href="http://danluu.com/butler-lampson-1999/#parallelism">Butler Lampson has remarked</a> that when they came up with threads, locks, and condition variables at PARC, they thought that they were creating a programming model that anyone could use, but that there’s now decades of evidence that they were wrong. We’ve accumulated a lot of evidence that humans are very bad at reasoning at these kinds of problems, which are very similar to the problems you have when writing correct code to interact with current filesystems. Lampson suggests that the best known general purpose solution is to package up all of your parallelism into as small a box as possible and then have a wizard write the code in the box. Translated to filesystems, that’s equivalent to saying that as an application developer, writing to files safely is hard enough that it should be done via some kind of library and/or database, not by directly making syscalls.</p>
+
+<p>Sqlite is quite good in terms of reliability if you want a good default. However, some people find it to be too heavyweight if all they want is a file-based abstraction. What they really want is a sort of polyfill for the file abstraction that works on top of all filesystems without having to understand the differences between different configurations (and even different versions) of each filesystem. Since that doesn’t exist yet, when no existing library is sufficient, you need to checksum your data since you will get silent errors and corruption. The only questions are whether or not you detect the errors and whether or not your record format only destroys a single record when corruption happens, or if it destroys the entire database. As far as I can tell, most desktop email client developers have chosen to go the route of destroying all of your email if corruption happens.</p>
+
+<p>These studies also hammer home the point that <a href="http://danluu.com/everything-is-broken/">conventional testing isn’t sufficient</a>. There were multiple cases where the authors of a paper wrote a relatively simple tool and found a huge number of bugs. You don’t need any deep computer science magic to write the tools. The error propagation checker from the paper that found a ton of bugs in filesystem error handling was 4k LOC. If you read the paper, you’ll see that the authors observed that the tool had a very large number of shortcomings because of its simplicity, but despite those shortcomings, it was able to find a lot of real bugs. I wrote a vaguely similar tool at my last job to enforce some invariants, and it was literally two pages of code. It didn’t even have a real parser (it just went line-by-line through files and did some regexp matching to detect the simple errors that it’s possible to detect with just a state machine and regexes), but it found enough bugs that it paid for itself in development time the first time I ran it.</p>
+
+<p>Almost every software project I’ve seen has a lot of low hanging testing fruit. Really basic <a href="http://danluu.com/testing/">random testing</a>, <a href="http://danluu.com/pl-troll/">static analysis</a>, and <a href="https://aphyr.com/tags/jepsen">fault injection</a> can pay for themselves in terms of dev time pretty much the first time you use them.</p>
+
+<h3 id="appendix">Appendix</h3>
+
+<p>I’ve probably covered less than 20% of the material in the papers I’ve referred to here. Here’s a bit of info about some other neat info you can find in those papers, and others.</p>
+
+<p><a href="https://www.usenix.org/system/files/conference/osdi14/osdi14-paper-pillai.pdf">Pillai et al., OSDI ‘14</a>: this papers goes into much more detail about what’s required for crash consistency than this post does. It also gives a fair amount of detail about how exactly applications fail, including diagrams of traces that indicate what false assumptions are embedded in each trace.</p>
+
+<p><a href="http://static.usenix.org/legacy/events/fast12/tech/full_papers/Chidambaram.pdf">Chidambara et al., FAST ‘12</a>: the same filesystem primitives are responsible for both consistency and ordering. The authors propose alternative primitives that seperate these concerns, allow better performance while maintaining safety.</p>
+
+<p><a href="https://www.researchgate.net/publication/220958003_Coerced_Cache_Eviction_and_Discreet_Mode_Journaling_Dealing_with_Misbehaving_Disks">Rajimwale et al. DSN ‘01</a>: you probably shouldn’t use disks that ignore flush directives, but in case you do, here’s a protocol that forces those disks to flush using normal filesystem operations. As you might expect, the performance for this is quite bad.</p>
+
+<p><a href="http://research.cs.wisc.edu/wind/Publications/iron-sosp05.pdf">Prabhakaran et al. SOSP ‘05</a>: This has a lot more detail on filesystem responses to error than was covered in this post. The authors also discuss JFS, an IBM filesystem for AIX. Although it was designed for high reliability systems, it isn’t particularly more reliable than the alternatives. Related material is covered furtehr in <a href="http://research.cs.wisc.edu/adsl/Publications/pointer-dsn08.pdf">DSN ‘08</a>, <a href="http://research.cs.wisc.edu/adsl/Publications/trust-storagess06.pdf">StorageSS ‘06</a>, <a href="http://research.cs.wisc.edu/adsl/Publications/vmdep-dsn06.pdf">DSN ‘06</a>, <a href="http://research.cs.wisc.edu/adsl/Publications/parity-fast08.pdf">FAST ‘08</a>, and <a href="http://research.cs.wisc.edu/adsl/Publications/envyfs-usenix09.pdf">USENIX ‘09</a>, among others.</p>
+
+<p><a href="http://usenix.org/legacy/event/fast08/tech/full_papers/gunawi/gunawi_html/index.html">Gunawi et al. FAST ‘08</a> : Again, much more detail than is covered in this post on when errors get dropped, and how they wrote their tools. They also have some call graphs that give you one rough measure of the complexity involved in a filesystem. The XFS call graph is particularly messy, and one of the authors noted in a presentation that an XFS developer said that XFS was fun to work on since they took advantage of every possible optimization opportunity regardless of how messy it made things.</p>
+
+<p><a href="http://bnrg.eecs.berkeley.edu/~randy/Courses/CS294.F07/11.1.pdf">Bairavasundaram et al. SIGMETRICS ‘07</a>: There’s a lot of information on disk error locality and disk error probability over time that isn’t covered in this post. <a href="http://research.cs.wisc.edu/adsl/Publications/corruption-fast08.pdf">A followup paper in FAST08 has more details</a>.</p>
+
+<p><a href="http://usenix.org/legacy/events/osdi08/tech/full_papers/gunawi/gunawi_html/index.html">Gunawi et al. OSDI ‘08</a>: This paper has a lot more detail about when fsck doesn’t work. In a presentation, one of the authors mentioned that fsck is the only program that’s ever insulted him. Apparently, if you have a corrupt pointer that points to a superblock, fsck destroys the superblock (possibly rendering the disk unmountable), tells you something like “you dummy, you must have run fsck on a mounted disk”, and then gives up. In the paper, the authors reimplement basically all of fsck using a declarative model, and find that the declarive version is shorter, easier to understand, and much easier to extend, at the cost of being somewhat slower.</p>
+
+<p>Memory errors are beyond the scope of this post, but <a href="http://danluu.com/why-ecc/">memory corruption</a> can cause disk corruption. This is especially annoying because memory corruption can cause you to take a checksum of bad data and write a bad checksum. It’s also possible to corrupt in memory pointers, which often results in something very bad happening. See the <a href="http://research.cs.wisc.edu/adsl/Publications/zfs-corruption-fast10.pdf">Zhang et al. FAST ‘10 paper</a> for more on how ZFS is affected by that. There’s a meme going around that ZFS is safe against memory corruption because it checksums, but that paper found that critical things held in memory aren’t checksummed, and that memory errors can cause data corruption in real scenarios.</p>
+
+<p>The sqlite devs are serious about both <a href="https://www.sqlite.org/howtocorrupt.html">documentation</a> and <a href="https://www.sqlite.org/testing.html">testing</a>. If I wanted to write a reliable desktop application, I’d start by reading the sqlite docs and then talking to some of the core devs. If I wanted to write a reliable distributed application I’d start by getting a job at Google and then reading the design docs and <a href="http://danluu.com/postmortem-lessons/">postmortems</a> for GFS, Colossus, Spanner, etc. J/k, but not really.</p>
+
+<p>We haven’t looked at formal methods at all, but there have been a variety of attempts to formally verify properties of filesystems, such as <a href="https://sibylfs.github.io/">SibylFS</a>.</p>
+
+<p>This list isn’t intended to be exhaustive. It’s just a list of things I’ve read that I think are interesting.</p>
+
+<p><em>Update: many people have read this post and suggested that, in the first file example, you should use the much simpler protocol of copying the file to modified to a temp file, modifying the temp file, and then renaming the temp file to overwrite the original file. In fact, that’s probably the most common comment I’ve gotten on this post. If you think this solves the problem, I’m going to ask you to pause for five seconds and consider the problems this might have. First, you still need to fsync in multiple places. Second, you will get very poor performance with large files. People have also suggested using many small files to work around that problem, but that will also give you very poor performance unless you do something fairly exotic. Third, if there’s a hardlink, you’ve now made the problem of crash consistency much more complicated than in the original example. Fourth, you’ll lose file metadata, sometimes in ways that can’t be fixed up after the fact. That problem can, on some filesystems, be worked around with ioctls, but that only sometimes fixes the issue and now you’ve got fs specific code to preserve correctness even in the non-crash case. And that’s just the beginning. The fact that so many people thought that this was a simple solution to the problem demonstrates that this problem is one that people are prone to underestimating, even they’re explicitly warned that people tend to underestimate this problem!</em></p>
+
+<p><strong>If you liked this, you’ll probably enjoy <a href="http://danluu.com/cpu-bugs/">this post on cpu bugs</a>.</strong></p>
+
+<p><small>
+Thanks to Leah Hanson, Katerina Barone-Adesi, Jamie Brandon, Kamal Marhubi, David Turner, Benjamin Gilbert, Tom Murphy, Chris Ball, Joe Doliner, Alexy Romanov, Mindy Preston, Paul McJones, and Evan Jones for comments/discussion.
+</small></p>
+<div class="footnotes">
+
+<hr>
+
+<ol>
+<li id="fn:D">Turns out <a href="https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Storage_Administration_Guide/ch-ext3.html">some commercially supported distros</a> only support <code>data=ordered</code>. Oh, and when I said <code>data=ordered</code> was the default, that’s only the case if pre-2.6.30. After 2.6.30, there’s a config option, <code>CONFIG_EXT3_DEFAULTS_TO_ORDERED</code>. If that’s not set, the default becomes <code>data=writeback</code>.
+ <a class="footnote-return" href="http://danluu.com/file-consistency/#fnref:D"><sup>[return]</sup></a></li>
+
+<li id="fn:A"><p>Cases where overwrite atomicity is required were documented as known issues, and all such cases assumed single-block atomicity and not multi-block atomicity. By contrast, multiple applications (LevelDB, Mercurial, and HSQLDB) had bad data corruption bugs that came from assuming appends are atomic.</p>
+
+<p>That seems to be an indirect result of a commonly used update protocol, where modifications are logged via appends, and then logged data is written via overwrites. Application developers are careful to check for and handle errors in the actual data, but the errors in the log file are often overlooked.</p>
+
+<p>There are a number of other classes of errors discussed, and I recommend reading the paper for the details if you work on an application that writes files.</p>
+ <a class="footnote-return" href="http://danluu.com/file-consistency/#fnref:A"><sup>[return]</sup></a></li>
+</ol>
+</div>
+
+ </div>
+ </div>
+
+<div class="navi-parent">
+ <div class="navi"><div><a href="http://danluu.com/startup-tradeoffs/">← Big company vs. startup work and pay</a></div></div>
+ <div class="navi-right"><div><a href="http://danluu.com/why-ecc/">Should I buy ECC memory? →</a></div></div>
+ </div>
+ </div>
+ <footer>
+<div class="navi-parent">
+ <div class="navi">
+ <div><a href="http://danluu.com/">Archive</a></div>
+ <div><a href="http://danluu.com/blog/archives/popularity">Popular</a></div>
+ <div><a href="http://danluu.com/about">About (hire me!)</a></div>
+ </div>
+<div class="navi-right">
+ <div><a href="https://twitter.com/danluu">Twitter</a></div>
+ <div><a href="http://danluu.com/atom.xml" rel="subscribe-rss" title="subscribe via RSS">RSS</a></div>
+ </div>
+</div>
+ </footer>
+ <script type="text/javascript" src="http://www.google-analytics.com/ga.js"></script><script type="text/javascript">
+ var _gaq = [['_setAccount', 'UA-43852829-1'], ['_trackPageview']];
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript';
+ ga.src = '//www.google-analytics.com/ga.js';
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
+ })();
+ </script>
+
+
+
+
+</div></body></html> \ No newline at end of file
diff --git a/reference/Files are hard_files/fs_properties.png b/reference/Files are hard_files/fs_properties.png
new file mode 100644
index 00000000..74230792
--- /dev/null
+++ b/reference/Files are hard_files/fs_properties.png
Binary files differ
diff --git a/reference/Files are hard_files/program_bugs.png b/reference/Files are hard_files/program_bugs.png
new file mode 100644
index 00000000..129a0b8b
--- /dev/null
+++ b/reference/Files are hard_files/program_bugs.png
Binary files differ
diff --git a/reference/Linux KAIO/History of Linux KAIO API.pdf b/reference/Linux KAIO/History of Linux KAIO API.pdf
new file mode 100644
index 00000000..2f725ab4
--- /dev/null
+++ b/reference/Linux KAIO/History of Linux KAIO API.pdf
Binary files differ
diff --git a/reference/Linux KAIO/KAIOUserGuide.htm b/reference/Linux KAIO/KAIOUserGuide.htm
new file mode 100644
index 00000000..9d197f12
--- /dev/null
+++ b/reference/Linux KAIO/KAIOUserGuide.htm
@@ -0,0 +1,733 @@
+<!DOCTYPE html>
+<!-- saved from url=(0050)https://code.google.com/p/kernel/wiki/AIOUserGuide -->
+<html><script>var gapi={plusone:{render:function(){},go:function(){}}};</script><script type="text/javascript" async="" src="https://apis.google.com/js/plusone.js"></script><script>var urchinTracker=function(){},_gaq={push:function(){try {if(arguments[0][0]=='_link')window.location.href=arguments[0][1]}catch(er){}}},_gat={_createTracker:function(){}, _getTracker:function(){return{__noSuchMethod__:function(){},_link:function(o){if(o)location.href=o;},_linkByPost:function(){return true;},_getLinkerUrl:function(o){return o;},_trackEvent:function(){}}}};</script><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
+
+ <meta name="ROBOTS" content="NOARCHIVE">
+
+ <link rel="icon" type="image/vnd.microsoft.icon" href="https://ssl.gstatic.com/codesite/ph/images/phosting.ico">
+
+
+ <link rel="canonical" href="http://code.google.com/p/kernel/wiki/AIOUserGuide">
+
+ <script type="text/javascript">
+
+
+
+
+ var codesite_token = "ABZ6GAciBR7wXJANw0lEk1JMtMA3vIzrjA:1408361093458";
+
+
+ var CS_env = {"token": "ABZ6GAciBR7wXJANw0lEk1JMtMA3vIzrjA:1408361093458", "profileUrl": "/u/nialldouglas14/", "projectName": "kernel", "assetHostPath": "https://ssl.gstatic.com/codesite/ph", "domainName": null, "projectHomeUrl": "/p/kernel", "assetVersionPath": "https://ssl.gstatic.com/codesite/ph/13997016681179179006", "loggedInUserEmail": "nialldouglas14@gmail.com", "relativeBaseUrl": ""};
+ var _gaq = _gaq || [];
+ _gaq.push(
+ ['siteTracker._setAccount', 'UA-18071-1'],
+ ['siteTracker._trackPageview']);
+
+ _gaq.push(
+ ['projectTracker._setAccount', 'UA-26096441-1'],
+ ['projectTracker._trackPageview']);
+
+ (function() {
+ var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
+ ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
+ })();
+
+ </script><script type="text/javascript" async="" src="https://ssl.google-analytics.com/ga.js"></script>
+
+
+ <title>AIOUserGuide -
+ kernel -
+
+ A description of how to use AIO -
+ Google production server Linux kernel development - Google Project Hosting
+ </title>
+ <link type="text/css" rel="stylesheet" href="./AIOUserGuide_files/core.css">
+
+ <link type="text/css" rel="stylesheet" href="./AIOUserGuide_files/ph_detail.css">
+
+
+
+ <link type="application/atom+xml" rel="alternate" href="https://code.google.com/feeds/p/kernel/gitchanges/basic?path=/AIOUserGuide.wiki&repo=wiki">
+
+
+<!--[if IE]>
+ <link type="text/css" rel="stylesheet" href="https://ssl.gstatic.com/codesite/ph/13997016681179179006/css/d_ie.css" >
+<![endif]-->
+ <style type="text/css">
+ .menuIcon.off { background: no-repeat url(https://ssl.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 -42px }
+ .menuIcon.on { background: no-repeat url(https://ssl.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 -28px }
+ .menuIcon.down { background: no-repeat url(https://ssl.gstatic.com/codesite/ph/images/dropdown_sprite.gif) 0 0; }
+
+
+ #maincol {
+ padding-top: 0;
+ padding-bottom: 0;
+ }
+
+
+ </style>
+<style type="text/css"></style></head>
+<body class="t6">
+<script type="text/javascript">
+ window.___gcfg = {lang: 'en'};
+ (function()
+ {var po = document.createElement("script");
+ po.type = "text/javascript"; po.async = true;po.src = "https://apis.google.com/js/plusone.js";
+ var s = document.getElementsByTagName("script")[0];
+ s.parentNode.insertBefore(po, s);
+ })();
+</script>
+<div class="headbg">
+
+ <div id="gaia">
+
+
+ <span>
+
+
+
+ <a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#" id="multilogin-dropdown" onclick="return false;"><u><b>nialldouglas14@gmail.com</b></u> <small>▼</small></a>
+
+
+ | <a href="https://code.google.com/u/nialldouglas14/" id="projects-dropdown" onclick="return false;"><u>My favorites</u> <small>▼</small></a>
+ | <a href="https://code.google.com/u/nialldouglas14/" onclick="_CS_click(&#39;/gb/ph/profile&#39;);" title="Profile, Updates, and Settings"><u>Profile</u></a>
+ | <a href="https://www.google.com/accounts/Logout?continue=https%3A%2F%2Fcode.google.com%2Fp%2Fkernel%2Fwiki%2FAIOUserGuide" onclick="_CS_click(&#39;/gb/ph/signout&#39;);"><u>Sign out</u></a>
+
+ </span>
+
+ </div>
+
+ <div class="gbh" style="left: 0pt;"></div>
+ <div class="gbh" style="right: 0pt;"></div>
+
+
+ <div style="height: 1px"></div>
+<!--[if lte IE 7]>
+<div style="text-align:center;">
+Your version of Internet Explorer is not supported. Try a browser that
+contributes to open source, such as <a href="http://www.firefox.com">Firefox</a>,
+<a href="http://www.google.com/chrome">Google Chrome</a>, or
+<a href="http://code.google.com/chrome/chromeframe/">Google Chrome Frame</a>.
+</div>
+<![endif]-->
+
+
+
+ <table style="padding:0px; margin: 0px 0px 10px 0px; width:100%" cellpadding="0" cellspacing="0" itemscope="" itemtype="http://schema.org/CreativeWork">
+ <tbody><tr style="height: 58px;">
+
+
+
+ <td id="plogo">
+ <link itemprop="url" href="https://code.google.com/p/kernel">
+ <a href="https://code.google.com/p/kernel/">
+
+ <img src="./AIOUserGuide_files/defaultlogo.png" alt="Logo" itemprop="image">
+
+ </a>
+ </td>
+
+ <td style="padding-left: 0.5em">
+
+ <div id="pname">
+ <a href="https://code.google.com/p/kernel/"><span itemprop="name">kernel</span></a>
+ </div>
+
+ <div id="psum">
+ <a id="project_summary_link" href="https://code.google.com/p/kernel/"><span itemprop="description">Google production server Linux kernel development</span></a>
+
+ </div>
+
+
+ </td>
+ <td style="white-space:nowrap;text-align:right; vertical-align:bottom;">
+
+ <form action="https://code.google.com/hosting/search">
+ <input size="30" name="q" value="" type="text">
+
+ <input type="submit" name="projectsearch" value="Search projects">
+ </form>
+
+ </td></tr>
+ </tbody></table>
+
+</div>
+
+
+<div id="mt" class="gtb">
+ <a href="https://code.google.com/p/kernel/" class="tab ">Project&nbsp;Home</a>
+
+
+
+
+ <a href="https://code.google.com/p/kernel/downloads/list" class="tab ">Downloads</a>
+
+
+
+
+
+ <a href="https://code.google.com/p/kernel/w/list" class="tab active">Wiki</a>
+
+
+
+
+
+
+
+ <a href="https://code.google.com/p/kernel/wiki/Git?tm=4" class="tab ">Source</a>
+
+
+
+
+
+
+
+
+ <div class="gtbc"></div>
+</div>
+<table cellspacing="0" cellpadding="0" width="100%" align="center" border="0" class="st">
+ <tbody><tr>
+
+
+
+ <td class="subt">
+ <div class="issueDetail">
+<div class="isf">
+
+ <span class="inIssueList">
+ <span>Search</span>
+ <form action="https://code.google.com/p/kernel/w/list" method="GET" style="display:inline">
+ <select id="can" name="can">
+ <option disabled="disabled">Search within:</option>
+
+ <option value="1">&nbsp;All wiki pages</option>
+ <option value="3">&nbsp;Featured pages</option>
+ <option value="2" selected="selected">&nbsp;Current pages</option>
+
+
+ <option value="5">&nbsp;My starred pages</option>
+
+ <option value="4">&nbsp;Deprecated pages</option>
+
+ </select>
+ <span>for</span>
+ <span id="qq"><input type="text" size="38" id="searchq" name="q" value="" autocomplete="on"></span>
+
+
+
+ <input type="submit" value="Search">
+ </form>
+ </span>
+
+
+
+
+
+
+
+
+
+</div>
+</div>
+
+ </td>
+
+
+
+
+
+
+ <td align="right" valign="top" class="bevel-right"></td>
+ </tr>
+</tbody></table>
+
+
+<script type="text/javascript">
+ var cancelBubble = false;
+ function _go(url) { document.location = url; }
+</script>
+<div id="maincol">
+
+
+
+
+
+
+
+
+
+ <style type="text/css">
+ .delcom { background: #e8e8e8 }
+ .commentcontent {
+ margin: 2em;
+ padding: 0px 10px;
+ width: 66em;
+ }
+ .artifactcomment {
+ border-top: 3px solid #c3d9ff;
+ }
+ #commentform {
+ border-top: 3px solid #c3d9ff;
+ }
+ </style>
+
+<div id="wikipage">
+<table>
+ <tbody><tr>
+
+
+ <td style="vertical-align:top; padding-left:5px">
+
+ <div id="wikiheader">
+
+ <img width="15" height="15" id="star_img" src="./AIOUserGuide_files/star_off.gif" style="cursor:pointer" onclick="_CS_toggleStar(this,
+ {&#39;scope&#39;: &#39;wiki&#39;,
+ &#39;user&#39;: &#39;_CURRENT_USER&#39;,
+ &#39;item&#39;: &#39;kernel:AIOUserGuide&#39;
+ });">
+
+ <span style="font-size:120%;font-weight:bold">AIOUserGuide</span>
+ &nbsp;
+ <div>
+
+ <i>A description of how to use AIO</i>
+
+
+
+ <div id="wikiauthor" style="float:right">
+ Updated <span title="Mon Apr 21 08:39:31 2014">
+ Apr 21, 2014</span>
+
+ by
+
+ <a class="userlink" href="https://code.google.com/u/111678707898441125339/">dehrenb...@google.com</a>
+
+ </div>
+ </div>
+ </div>
+
+ <div id="wikicontent">
+ <div class="vt" id="wikimaincol">
+ <h1><a name="Introduction"></a>Introduction<a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#Introduction" class="section_anchor"></a></h1><p>The Asynchronous Input/Output (AIO) interface allows many I/O requests to be submitted in parallel without the overhead of a thread per request. The purpose of this document is to explain how to use the Linux AIO interface, namely the function family <tt>io_setup</tt>, <tt>io_submit</tt>, <tt>io_getevents</tt>, <tt>io_destroy</tt>. Currently, the AIO interface is best for <tt>O_DIRECT</tt> access to a raw block device like a disk, flash drive or storage array. </p><h1><a name="What_is_AIO?"></a>What is AIO?<a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#What_is_AIO?" class="section_anchor"></a></h1><p>Input and output functions involve a device, like a disk or flash drive, which works much slower than the CPU. Consequently, the CPU can be doing other things while waiting for an operation on the device to complete. There are multiple ways to handle this: </p><ul><li>In the <strong>synchronous I/O</strong> model, the application issues a request from a thread. The thread blocks until the operation is complete. The operating system creates the illusion that issuing the request to the device and receiving the result was just like any other operation that would proceed just on the CPU, but in reality, it may switch in other threads or processes to make use of the CPU resources and to allow other device requests to be issued to the device in parallel, originating from the same CPU. </li><li>In the <strong>asynchronous I/O (AIO)</strong> model, the application can submit one or many requests from a thread. Submitting a request does not cause the thread to block, and instead the thread can proceed to do other computations and submit further requests to the device while the original request is in flight. The application is expected to process completions and organize logical computations itself without depending on threads to organize the use of data. </li></ul><p></p><p>Asynchronous I/O can be considered “lower level” than synchronous I/O because it does not make use of a system-provided concept of threads to organize its computation. However, it is often more efficient to use AIO than synchronous I/O due the nondeterministic overhead of threads. </p><h1><a name="The_Linux_AIO_model"></a>The Linux AIO model<a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#The_Linux_AIO_model" class="section_anchor"></a></h1><p>The Linux AIO model is used as follows: </p><ol><li>Open an <a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#I/O_context">I/O context</a> to submit and reap I/O requests from. </li><li>Create one or more <a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#Submitting_requests">request objects</a> and set them up to represent the desired operation </li><li>Submit these requests to the I/O context, which will send them down to the device driver to process on the device </li><li><a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#Processing_results">Reap completions</a> from the I/O context in the form of event completion objects, </li><li>Return to step 2 as needed. </li></ol><p></p><h1><a name="I/O_context"></a>I/O context<a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#I/O_context" class="section_anchor"></a></h1><p><tt>io_context_t</tt> is a pointer-sized opaque datatype that represents an “AIO context”. It can be safely passed around by value. Requests in the form of a struct iocb are submitted to an <tt>io_context_t</tt> and completions are read from the <tt>io_context_t</tt>. Internally, this structure contains a queue of completed requests. The length of the queue forms an upper bound on the number of concurrent requests which may be submitted to the <tt>io_context_t</tt>. </p><p>To create a new io_context_t, use the function </p><pre class="prettyprint"><span class="kwd">int</span><span class="pln"> io_setup</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> maxevents</span><span class="pun">,</span><span class="pln"> io_context_t </span><span class="pun">*</span><span class="pln">ctxp</span><span class="pun">);</span></pre><p>Here, ctxp is the output and maxevents is the input. The function creates an io_context_t with an internal queue of length maxevents. To deallocate an io_context_t, use </p><pre class="prettyprint"><span class="kwd">int</span><span class="pln"> io_destroy</span><span class="pun">(</span><span class="pln">io_context_t ctx</span><span class="pun">);</span></pre><p>There is a system-wide maximum number of allocated <tt>io_context_t</tt> objects, set at 65536. </p><p>An <tt>io_context_t</tt> object can be shared between threads, both for submission and completion. No guarantees are provided about ordering of submission and completion with respect to interaction from multiple threads. There may be performance implications from sharing <tt>io_context_t</tt> objects between threads. </p><h1><a name="Submitting_requests"></a>Submitting requests<a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#Submitting_requests" class="section_anchor"></a></h1><p><tt>struct iocb</tt> represents a single request for a read or write operation. The following struct shows a simplification on the struct definition; a full definition is found in <tt>&lt;libaio.h&gt;</tt> within the libaio source code. </p><pre class="prettyprint"><span class="kwd">struct</span><span class="pln"> iocb </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">data</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">short</span><span class="pln"> aio_lio_opcode</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">int</span><span class="pln"> aio_fildes</span><span class="pun">;</span><span class="pln"><br><br>&nbsp; &nbsp; </span><span class="kwd">union</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">struct</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">buf</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">unsigned</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> nbytes</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> offset</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"> c</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"> u</span><span class="pun">;</span><span class="pln"><br></span><span class="pun">};</span></pre><p>The meaning of the fields is as follows: data is a pointer to a user-defined object used to represent the operation </p><ul><li><tt>aio_lio_opcode</tt> is a flag indicate whether the operation is a read (<tt>IO_CMD_PREAD</tt>) or a write (<tt>IO_CMD_PWRITE</tt>) or one of the other supported operations </li><li><tt>aio_fildes</tt> is the fd of the file that the iocb reads or writes </li><li><tt>buf</tt> is the pointer to memory that is read or written </li><li><tt>nbytes</tt> is the length of the request </li><li><tt>offset</tt> is the initial offset of the read or write within the file </li></ul>The convenience functions <tt>io_prep_pread</tt> and <tt>io_prep_pwrite</tt> can be used to initialize a <tt>struct iocb</tt>. <p></p><p>New operations are sent to the device with <tt>io_submit</tt>. </p><pre class="prettyprint"><span class="kwd">int</span><span class="pln"> io_submit</span><span class="pun">(</span><span class="pln">io_context_t ctx</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> nr</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> iocb </span><span class="pun">*</span><span class="pln">ios</span><span class="pun">[]);</span></pre><p><tt>io_submit</tt> allows an array of pointers to <tt>struct iocb</tt>s to be submitted all at once. In this function call, <tt>nr</tt> is the length of the <tt>ios</tt> array. If multiple operations are sent in one array, then no ordering guarantees are given between the <tt>iocb</tt>s. Submitting in larger batches sometimes results in a performance improvement due to a reduction in CPU usage. A performance improvement also sometimes results from keeping many I/Os ‘in flight’ simultaneously. </p><p>If the submission includes too many iocbs such that the internal queue of the <tt>io_context_t</tt> would overfill on completion, then <tt>io_submit</tt> will return a non-zero number and set <tt>errno</tt> to <tt>EAGAIN</tt>. </p><p>When used under the right conditions, <tt>io_submit</tt> should not block. However, when used in certain ways, it may block, undermining the purpose of asynchronous I/O. If this is a problem for your application, be sure to use the <tt>O_DIRECT</tt> flag when opening a file, and operate on a raw block device. Work is ongoing to fix the problem. </p><h1><a name="Processing_results"></a>Processing results<a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#Processing_results" class="section_anchor"></a></h1><p>Completions read from an <tt>io_context_t</tt> are of the type <tt>struct io_event</tt>, which contains the following relevant fields. </p><pre class="prettyprint"><span class="kwd">struct</span><span class="pln"> io_event </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">void</span><span class="pln"> </span><span class="pun">*</span><span class="pln">data</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">struct</span><span class="pln"> iocb </span><span class="pun">*</span><span class="pln">obj</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">long</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> res</span><span class="pun">;</span><span class="pln"><br></span><span class="pun">};</span></pre><p>Here, data is the same data pointer that was passed in with the <tt>struct iocb</tt>, and obj is the original <tt>struct iocb</tt>. <tt>res</tt> is the return value of the read or write. </p><p>Completions are reaped with <tt>io_getevents</tt>. </p><pre class="prettyprint"><span class="kwd">int</span><span class="pln"> io_getevents</span><span class="pun">(</span><span class="pln">io_context_t ctx_id</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> min_nr</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">long</span><span class="pln"> nr</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> io_event </span><span class="pun">*</span><span class="pln">events</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">struct</span><span class="pln"> timespec </span><span class="pun">*</span><span class="pln">timeout</span><span class="pun">);</span></pre><p>This function has a good number of parameters, so an explanation is in order: </p><ul><li><tt>ctx_id</tt> is the io_context_t that is being reaped from. </li><li><tt>min_nr</tt> is the minimum number of io_events to return. io_gevents will block until there are min_nr completions to report, if this is not already the case when the function call is made. </li><li><tt>nr</tt> is the maximum number of completions to return. It is expected to be the length of the events array. </li><li><tt>events</tt> is an array of <tt>io_event</tt>s into which the information about completions is written. </li><li><tt>timeout</tt> is the maximum time that a call to <tt>io_getevents</tt> may block until it will return. If <tt>NULL</tt> is passed, then <tt>io_getevents</tt> will block until <tt>min_nr</tt> completions are available. </li></ul><p></p><p>The return value represents how many completions were reported, ie how much of events was written. The return value will be between <tt>0</tt> and <tt>nr</tt>. The return value may be lower than <tt>min_nr</tt> if the timeout expires; if the timeout is <tt>NULL</tt>, then the return value will be between <tt>min_nr</tt> and <tt>nr</tt>. </p><p>The parameters give a broad range of flexibility in how AIO can be used. </p><ul><li><tt>min_nr</tt> = 0 (or, equivalently, timeout = 0). This option forms a non-blocking polling technique: it will always return immediately, regardless of whether any completions are available. It makes sense to use min_nr = 0 when calling io_getevents as part of a main run-loop of an application, on each iteration. </li><li><tt>min_nr</tt> = 1. This option blocks until a single completion is available. This parameter is the minimum value which will produce a blocking call, and therefore may be the best value for low latency operations for some users. When an application notices that an <tt>eventfd</tt> corresponding to an <tt>iocb</tt> is triggered (see the next section about <tt>epoll</tt>), then the application can call <tt>io_getevents</tt> on the corresponding io_context_t with a guarantee that no blocking will occur. </li><li><tt>min_nr</tt> &gt; 1. This option waits for multiple completions to return, unless the timeout expires. Waiting for multiple completions may improve throughput due to reduced CPU usage, both due to fewer <tt>io_getevents</tt> calls and because if there is more space in the completion queue due to the removed completions, then a later <tt>io_submit</tt> call may have a larger granularity, as well as a reduced number of context switches back to the calling thread when the event is available. This option runs the risk of increasing the latency of operations, especially when the operation rate is lower. </li></ul><p></p><p>Even if <tt>min_nr</tt> = 0 or 1, it is useful to make nr a bit bigger for performance reasons: more than one event may be already complete, and it could be processed without multiple calls to <tt>io_getevents</tt>. The only cost of a larger <tt>nr</tt> value library is that the user must allocate a larger array of events and be prepared to accept them. </p><h1><a name="Use_with_epoll"></a>Use with <tt>epoll</tt><a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#Use_with_epoll" class="section_anchor"></a></h1><p>Any iocb can be set to notify an eventfd on completion using the libaio function <tt>io_set_eventfd</tt>. The eventfd can be put in an epoll object. When the eventfd is triggered, then the <tt>io_getevents</tt> function can be called on the corresponding <tt>io_context_t</tt>. </p><p>There is no way to use this API to trigger an eventfd only when multiple operations are complete--the <tt>eventfd</tt> will always be triggered on the first operation. Consequently, as described in the previous section, it will often make sense to use <tt>min_nr</tt> = 1 when using <tt>io_getevents</tt> after an <tt>epoll_wait</tt> call that indicates an eventfd involved in AIO. </p><h1><a name="Performance_considerations"></a>Performance considerations<a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#Performance_considerations" class="section_anchor"></a></h1><ul><li><strong>Blocking during io_submit on ext4, on buffered operations, network access, pipes, etc.</strong> Some operations are not well-represented by the AIO interface. With completely unsupported operations like buffered reads, operations on a socket or pipes, the entire operation will be performed during the io_submit syscall, with the completion available immediately for access with io_getevents. AIO access to a file on a filesystem like ext4 is partially supported: if a metadata read is required to look up the data block (ie if the metadata is not already in memory), then the io_submit call will block on the metadata read. Certain types of file-enlarging writes are completely unsupported and block for the entire duration of the operation. </li><li><strong>CPU overhead</strong>. When performing small operations on a high-performance device and targeting a very high operation rate from single CPU, a CPU bottleneck may result. This can be resolved by submitting and reaping AIO from multiple threads. </li><li><strong>Lock contention when many CPUs or requests share an io_context_t</strong>. There are several circumstances when the kernel datastructure corresponding to an io_context_t may be accessed from multiple CPUs. For example, multiple threads may submit and get events from the same io_context_t. Some devices may use a single interrupt line for all completions. This can cause the lock to be bounced around between cores or the lock to be heavily contended, resulting in higher CPU usage and potentially lower throughput. One solution is to shard into multiple io_context_t objects, for example by thread and a hash of the address. </li><li><strong>Ensuring sufficient parallelism</strong>. Some devices require many concurrent operations to reach peak performance. This means making sure that there are several operations ‘in flight’ simultaneously. On some high-performance storage devices, when operations are small, tens or hundreds must be submitted in parallel in order to achieve maximum throughput. For disk drives, performance may improve with greater parallelism if the elevator scheduler can make better decisions with more operations simultaneously in flight, but the effect is expected to be small in many situations. </li></ul><h1><a name="Alternatives_to_Linux_AIO"></a>Alternatives to Linux AIO<a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#Alternatives_to_Linux_AIO" class="section_anchor"></a></h1><ul><li><strong>Thread pool of synchronous I/O threads</strong>. This can work for many use cases, and it may be easier to program with. Unlike with AIO, all functions can be parallelized via a thread pool. Some users find that a thread pool does not work well due to the overhead of threads in terms of CPU and memory bandwidth usage from context switching. This comes up as an especially big problem with small random reads on high-performance storage devices. </li><li><strong>POSIX AIO</strong>. Another asynchronous I/O interface is POSIX AIO. It is implemented as part of glibc. However, the glibc implementation uses a thread pool internally. For cases where this is acceptable, it might be better to use your own thread pool instead. Joel Becker implemented <a href="http://oss.oracle.com/projects/libaio-oracle/files/" rel="nofollow">a version</a> of POSIX AIO based on the Linux AIO mechanism described above. IBM DeveloperWorks has <a href="http://www.ibm.com/developerworks/linux/library/l-async/index.html" rel="nofollow">a good introduction</a> to POSIX AIO. </li><li><strong>epoll</strong>. Linux has limited support for using epoll as a mechanism for asynchronous I/O. For reads to a file opened in buffered mode (that is, without <tt>O_DIRECT</tt>), if the file is opened as <tt>O_NONBLOCK</tt>, then a read will return <tt>EAGAIN</tt> until the relevant part is in memory. Writes to a buffered file are usually immediate, as they are written out with another writeback thread. However, these mechanisms don’t give the level of control over I/O that direct I/O gives. </li></ul><h1><a name="Sample_code"></a>Sample code<a href="https://code.google.com/p/kernel/wiki/AIOUserGuide#Sample_code" class="section_anchor"></a></h1><p>Below is some example code which uses Linux AIO. I wrote it at Google, so it uses the <a href="http://code.google.com/p/google-glog/" rel="nofollow">Google glog logging library</a> and the <a href="http://code.google.com/p/gflags/?redir=1" rel="nofollow">Google gflags command-line flags library</a>, as well as a loose interpretation of <a href="http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml" rel="nofollow">Google’s C++ coding conventions</a>. When compiling it with gcc, pass <tt>-laio</tt> to dynamically link with libaio. (It isn’t included in glibc, so it must be explicitly included.) </p><pre class="prettyprint"><span class="com">// Code written by Daniel Ehrenberg, released into the public domain</span><span class="pln"><br><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;fcntl.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;gflags/gflags.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;glog/logging.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;libaio.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdlib.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;sys/stat.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;sys/types.h&gt;</span><span class="pln"><br><br>DEFINE_string</span><span class="pun">(</span><span class="pln">path</span><span class="pun">,</span><span class="pln"> </span><span class="str">"/tmp/testfile"</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Path to the file to manipulate"</span><span class="pun">);</span><span class="pln"><br>DEFINE_int32</span><span class="pun">(</span><span class="pln">file_size</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1000</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Length of file in 4k blocks"</span><span class="pun">);</span><span class="pln"><br>DEFINE_int32</span><span class="pun">(</span><span class="pln">concurrent_requests</span><span class="pun">,</span><span class="pln"> </span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="str">"Number of concurrent requests"</span><span class="pun">);</span><span class="pln"><br>DEFINE_int32</span><span class="pun">(</span><span class="pln">min_nr</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">"min_nr"</span><span class="pun">);</span><span class="pln"><br>DEFINE_int32</span><span class="pun">(</span><span class="pln">max_nr</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="str">"max_nr"</span><span class="pun">);</span><span class="pln"><br><br></span><span class="com">// The size of operation that will occur on the device</span><span class="pln"><br></span><span class="kwd">static</span><span class="pln"> </span><span class="kwd">const</span><span class="pln"> </span><span class="kwd">int</span><span class="pln"> kPageSize </span><span class="pun">=</span><span class="pln"> </span><span class="lit">4096</span><span class="pun">;</span><span class="pln"><br><br></span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AIORequest</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp;</span><span class="kwd">public</span><span class="pun">:</span><span class="pln"><br>&nbsp; </span><span class="kwd">int</span><span class="pun">*</span><span class="pln"> buffer_</span><span class="pun">;</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Complete</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"><br><br>&nbsp; </span><span class="typ">AIORequest</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">int</span><span class="pln"> ret </span><span class="pun">=</span><span class="pln"> posix_memalign</span><span class="pun">(</span><span class="kwd">reinterpret_cast</span><span class="pun">&lt;</span><span class="kwd">void</span><span class="pun">**&gt;(&amp;</span><span class="pln">buffer_</span><span class="pun">),</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;kPageSize</span><span class="pun">,</span><span class="pln"> kPageSize</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; CHECK_EQ</span><span class="pun">(</span><span class="pln">ret</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">virtual</span><span class="pln"> </span><span class="pun">~</span><span class="typ">AIORequest</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; free</span><span class="pun">(</span><span class="pln">buffer_</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br></span><span class="pun">};</span><span class="pln"><br><br></span><span class="kwd">class</span><span class="pln"> </span><span class="typ">Adder</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp;</span><span class="kwd">public</span><span class="pun">:</span><span class="pln"><br>&nbsp; </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Add</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> amount</span><span class="pun">)</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">virtual</span><span class="pln"> </span><span class="pun">~</span><span class="typ">Adder</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">};</span><span class="pln"><br></span><span class="pun">};</span><span class="pln"><br><br></span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AIOReadRequest</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">AIORequest</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp;</span><span class="kwd">private</span><span class="pun">:</span><span class="pln"><br>&nbsp; </span><span class="typ">Adder</span><span class="pun">*</span><span class="pln"> adder_</span><span class="pun">;</span><span class="pln"><br><br>&nbsp;</span><span class="kwd">public</span><span class="pun">:</span><span class="pln"><br>&nbsp; </span><span class="typ">AIOReadRequest</span><span class="pun">(</span><span class="typ">Adder</span><span class="pun">*</span><span class="pln"> adder</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">AIORequest</span><span class="pun">(),</span><span class="pln"> adder_</span><span class="pun">(</span><span class="pln">adder</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Complete</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; CHECK_EQ</span><span class="pun">(</span><span class="pln">res</span><span class="pun">,</span><span class="pln"> kPageSize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Read incomplete or error "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> res</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">int</span><span class="pln"> value </span><span class="pun">=</span><span class="pln"> buffer_</span><span class="pun">[</span><span class="lit">0</span><span class="pun">];</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Read of "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> value </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" completed"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; adder_</span><span class="pun">-&gt;</span><span class="typ">Add</span><span class="pun">(</span><span class="pln">value</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br></span><span class="pun">};</span><span class="pln"><br><br></span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AIOWriteRequest</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">AIORequest</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp;</span><span class="kwd">private</span><span class="pun">:</span><span class="pln"><br>&nbsp; </span><span class="kwd">int</span><span class="pln"> value_</span><span class="pun">;</span><span class="pln"><br><br>&nbsp;</span><span class="kwd">public</span><span class="pun">:</span><span class="pln"><br>&nbsp; </span><span class="typ">AIOWriteRequest</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="typ">AIORequest</span><span class="pun">(),</span><span class="pln"> value_</span><span class="pun">(</span><span class="pln">value</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; buffer_</span><span class="pun">[</span><span class="lit">0</span><span class="pun">]</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> value</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Complete</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> res</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; CHECK_EQ</span><span class="pun">(</span><span class="pln">res</span><span class="pun">,</span><span class="pln"> kPageSize</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Write incomplete or error "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> res</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Write of "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> value_ </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" completed"</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br></span><span class="pun">};</span><span class="pln"><br><br></span><span class="kwd">class</span><span class="pln"> </span><span class="typ">AIOAdder</span><span class="pln"> </span><span class="pun">:</span><span class="pln"> </span><span class="kwd">public</span><span class="pln"> </span><span class="typ">Adder</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp;</span><span class="kwd">public</span><span class="pun">:</span><span class="pln"><br>&nbsp; </span><span class="kwd">int</span><span class="pln"> fd_</span><span class="pun">;</span><span class="pln"><br>&nbsp; io_context_t ioctx_</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="kwd">int</span><span class="pln"> counter_</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="kwd">int</span><span class="pln"> reap_counter_</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="kwd">int</span><span class="pln"> sum_</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="kwd">int</span><span class="pln"> length_</span><span class="pun">;</span><span class="pln"><br><br>&nbsp; </span><span class="typ">AIOAdder</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> length</span><span class="pun">)</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; </span><span class="pun">:</span><span class="pln"> ioctx_</span><span class="pun">(</span><span class="lit">0</span><span class="pun">),</span><span class="pln"> counter_</span><span class="pun">(</span><span class="lit">0</span><span class="pun">),</span><span class="pln"> reap_counter_</span><span class="pun">(</span><span class="lit">0</span><span class="pun">),</span><span class="pln"> sum_</span><span class="pun">(</span><span class="lit">0</span><span class="pun">),</span><span class="pln"> length_</span><span class="pun">(</span><span class="pln">length</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"> </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Init</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Opening file"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; fd_ </span><span class="pun">=</span><span class="pln"> open</span><span class="pun">(</span><span class="pln">FLAGS_path</span><span class="pun">.</span><span class="pln">c_str</span><span class="pun">(),</span><span class="pln"> O_RDWR </span><span class="pun">|</span><span class="pln"> O_DIRECT </span><span class="pun">|</span><span class="pln"> O_CREAT</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0644</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; PCHECK</span><span class="pun">(</span><span class="pln">fd_ </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Error opening file"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Allocating enough space for the sum"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; PCHECK</span><span class="pun">(</span><span class="pln">fallocate</span><span class="pun">(</span><span class="pln">fd_</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> </span><span class="lit">0</span><span class="pun">,</span><span class="pln"> kPageSize </span><span class="pun">*</span><span class="pln"> length_</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Error in fallocate"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Setting up the io context"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; PCHECK</span><span class="pun">(</span><span class="pln">io_setup</span><span class="pun">(</span><span class="lit">100</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">ioctx_</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&gt;=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Error in io_setup"</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">virtual</span><span class="pln"> </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Add</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> amount</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; sum_ </span><span class="pun">+=</span><span class="pln"> amount</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Adding "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> amount </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" for a total of "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> sum_</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">SubmitWrite</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Submitting a write to "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> counter_</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">struct</span><span class="pln"> iocb iocb</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">struct</span><span class="pln"> iocb</span><span class="pun">*</span><span class="pln"> iocbs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">iocb</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="typ">AIORequest</span><span class="pln"> </span><span class="pun">*</span><span class="pln">req </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AIOWriteRequest</span><span class="pun">(</span><span class="pln">counter_</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; io_prep_pwrite</span><span class="pun">(&amp;</span><span class="pln">iocb</span><span class="pun">,</span><span class="pln"> fd_</span><span class="pun">,</span><span class="pln"> req</span><span class="pun">-&gt;</span><span class="pln">buffer_</span><span class="pun">,</span><span class="pln"> kPageSize</span><span class="pun">,</span><span class="pln"> counter_ </span><span class="pun">*</span><span class="pln"> kPageSize</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; iocb</span><span class="pun">.</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> req</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">int</span><span class="pln"> res </span><span class="pun">=</span><span class="pln"> io_submit</span><span class="pun">(</span><span class="pln">ioctx_</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">iocbs</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; CHECK_EQ</span><span class="pun">(</span><span class="pln">res</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">WriteFile</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; reap_counter_ </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">counter_ </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> counter_ </span><span class="pun">&lt;</span><span class="pln"> length_</span><span class="pun">;</span><span class="pln"> counter_</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; </span><span class="typ">SubmitWrite</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; </span><span class="typ">Reap</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="typ">ReapRemaining</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">SubmitRead</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Submitting a read from "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> counter_</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">struct</span><span class="pln"> iocb iocb</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">struct</span><span class="pln"> iocb</span><span class="pun">*</span><span class="pln"> iocbs </span><span class="pun">=</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">iocb</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="typ">AIORequest</span><span class="pln"> </span><span class="pun">*</span><span class="pln">req </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> </span><span class="typ">AIOReadRequest</span><span class="pun">(</span><span class="kwd">this</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; io_prep_pread</span><span class="pun">(&amp;</span><span class="pln">iocb</span><span class="pun">,</span><span class="pln"> fd_</span><span class="pun">,</span><span class="pln"> req</span><span class="pun">-&gt;</span><span class="pln">buffer_</span><span class="pun">,</span><span class="pln"> kPageSize</span><span class="pun">,</span><span class="pln"> counter_ </span><span class="pun">*</span><span class="pln"> kPageSize</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; iocb</span><span class="pun">.</span><span class="pln">data </span><span class="pun">=</span><span class="pln"> req</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">int</span><span class="pln"> res </span><span class="pun">=</span><span class="pln"> io_submit</span><span class="pun">(</span><span class="pln">ioctx_</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">iocbs</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; CHECK_EQ</span><span class="pun">(</span><span class="pln">res</span><span class="pun">,</span><span class="pln"> </span><span class="lit">1</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ReadFile</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; reap_counter_ </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="pln">counter_ </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> counter_ </span><span class="pun">&lt;</span><span class="pln"> length_</span><span class="pun">;</span><span class="pln"> counter_</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="typ">SubmitRead</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="typ">Reap</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="typ">ReapRemaining</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">int</span><span class="pln"> </span><span class="typ">DoReap</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> min_nr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Reaping between "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> min_nr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" and "</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">&lt;&lt;</span><span class="pln"> FLAGS_max_nr </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" io_events"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">struct</span><span class="pln"> io_event</span><span class="pun">*</span><span class="pln"> events </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">new</span><span class="pln"> io_event</span><span class="pun">[</span><span class="pln">FLAGS_max_nr</span><span class="pun">];</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">struct</span><span class="pln"> timespec timeout</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; timeout</span><span class="pun">.</span><span class="pln">tv_sec </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; timeout</span><span class="pun">.</span><span class="pln">tv_nsec </span><span class="pun">=</span><span class="pln"> </span><span class="lit">100000000</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">int</span><span class="pln"> num_events</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Calling io_getevents"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; num_events </span><span class="pun">=</span><span class="pln"> io_getevents</span><span class="pun">(</span><span class="pln">ioctx_</span><span class="pun">,</span><span class="pln"> min_nr</span><span class="pun">,</span><span class="pln"> FLAGS_max_nr</span><span class="pun">,</span><span class="pln"> events</span><span class="pun">,</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">&amp;</span><span class="pln">timeout</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Calling completion function on results"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">for</span><span class="pln"> </span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> i </span><span class="pun">=</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"> i </span><span class="pun">&lt;</span><span class="pln"> num_events</span><span class="pun">;</span><span class="pln"> i</span><span class="pun">++)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; </span><span class="kwd">struct</span><span class="pln"> io_event </span><span class="kwd">event</span><span class="pln"> </span><span class="pun">=</span><span class="pln"> events</span><span class="pun">[</span><span class="pln">i</span><span class="pun">];</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; </span><span class="typ">AIORequest</span><span class="pun">*</span><span class="pln"> req </span><span class="pun">=</span><span class="pln"> </span><span class="kwd">static_cast</span><span class="pun">&lt;</span><span class="typ">AIORequest</span><span class="pun">*&gt;(</span><span class="kwd">event</span><span class="pun">.</span><span class="pln">data</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; req</span><span class="pun">-&gt;</span><span class="typ">Complete</span><span class="pun">(</span><span class="kwd">event</span><span class="pun">.</span><span class="pln">res</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; </span><span class="kwd">delete</span><span class="pln"> req</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">delete</span><span class="pln"> events</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; <br>LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Reaped "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> num_events </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" io_events"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; reap_counter_ </span><span class="pun">+=</span><span class="pln"> num_events</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">return</span><span class="pln"> num_events</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">Reap</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">counter_ </span><span class="pun">&gt;=</span><span class="pln"> FLAGS_min_nr</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; </span><span class="typ">DoReap</span><span class="pun">(</span><span class="pln">FLAGS_min_nr</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">void</span><span class="pln"> </span><span class="typ">ReapRemaining</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">while</span><span class="pln"> </span><span class="pun">(</span><span class="pln">reap_counter_ </span><span class="pun">&lt;</span><span class="pln"> length_</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; </span><span class="typ">DoReap</span><span class="pun">(</span><span class="lit">1</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="pun">~</span><span class="typ">AIOAdder</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Closing AIO context and file"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; io_destroy</span><span class="pun">(</span><span class="pln">ioctx_</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; close</span><span class="pun">(</span><span class="pln">fd_</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; </span><span class="kwd">int</span><span class="pln"> </span><span class="typ">Sum</span><span class="pun">()</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Writing consecutive integers to file"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="typ">WriteFile</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Reading consecutive integers from file"</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="typ">ReadFile</span><span class="pun">();</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">return</span><span class="pln"> sum_</span><span class="pun">;</span><span class="pln"><br>&nbsp; </span><span class="pun">}</span><span class="pln"><br></span><span class="pun">};</span><span class="pln"><br><br></span><span class="kwd">int</span><span class="pln"> main</span><span class="pun">(</span><span class="kwd">int</span><span class="pln"> argc</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">char</span><span class="pun">*</span><span class="pln"> argv</span><span class="pun">[])</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; google</span><span class="pun">::</span><span class="typ">ParseCommandLineFlags</span><span class="pun">(&amp;</span><span class="pln">argc</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">argv</span><span class="pun">,</span><span class="pln"> </span><span class="kwd">true</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="typ">AIOAdder</span><span class="pln"> adder</span><span class="pun">(</span><span class="pln">FLAGS_file_size</span><span class="pun">);</span><span class="pln"><br>&nbsp; adder</span><span class="pun">.</span><span class="typ">Init</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="kwd">int</span><span class="pln"> sum </span><span class="pun">=</span><span class="pln"> adder</span><span class="pun">.</span><span class="typ">Sum</span><span class="pun">();</span><span class="pln"><br>&nbsp; </span><span class="kwd">int</span><span class="pln"> expected </span><span class="pun">=</span><span class="pln"> </span><span class="pun">(</span><span class="pln">FLAGS_file_size </span><span class="pun">*</span><span class="pln"> </span><span class="pun">(</span><span class="pln">FLAGS_file_size </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">))</span><span class="pln"> </span><span class="pun">/</span><span class="pln"> </span><span class="lit">2</span><span class="pun">;</span><span class="pln"><br>&nbsp; LOG</span><span class="pun">(</span><span class="pln">INFO</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"AIO is complete"</span><span class="pun">;</span><span class="pln"><br>&nbsp; CHECK_EQ</span><span class="pun">(</span><span class="pln">sum</span><span class="pun">,</span><span class="pln"> expected</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">"Expected "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> expected </span><span class="pun">&lt;&lt;</span><span class="pln"> </span><span class="str">" Got "</span><span class="pln"> </span><span class="pun">&lt;&lt;</span><span class="pln"> sum</span><span class="pun">;</span><span class="pln"><br>&nbsp; printf</span><span class="pun">(</span><span class="str">"Successfully calculated that the sum of integers from 0"</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span><span class="str">" to %d is %d\n"</span><span class="pun">,</span><span class="pln"> FLAGS_file_size </span><span class="pun">-</span><span class="pln"> </span><span class="lit">1</span><span class="pun">,</span><span class="pln"> sum</span><span class="pun">);</span><span class="pln"><br>&nbsp; </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"><br></span><span class="pun">}</span></pre>
+ </div>
+ </div>
+ </td></tr><tr>
+</tr></tbody></table>
+ </div>
+
+
+
+ <div id="wikicommentcol">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<div class="collapse">
+
+
+
+
+
+<div id="commentlist">
+
+
+ <div class="artifactcomment">
+
+
+
+
+
+
+ <span class="author">Comment
+
+ by
+
+
+
+ <a class="userlink" href="https://code.google.com/u/103967590852603522648/">bert.hub...@gmail.com</a>,
+
+ </span>
+ <span class="date" title="Wed May 30 01:42:21 2012">May 30, 2012</span>
+ <div>
+<div class="commentcontent">
+<p>Hi Daniel, </p><p>Thanks for writing this fine document. I reference it from <a href="http://bert-hubert.blogspot.com/2012/05/on-linux-asynchronous-file-io.html" rel="nofollow">http://bert-hubert.blogspot.com/2012/05/on-linux-asynchronous-file-io.html</a> - thanks! </p>
+</div>
+
+
+ </div>
+ </div>
+
+
+
+ <div class="artifactcomment">
+
+
+
+
+
+
+ <span class="author">Comment
+
+ by
+
+
+
+ <a class="userlink" href="https://code.google.com/u/115155307146537731796/">ersun.wa...@gmail.com</a>,
+
+ </span>
+ <span class="date" title="Sun Aug 12 13:57:26 2012">Aug 12, 2012</span>
+ <div>
+<div class="commentcontent">
+<p>Great write-up. Linked here: <a href="http://webfiveoh.com/content/guides/2012/aug/mon-13th/linux-asynchronous-io-and-libaio.html" rel="nofollow">http://webfiveoh.com/content/guides/2012/aug/mon-13th/linux-asynchronous-io-and-libaio.html</a> </p>
+</div>
+
+
+ </div>
+ </div>
+
+
+
+ <div class="artifactcomment">
+
+
+
+
+
+
+ <span class="author">Comment
+
+ by
+
+
+
+ <a class="userlink" href="https://code.google.com/u/103790604532162480537/">una...@gmail.com</a>,
+
+ </span>
+ <span class="date" title="Tue Feb 19 03:21:40 2013">Feb 19, 2013</span>
+ <div>
+<div class="commentcontent">
+<p>Epoll on linux doesn't support regular files. It always returns ENOPERM when registering them with the epollctl syscall. I've tried with ext4, btrfs, xfs &amp; jfs on Linux 3.6 with the same result. </p><pre class="prettyprint"><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;sys/epoll.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;sys/types.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;sys/stat.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;fcntl.h&gt;</span><span class="pln"><br></span><span class="com">#include</span><span class="pln"> </span><span class="str">&lt;stdio.h&gt;</span><span class="pln"><br><br></span><span class="kwd">int</span><span class="pln"> main</span><span class="pun">(</span><span class="kwd">void</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">int</span><span class="pln"> ep </span><span class="pun">=</span><span class="pln"> epoll_create1</span><span class="pun">(</span><span class="lit">0</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">int</span><span class="pln"> fd </span><span class="pun">=</span><span class="pln"> open</span><span class="pun">(</span><span class="str">"/root/kernel-uek-2.6.39-300.17.2.el6uek.src.rpm"</span><span class="pun">,</span><span class="pln"> O_RDONLY</span><span class="pun">|</span><span class="pln">O_NONBLOCK</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">struct</span><span class="pln"> epoll_event evt </span><span class="pun">=</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="pun">.</span><span class="pln">events </span><span class="pun">=</span><span class="pln"> EPOLLIN<br>&nbsp; &nbsp; </span><span class="pun">};</span><span class="pln"><br><br>&nbsp; &nbsp; </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">ep </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pln"> </span><span class="pun">||</span><span class="pln"> fd </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; printf</span><span class="pun">(</span><span class="str">"Error opening fds.\n"</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br><br>&nbsp; &nbsp; </span><span class="kwd">if</span><span class="pln"> </span><span class="pun">(</span><span class="pln">epoll_ctl</span><span class="pun">(</span><span class="pln">ep</span><span class="pun">,</span><span class="pln"> EPOLL_CTL_ADD</span><span class="pun">,</span><span class="pln"> fd</span><span class="pun">,</span><span class="pln"> </span><span class="pun">&amp;</span><span class="pln">evt</span><span class="pun">)</span><span class="pln"> </span><span class="pun">&lt;</span><span class="pln"> </span><span class="lit">0</span><span class="pun">)</span><span class="pln"> </span><span class="pun">{</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; perror</span><span class="pun">(</span><span class="str">"epoll_ctl"</span><span class="pun">);</span><span class="pln"><br>&nbsp; &nbsp; &nbsp; &nbsp; </span><span class="kwd">return</span><span class="pln"> </span><span class="pun">-</span><span class="lit">1</span><span class="pun">;</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="pun">}</span><span class="pln"><br>&nbsp; &nbsp; </span><span class="kwd">return</span><span class="pln"> </span><span class="lit">0</span><span class="pun">;</span><span class="pln"><br></span><span class="pun">}</span></pre>
+</div>
+
+
+ </div>
+ </div>
+
+
+
+ <div class="artifactcomment">
+
+
+
+
+
+
+ <span class="author">Comment
+
+ by
+
+
+
+ <a class="userlink" href="https://code.google.com/u/113801981588238309041/">haghdo...@gmail.com</a>,
+
+ </span>
+ <span class="date" title="Fri Mar 15 22:05:23 2013">Mar 15, 2013</span>
+ <div>
+<div class="commentcontent">
+<p>Thanks for your grate article. There is a compilation error in this line num_events = io_getevents(ioctx<i>, min_nr, FLAGS_max_nr, events.get(),&amp;timeout); </i></p><p>it should be something like this num_events = io_getevents(ioctx<i>, min_nr, FLAGS_max_nr, events,&amp;timeout); </i></p>
+</div>
+
+
+ </div>
+ </div>
+
+
+
+ <div class="artifactcomment">
+
+
+
+
+
+
+ <span class="author">Comment
+
+ by
+
+
+
+ <a class="userlink" href="https://code.google.com/u/108474981592634289690/">garethb...@gmail.com</a>,
+
+ </span>
+ <span class="date" title="Thu Jun 13 08:56:09 2013">Jun 13, 2013</span>
+ <div>
+<div class="commentcontent">
+<p>It seems the posix_memalign is not actually required, is there some sort of performance benefit to be had by using it?? </p>
+</div>
+
+
+ </div>
+ </div>
+
+
+
+ <div class="artifactcomment">
+
+
+
+
+
+
+ <span class="author">Comment
+
+ by
+
+
+
+ <a class="userlink" href="https://code.google.com/u/103724282630885316886/">vishn...@gmail.com</a>,
+
+ </span>
+ <span class="date" title="Fri Aug 9 15:26:44 2013">Aug 9, 2013</span>
+ <div>
+<div class="commentcontent">
+<p>The posix_memalign is required and O_DIRECT is required. If I don't align my buffers to PAGE_SIZE, kernel 2.6.32 and 3.3 and 3.5.0-37-generic return -EINVAL in event.res, but event.res2 will be zero. </p>
+</div>
+
+
+ </div>
+ </div>
+
+
+
+ <div class="artifactcomment">
+
+
+
+
+
+
+ <span class="author">Comment
+
+ by
+
+
+
+ <a class="userlink" href="https://code.google.com/u/103724282630885316886/">vishn...@gmail.com</a>,
+
+ </span>
+ <span class="date" title="Fri Aug 9 17:52:29 2013">Aug 9, 2013</span>
+ <div>
+<div class="commentcontent">
+<p>Actually it's probably O_DIRECT that requires buffer length to be aligned to 512B and offset to be also aligned to 512B. </p>
+</div>
+
+
+ </div>
+ </div>
+
+
+
+ <div class="artifactcomment">
+
+
+
+
+
+
+ <span class="author">Comment
+
+ by
+
+
+
+ <a class="userlink" href="https://code.google.com/u/104805717887225221125/">nepor...@gmail.com</a>,
+
+ </span>
+ <span class="date" title="Wed Nov 6 07:35:43 2013">Nov 6, 2013</span>
+ <div>
+<div class="commentcontent">
+<p>Linux 3.12 has a solution to blocking io_submit() on regular files - see <a href="http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7869a4a6c5caa7b2e5c41ccaf46eb3371f88eea7" rel="nofollow">http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=7869a4a6c5caa7b2e5c41ccaf46eb3371f88eea7</a> </p>
+</div>
+
+
+ </div>
+ </div>
+
+
+
+ <div class="artifactcomment">
+
+
+
+
+
+
+ <span class="author">Comment
+
+ by project member
+
+
+
+ <a class="userlink" href="https://code.google.com/u/111678707898441125339/">dehrenb...@google.com</a>,
+
+ </span>
+ <span class="date" title="Mon Apr 21 08:32:41 2014">Apr 21, 2014</span>
+ <div>
+<div class="commentcontent">
+<p>The sample code here is released into the public domain. So feel free to copy it into whatever program you want to write. </p>
+</div>
+
+
+ </div>
+ </div>
+
+
+
+ <div class="artifactcomment">
+
+
+
+
+
+
+ <span class="author">Comment
+
+ by
+
+
+
+
+ <span class="userlink">dehrenb...@chromium.org</span>,
+
+ </span>
+ <span class="date" title="Tue Jul 22 09:58:03 2014">Jul 22, 2014</span>
+ <div>
+<div class="commentcontent">
+<p>haghdo..., thanks, I've fixed the code based on your comment. </p><p>vishn..., that's right, the alignment restriction is actually based on the backing device (for ext4 at least)--if you're using a device with 4k blocks, 4k alignment is needed, otherwise your direct I/O doesn't work. </p><p>una..., that's right, you can't use epoll directly on the file; instead, you have to use it on the ioctx with the io_set_eventfd syscall. </p>
+</div>
+
+
+ </div>
+ </div>
+
+
+</div>
+</div>
+
+
+
+
+ <script type="text/javascript">
+ function delComment(sequence_num, create_time, delete_mode) {
+ var f = document.forms["delcom"];
+ f.sequence_num.value = sequence_num;
+ f.create_time.value = create_time;
+ f.mode.value = delete_mode;
+ f.submit();
+ return false;
+ }
+ </script>
+
+
+ </div>
+
+
+
+
+
+ <div id="commentform">
+ <form action="https://code.google.com/p/kernel/w/detail.do" method="post">
+ <table>
+ <tbody><tr><td class="vt">
+ <input type="hidden" name="pagename" value="AIOUserGuide">
+ <input type="hidden" name="token" value="ABZ6GAciBR7wXJANw0lEk1JMtMA3vIzrjA:1408361093458">
+ <div class="graytext" style="float: right;">
+ Hint: You can use <a href="http://code.google.com/p/support/wiki/WikiSyntax">Wiki Syntax.</a>
+ </div>
+ <div>Enter a comment:</div>
+ <textarea name="content" rows="6" cols="100"></textarea><br><br>
+ <input type="submit" name="submit" value="Submit">
+ </td>
+ </tr></tbody></table>
+ </form>
+ </div>
+
+
+
+
+
+ <form name="delcom" action="https://code.google.com/p/kernel/w/delComment.do" method="POST">
+ <input type="hidden" name="sequence_num" value="">
+ <input type="hidden" name="create_time" value="">
+ <input type="hidden" name="mode" value="">
+ <input type="hidden" name="pagename" value="AIOUserGuide">
+ <input type="hidden" name="token" value="ABZ6GAciBR7wXJANw0lEk1JMtMA3vIzrjA:1408361093458">
+ </form>
+
+
+ <script src="./AIOUserGuide_files/prettify.js"></script>
+ <script type="text/javascript">
+ prettyPrint();
+ </script>
+
+<script type="text/javascript" src="./AIOUserGuide_files/dit_scripts.js"></script>
+
+
+
+
+
+
+ <script type="text/javascript" src="./AIOUserGuide_files/ph_core.js"></script>
+
+ <script type="text/javascript" src="./AIOUserGuide_files/ph_dwiki.js"></script>
+
+
+
+
+</div>
+
+<div id="footer" dir="ltr">
+ <div class="text">
+ <a href="https://code.google.com/projecthosting/terms.html">Terms</a> -
+ <a href="http://www.google.com/privacy.html">Privacy</a> -
+ <a href="https://code.google.com/p/support/">Project Hosting Help</a>
+ </div>
+</div>
+ <div class="hostedBy" style="margin-top: -20px;">
+ <span style="vertical-align: top;">Powered by <a href="http://code.google.com/projecthosting/">Google Project Hosting</a></span>
+ </div>
+
+
+
+
+
+
+
+
+
+
+<div class="menuDiv instance0" id="menuDiv-projects-dropdown" style="display: none;"><div class="menuCategory default"></div><b class="categoryTitle projects" style="display: block;">Projects</b><div class="menuCategory projects"><a class="menuItem" href="https://code.google.com/p/easyshop-for-plone/" style="display: block;">easyshop-for-plone</a></div><b class="categoryTitle starred_projects" style="display: block;">Starred projects</b><div class="menuCategory starred_projects"><a class="menuItem" href="https://code.google.com/p/easyshop-for-plone/" style="display: block;">easyshop-for-plone</a></div><div class="menuCategory controls"><hr class="menuSeparator"><a class="menuItem" href="https://code.google.com/hosting/" style="display: block;">Find open source projects...</a><a class="menuItem" href="https://code.google.com/hosting/createProject" style="display: block;">Create a project...</a></div></div><div class="menuDiv instance1" id="menuDiv-multilogin-dropdown" style="display: none;"><div class="menuCategory default"><span class="menuText" style="display: block;"><b>nialldouglas14@gmail.com</b></span></div><div class="menuCategory controls"><hr class="menuSeparator"><a class="menuItem" href="http://www.google.com/accounts/AddSession?service=code&continue=https%3A%2F%2Fcode.google.com%2Fp%2Fkernel%2Fwiki%2FAIOUserGuide" style="display: block;"><nobr>Sign in with another account...</nobr></a></div></div></body></html> \ No newline at end of file
diff --git a/reference/Linux KAIO/linux-kaio.txt b/reference/Linux KAIO/linux-kaio.txt
new file mode 100644
index 00000000..8497b468
--- /dev/null
+++ b/reference/Linux KAIO/linux-kaio.txt
@@ -0,0 +1,552 @@
+Linux Asynchronous I/O Explained (Last updated: 13 Apr 2012)
+*******************************************************************************
+ by Vasily Tarasov <tarasov AT vasily dot name>
+
+Asynchronoes I/O (AIO) is a method for performing I/O operations so that the
+process that issued an I/O request is not blocked till the data is available.
+Instead, after an I/O request is submitted, the process continues to execute
+its code and can later check the status of the submitted request.
+
+Linux kernel provides only *5* system calls for performing asynchronoes I/O.
+Other AIO functions commonly descibed in the literature are implemented in the
+user space libraries and use the system calls internally. Some libraries can
+also emulate AIO functionality entirely in the user space without any kernel
+support.
+
+There are two main libraries in Linux that facilitate AIO, we will refer to
+them as *libaio* and *librt* (the latter one is a part of libc).
+
+In this text, I first discuss system calls, then libaio, and finaly librt.
+
+AIO System Calls
+*******************************************************************************
+ based on Linux 3.2.1 kernel
+
+AIO system call entry points are located in "fs/aio.c" file in the kernel's
+source code. Types and constants exported to the user space reside in
+"/usr/include/linux/aio_abi.h" header file.
+
+There are only 5 AIO system calls:
+
+* int io_setup(unsigned nr_events, aio_context_t *ctxp);
+
+* int io_destroy(aio_context_t ctx);
+
+* int io_submit(aio_context_t ctx, long nr, struct iocb *cbp[]);
+
+* int io_cancel(aio_context_t ctx, struct iocb *, struct io_event *result);
+
+* int io_getevents(aio_context_t ctx, long min_nr, long nr,
+ struct io_event *events, struct timespec *timeout);
+
+I will demonstrate the usage of these system calls using a sequence of programs
+in the increasing order of their complexity.
+
+Program 1:
+
+>> snip start: 1.c >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+00 #define _GNU_SOURCE /* syscall() is not POSIX */
+01
+02 #include <stdio.h> /* for perror() */
+03 #include <unistd.h> /* for syscall() */
+04 #include <sys/syscall.h> /* for __NR_* definitions */
+05 #include <linux/aio_abi.h> /* for AIO types and constants */
+06
+07 inline int io_setup(unsigned nr, aio_context_t *ctxp)
+08 {
+09 return syscall(__NR_io_setup, nr, ctxp);
+10 }
+11
+12 inline int io_destroy(aio_context_t ctx)
+13 {
+14 return syscall(__NR_io_destroy, ctx);
+15 }
+16
+17 int main()
+18 {
+19 aio_context_t ctx;
+20 int ret;
+21
+22 ctx = 0;
+23
+24 ret = io_setup(128, &ctx);
+25 if (ret < 0) {
+26 perror("io_setup error");
+27 return -1;
+28 }
+29
+30 ret = io_destroy(ctx);
+31 if (ret < 0) {
+32 perror("io_destroy error");
+33 return -1;
+34 }
+35
+36 return 0;
+37 }
+
+<< snip end: 1.c <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+
+For now, ignore first 17 lines of the code and look at main() function. In line
+24 we call io_setup() system call to create so called "AIO context" in the
+kernel. AIO context is a set of data structures that the kernel supports to
+perform AIO. Every process can have multiple AIO contextes and as such one
+needs an identificator for every AIO context in a process (XXX: come up with a
+handy example how it can be used). Ctx variable of type aio_context_t defined in
+line 19 stores such an identificator in our example. A pointer to ctx variable
+is passed to io_setup() as a second argument and kernel fills this variable
+with a context identifier. Interestingly, aio_context_t is actually just an
+unsigned long defined in the kernel ("linux/aio_abi.h") like that:
+
+typedef unsigned long aio_context_t;
+
+In line 22 we set ctx to 0 which is required by kernel or io_setup() fails with
+-EINVAL error.
+
+The first argument of io_setup() function - 128 in our case - is the maximum
+number of requests that can simultaneously reside in the context. This will be
+explained in more details in the next examples.
+
+In line 30 we destroy just created AIO context by calling io_destroy() system
+call with ctx as an argument.
+
+The lines above 17 are just helpers that allow to call system calls directly. We
+use glibc's syscall() function that invokes any system call by its number. It
+is only required if one wants to call system calls directly without using AIO
+libraries' wrapper functions (provided by libaio and librt). Notice, that
+syscall() functions's return value follows the usual conventions for indicating
+an error: -1, with errno set to a positive value that indicates the error.
+So, we check if the values returned by io_setup() and io_destroy() are less than
+zero to detect the error, and then use perror() function that will print the
+errno.
+
+In the last example we did a minimal thing: created an AIO context and then
+destroyed it immediatelly. In the next example we submit one request to the
+context and then query its status later.
+
+Program 2:
+
+>> snip start: 2.c >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
+
+00 #define _GNU_SOURCE /* syscall() is not POSIX */
+01
+02 #include <stdio.h> /* for perror() */
+03 #include <unistd.h> /* for syscall() */
+04 #include <sys/syscall.h> /* for __NR_* definitions */
+05 #include <linux/aio_abi.h> /* for AIO types and constants */
+06 #include <fcntl.h> /* O_RDWR */
+07 #include <string.h> /* memset() */
+08 #include <inttypes.h> /* uint64_t */
+09
+10 inline int io_setup(unsigned nr, aio_context_t *ctxp)
+11 {
+12 return syscall(__NR_io_setup, nr, ctxp);
+13 }
+14
+15 inline int io_destroy(aio_context_t ctx)
+16 {
+17 return syscall(__NR_io_destroy, ctx);
+18 }
+19
+20 inline int io_submit(aio_context_t ctx, long nr, struct iocb **iocbpp)
+21 {
+22 return syscall(__NR_io_submit, ctx, nr, iocbpp);
+23 }
+24
+25 inline int io_getevents(aio_context_t ctx, long min_nr, long max_nr,
+26 struct io_event *events, struct timespec *timeout)
+27 {
+28 return syscall(__NR_io_getevents, ctx, min_nr, max_nr, events, timeout);
+29 }
+30
+31 int main()
+32 {
+33 aio_context_t ctx;
+34 struct iocb cb;
+35 struct iocb *cbs[1];
+36 char data[4096];
+37 struct io_event events[1];
+38 int ret;
+39 int fd;
+40
+41 fd = open("/tmp/testfile", O_RDWR | O_CREAT);
+42 if (fd < 0) {
+43 perror("open error");
+44 return -1;
+45 }
+46
+47 ctx = 0;
+48
+49 ret = io_setup(128, &ctx);
+50 if (ret < 0) {
+51 perror("io_setup error");
+52 return -1;
+53 }
+54
+55 /* setup I/O control block */
+56 memset(&cb, 0, sizeof(cb));
+57 cb.aio_fildes = fd;
+58 cb.aio_lio_opcode = IOCB_CMD_PWRITE;
+59
+60 /* command-specific options */
+61 cb.aio_buf = (uint64_t)data;
+62 cb.aio_offset = 0;
+63 cb.aio_nbytes = 4096;
+64
+65 cbs[0] = &cb;
+66
+67 ret = io_submit(ctx, 1, cbs);
+68 if (ret != 1) {
+69 if (ret < 0)
+70 perror("io_submit error");
+71 else
+72 fprintf(stderr, "could not sumbit IOs");
+73 return -1;
+74 }
+75
+76 /* get the reply */
+77 ret = io_getevents(ctx, 1, 1, events, NULL);
+78 printf("%d\n", ret);
+79
+80 ret = io_destroy(ctx);
+81 if (ret < 0) {
+82 perror("io_destroy error");
+83 return -1;
+84 }
+85
+86 return 0;
+87 }
+
+<< snip end: 2.c <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+
+Every I/O request that is submitted to an AIO context is represented by an I/O
+control block structure - struct iocb - declared in line 34. We initialize this
+structure in lines 55-63. First, the whole structure is zeroed, then file
+descriptor (aio_fildes) and command (aio_lio_opcode) fields are set.
+
+File descriptor corresponds to a previously opened file, in our example we
+open "/tmp/testfile" file in line 41.
+
+AIO commands currently supported by Linux kernel are:
+
+IOCB_CMD_PREAD
+ positioned read; corresponds to pread() system call.
+
+IOCB_CMD_PWRITE
+ positioned write; corresponds to pwrite() system call.
+
+IOCB_CMD_FSYNC
+ sync file's data and metadata with disk; corresponds to fsync() system call.
+
+IOCB_CMD_FDSYNC
+ sync file's data and metadata with disk, but only metadata needed to access
+ modified file data is written; corresponds to fdatasync() system call.
+
+IOCB_CMD_PREADV
+ vectored positioned read, sometimes called "scattered input";
+ corresponds to pread() system call.
+
+IOCB_CMD_PWRITEV
+ vectored positioned write, sometimes called "gathered output";
+ corresponds to pwrite() system call.
+
+IOCB_CMD_NOOP
+ defined in the header file, but is not used anywhere else in the kernel.
+
+The semantics of other fields in the iocb structure depends on the command
+specified. For now, we will limit our discussion to IOCB_CMD_PREAD and
+IOCB_CMD_PWRITE commands. After understanding AIO interface for these two
+commands, we will look into the remaining ones.
+
+In lines 60-63 of our running example we set command-specific fields of iocb
+structure: aio_buf and aio_nbytes corresond to a region in memory to which
+data should be read or written to; aio_offset is an absolute offset in a file.
+
+Now, when one I/O control block is ready, we put a pointer to it in an array
+(line 65) and then pass this array to the io_submit() system call (line 67).
+io_submit() takes AIO context ID, size of the array and the array itself as the
+arguments. Notice, that array should contain *pointers* to the iocb structures,
+not the structures themself.
+
+io_submit()'s return code can be one of the following values:
+
+A) ret = (number of iocbs sumbmitted)
+ Ideal case, all iocbs were accepted for processing.
+
+B) 0 < ret < (number of iocbs sumbmitted)
+ io_submit() system call processes iocbs one by one starting from
+ the first entry in the passed array. If submission of some iocb fails,
+ it stops at this point and returns the index of iocb that failed.
+ There is no way to know what is the exact reason of a failure.
+ However, if the very first iocb submission fails, see point C.
+
+C) ret < 0
+ There are two reasons why this could happen:
+ 1) Some error happened even before io_submit() started to iterate
+ over iocbs in the array (e.g., AIO context was invalid).
+ 2) The submission of the very first iocb (cbx[0]) failed).
+
+So, in our example, we handle io_submit()'s return code in an unusual way. If
+return code is not equal to the number of iocbs, then that is a clear error but
+we don't know its reason (errno is not set). Consequently, we use
+fprintf(stderr, ...) function to print error notification on the screen.
+Otherwise, if return code is less than zero, then we know the error (errno is
+set) and use perror() function instead. Notice, that in case of a single iocb
+in the array (as in our example) such a complex error handling makes less sense:
+if the first (and only) iocb fails, we are guaranteed to get an error
+information (see point C above). We handle error in a more complex way in this
+example only to reuse the same code later, when we submit multiple iocbs in a
+single io_submit() call.
+
+After iocb is submitted we can perform any other actions without waiting for I/O
+to complete. For every completed I/O request (successfully or unsuccessfully)
+kernel creates an io_event structure. To obtain the list of io_events (and
+consequently all completed iocbs) io_getevent() system call should be used (line
+77). When calling io_getevents(), one needs to specify:
+
+a) which AIO context to get events from (ctx variable)
+
+b) a buffer where the kernel should load events to (events varaiable)
+
+c) minimal number of events one wants to get (first 1 in our program).
+ If less then this number of iocbs are currently completed,
+ io_getevents() will block till enough events appear. See point e)
+ for more details on how to control blocking time.
+
+d) maximum number of events one wants to get. This usually is
+ the size of the events buffer (second 1 in our program)
+
+e) If not enough events are available, we don't want to wait forever.
+ One can specify a relative deadline as the last argument.
+ NULL in this case means to wait infinitely.
+ If one wants io_getevents() not to block at all then
+ timespec timeout structure need to be initialzed to zero
+ seconds and zero nanoseconds.
+
+The return code of io_getevents can be:
+
+A) ret = (max number of events)
+ All events that fit in the user provided buffer were obtained
+ from the kernel. There might be more pending events in the kernel.
+B) (min number of events) <= ret <= (max number of events)
+ All currently available events were read from the kernel and no
+ blocking happened.
+C) 0 < ret < (min number of events)
+ All currently available events were read from the kernel and
+ we blocked to wait for the time user has specified.
+E) ret = 0
+ no events are available XXX:? does blocking happen in this case?..
+
+F) ret < 0
+ an error happened
+
+
+TO BE CONTINUED...
+
+
+/proc/sys/fs/aio-max-nr
+/proc/sys/fs/aio-nr
+
+Note that timeout is relative and will be updated if not NULL and the operation
+blocks
+
+Check how vectors a provide to vectored PREADV and PWRITEV commands.
+
+Other fields to fill/explain:
+
+ /* these are internal to the kernel/libc. */
+ __u64 aio_data; /* data to be returned in event's data */
+ __u32 PADDED(aio_key, aio_reserved1);
+ /* the kernel sets aio_key to the req # */
+
+ /* common fields */
++++ __u16 aio_lio_opcode; /* see IOCB_CMD_ above */
+ __s16 aio_reqprio;
+ __u32 aio_fildes;
+
+ __u64 aio_buf;
+ __u64 aio_nbytes;
+ __s64 aio_offset;
+
+ /* extra parameters */
+ __u64 aio_reserved2; /* TODO: use this for a (struct sigevent *) */
+
+ /* flags for the "struct iocb" */
+ __u32 aio_flags;
+
+ /*
+ * if the IOCB_FLAG_RESFD flag of "aio_flags" is set, this is an
+ * eventfd to signal AIO readiness to
+ */
+ __u32 aio_resfd;
+
+*** SYNC RELATED COMMANDS ***
+IOCB_CMD_FSYNC
+ sync file's data and metadata with disk; corresponds to fsync() system call.
+
+IOCB_CMD_FDSYNC
+ sync file's data and metadata with disk, but only metadata needed to access
+ modified file data is written; corresponds to fdatasync() system call.
+
+
+*** VECTORED INPUT and OUTPUT ***
+IOCB_CMD_PREADV
+ vectored positioned read, sometimes called "scattered input";
+ corresponds to pread() system call.
+
+IOCB_CMD_PWRITEV
+ vectored positioned write, sometimes called "gathered output";
+ corresponds to pwrite() system call.
+
+*** OTHER COMMANDS ***
+IOCB_CMD_NOOP
+ defined in the header file, but is not used anywhere else in the kernel.
+
+XXX: May be discass Poll and other semi-existing commands here?...
+
+*********************************************************
+********************* LIBAIO LIBRARY ********************
+*********************************************************
+
+libaio:
+/lib64/libaio.so.1 (shared library)
+
+libaio-devel:
+/usr/include/libaio.h (header library)
+/usr/lib64/libaio.a (static library)
+
+Functions:
+
+a) Actual system call wrappers:
+
+int io_setup(int maxevents, io_context_t *ctxp);
+int io_destroy(io_context_t ctx);
+int io_submit(io_context_t ctx, long nr, struct iocb *ios[]);
+int io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
+io_getevents(io_context_t ctx_id, long min_nr, long nr, struct io_event *events, struct timespec *timeout);
+
+io_context_t is a pointer to an non-existing stucture:
+
+typedef struct io_context *io_context_t;
+
+Not a single line of code in any user tool or in the libaio library looks at the
+members of 'struct io_context'. So, gcc happily compiles the code even though
+struct io_context is not defined. This structure is probably defined just for
+type checking. The rule of thumb when using libaio is just to declare all
+variables as io_context_t and forget that it actually is a pointer!
+
+b) Convenient macroses:
+
+static inline void io_prep_pread(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
+static inline void io_prep_pwrite(struct iocb *iocb, int fd, void *buf, size_t count, long long offset)
+static inline void io_prep_preadv(struct iocb *iocb, int fd, const struct iovec *iov, int iovcnt, long long offset)
+static inline void io_prep_pwritev(struct iocb *iocb, int fd, const struct iovec *iov, int iovcnt, long long offset)
+
+static inline void io_prep_poll(struct iocb *iocb, int fd, int events)
+static inline void io_prep_fsync(struct iocb *iocb, int fd)
+static inline void io_prep_fdsync(struct iocb *iocb, int fd)
+
+static inline int io_poll(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd, int events)
+static inline int io_fsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd)
+static inline int io_fdsync(io_context_t ctx, struct iocb *iocb, io_callback_t cb, int fd)
+
+static inline void io_set_eventfd(struct iocb *iocb, int eventfd);
+
+*********************************************************
+******** MATCHING LIBAIO AND KERNEL INTERFACE ***********
+*********************************************************
+
+libaio.h redefines some of the kernel definitions (god know why),
+but they match at the binary level. E.g., this is kernel
+exported definition of iocb:
+
+struct iocb {
+ /* these are internal to the kernel/libc. */
+ __u64 aio_data; /* data to be returned in event's data */
+ __u32 PADDED(aio_key, aio_reserved1);
+ /* the kernel sets aio_key to the req # */
+
+ /* common fields */
+ __u16 aio_lio_opcode; /* see IOCB_CMD_ above */
+ __s16 aio_reqprio;
+ __u32 aio_fildes;
+
+ __u64 aio_buf;
+ __u64 aio_nbytes;
+ __s64 aio_offset;
+
+ /* extra parameters */
+ __u64 aio_reserved2; /* TODO: use this for a (struct sigevent *) */
+
+ /* flags for the "struct iocb" */
+ __u32 aio_flags;
+
+ /*
+ * if the IOCB_FLAG_RESFD flag of "aio_flags" is set, this is an
+ * eventfd to signal AIO readiness to
+ */
+ __u32 aio_resfd;
+}; /* 64 bytes */
+
+And this is definition of iocb by libaio.h:
+
+struct io_iocb_common {
+ PADDEDptr(void *buf, __pad1);
+ PADDEDul(nbytes, __pad2);
+ long long offset;
+ long long __pad3;
+ unsigned flags;
+ unsigned resfd;
+}; /* result code is the amount read or -'ve errno */
+
+
+struct iocb {
+ PADDEDptr(void *data, __pad1); /* Return in the io completion event */
+ PADDED(unsigned key, __pad2); /* For use in identifying io requests */
+
+ short aio_lio_opcode;
+ short aio_reqprio;
+ int aio_fildes;
+
+ union {
+ struct io_iocb_common c;
+ struct io_iocb_vector v;
+ struct io_iocb_poll poll;
+ struct io_iocb_sockaddr saddr;
+ } u;
+};
+
+
+
+
+****** AIO LIBRARY *****
+
+glibc:
+/lib64/librt.so.1
+
+glibc-headers:
+/usr/include/aio.h
+
+Provide POSIX-defined interface for async I/O.
+
+aio_read()
+aio_write()
+aio_cancel()
+aio_error()
+aio_fsync()
+aio_suspend()
+aio_return()
+
+lio_listio
+
+
+****** To discover ****
+XXX: see if these are implemented in some other kernels:
+/* These two are experimental.
+ * IOCB_CMD_PREADX = 4,
+ * IOCB_CMD_POLL = 5,
+ */
+XXX: potential resubmittion of the wrong iocb, knowing its index.
+XXX: two AIO contextes per process?
+
+
diff --git a/reference/iron-sosp05.pdf b/reference/iron-sosp05.pdf
new file mode 100644
index 00000000..0359a0ee
--- /dev/null
+++ b/reference/iron-sosp05.pdf
Binary files differ
diff --git a/reference/osdi14-paper-pillai.pdf b/reference/osdi14-paper-pillai.pdf
new file mode 100644
index 00000000..91d27814
--- /dev/null
+++ b/reference/osdi14-paper-pillai.pdf
Binary files differ
diff --git a/release_notes.md b/release_notes.md
new file mode 100644
index 00000000..42d9fbfe
--- /dev/null
+++ b/release_notes.md
@@ -0,0 +1,40 @@
+Herein lies the beginnings of the proposed Boost.AFIO v2 post-peer-review rewrite.
+
+\note Note that this code is so early alpha that no test code, let alone unit test code, exists
+yet.
+
+## Architecture and design:
+
+| NEW in v2 | Boost peer review feedback | |
+| --------- | -------------------------- | --- |
+| ✔ | ✔ | Universal native handle/fd abstraction instead of `void *`.
+| ✔ | ✔ | Perfectly/Ideally low memory allocation per op (usually none).
+| ✔ | ✔ | noexcept API throughout returning error_code for failure instead of throwing exceptions.
+| ✔ | ✔ | AFIO v1 handle type split into hierarchy of types:<ol><li>handle - provides open, close, get path, clone, set/unset append only, change caching, characteristics<li>io_handle - adds synchronous scatter-gather i/o<li>file_handle - adds open/create file, get and set maximum extent<li>async_file_handle - adds asynchronous scatter-gather i/o</ol>
+| ✔ | ✔ | Cancelable i/o (made possible thanks to dropping XP support).
+| ✔ | ✔ | All shared_ptr usage removed as all use of multiple threads removed.
+| ✔ | ✔ | Use of std::vector to transport scatter-gather sequences replaced with C++ 17 `span<>`.
+| ✔ | ✔ | Completion callbacks are now some arbitrary type `U&&` instead of a future continuation. Type erasure for its storage is bound into the one single memory allocation for everything needed to execute the op, and so therefore overhead is optimal.
+| ✔ | | Abstraction of native handle management via bitfield specified "characteristics".
+| ✔ | | Storage profiles, a YAML database of behaviours of hardware, OS and filing system combinations.
+| ✔ | | Absolute and interval deadline timed i/o throughout (made possible thanks to dropping XP support).
+| ✔ | | Dependency on ASIO/Networking TS removed completely.
+
+
+## Features implemented:
+
+| NEW in v2 | Windows | POSIX | |
+| --------- | --------| ----- | --- |
+| ✔ | ✔ | ✔ | Native handle cloning.
+| ✔ (up from four) | ✔ | ✔ | Maximum possible (seven) forms of kernel caching.
+| | ✔ | ✔ | Absolute path open.
+| | | | Relative path open.
+| ✔ | ✔ | | Win32 path support (260 path limit).
+| | | | NT kernel path support (32,768 path limit).
+| ✔ | ✔ | ✔ | Synchronous universal scatter-gather i/o.
+| ✔ (POSIX AIO support) | ✔ | ✔ | Asynchronous universal scatter-gather i/o.
+| ✔ | ✔ | ✔ | i/o cancellation.
+| | ✔ | ✔ | Retrieving and setting the current maximum extent (size) of an open file.
+| | ✔ | ✔ | statfs_t ported over from AFIO v1.
+| | ✔ | ✔ | utils namespace ported over from AFIO v1.
+
diff --git a/scripts/AddTryItNow.py b/scripts/AddTryItNow.py
new file mode 100644
index 00000000..1bd03f07
--- /dev/null
+++ b/scripts/AddTryItNow.py
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+import glob, os, sys
+
+if not os.path.exists("html/afio/try_it_now.html"):
+ sys.exit(0)
+with open("html/afio/try_it_now.html", "rb") as ih:
+ try_it_now=ih.read()
+
+def patch(i):
+ with open(i, "r+b") as ih:
+ t=ih.read();
+ it1=0
+ while 1:
+ it1=t.find('<div class="spirit-nav">', it1)
+ if it1==-1: break
+ it1=it1+24
+ t=t[:it1]+try_it_now+t[it1:]
+ ih.seek(0)
+ ih.truncate(0)
+ ih.write(t)
+
+patch("html/afio.html")
+for i in glob.glob("html/afio/*.html"):
+ patch(i)
+for i in glob.glob("html/afio/*/*.html"):
+ patch(i)
+for i in glob.glob("html/afio/*/*/*.html"):
+ patch(i)
diff --git a/scripts/BuildTimes.py b/scripts/BuildTimes.py
new file mode 100755
index 00000000..88f2b9ec
--- /dev/null
+++ b/scripts/BuildTimes.py
@@ -0,0 +1,87 @@
+#!/usr/bin/python3
+# Calculate boost.afio build times under various configs
+# (C) 2015 Niall Douglas
+# Created: 12th March 2015
+#[ [`--link-test --fast-build debug`][][[footnote ASIO has a link error without `link=static`]][fails]]
+#[ [`--link-test debug`][][][]]
+#[ [`--link-test --lto debug`][[]][][]]
+#[ [`--link-test pch=off debug`][][][]]
+#[[`--link-test --fast-build release`][][[footnote ASIO has a link error without `link=static`]][fails]]
+#[ [`--link-test release`][][][]]
+#[ [`--link-test --lto release`][][][]]
+
+import os, sys, subprocess, time, shutil, platform
+
+if len(sys.argv)<2:
+ print("Usage: "+sys.argv[0]+" <toolset>", file=sys.stderr)
+ sys.exit(1)
+if not os.path.exists("b2") and not os.path.exists("b2.exe"):
+ print("ERROR: Need to run me from boost root directory please", file=sys.stderr)
+print(os.getcwd())
+shutil.rmtree("bin.v2", True)
+
+onWindows="Windows" in platform.system()
+
+configs=[
+ ["--c++14 --link-test --fast-build debug", None],
+ ["--c++14 --link-test debug", None],
+ ["--c++14 --link-test --lto debug", None],
+ ["--c++14 --link-test pch=off debug", None],
+ ["--c++14 --link-test --fast-build release", None],
+ ["--c++14 --link-test release", None],
+ ["--c++14 --link-test --lto release", None],
+ ["standalone_singleabi", None],
+ ["standalone_multiabi", None]
+]
+for config in configs:
+ print("\n\nConfig: "+config[0])
+ if config[0]=="standalone_singleabi" or config[0]=="standalone_multiabi":
+ if onWindows:
+ test_all="test_all.exe"
+ tocall="alltests_msvc.bat" if "msvc" in sys.argv[1] else "alltests_gcc.bat"
+ else:
+ test_all="test_all"
+ tocall="alltests_gcc.sh"
+ if config[0]=="standalone_singleabi":
+ tocall="standalone_"+tocall
+ else:
+ tocall="multiabi_"+tocall
+ basedir=os.getcwd()
+ env=dict(os.environ)
+ if not onWindows:
+ tocall="./"+tocall
+ env['CXX']=sys.argv[1]
+ env['CXX']=env['CXX'].replace('gcc', 'g++')
+ env['CXX']=env['CXX'].replace('clang', 'clang++')
+ try:
+ os.chdir("libs/afio")
+ shutil.rmtree(test_all, True)
+ if subprocess.call(tocall, env=env, shell=True):
+ config[1]="FAILED"
+ continue
+ shutil.rmtree(test_all, True)
+ print("\n\nStarting benchmark ...")
+ begin=time.perf_counter()
+ subprocess.call(tocall, env=env, shell=True)
+ end=time.perf_counter()
+ finally:
+ os.chdir(basedir)
+ else:
+ shutil.rmtree("bin.v2/libs/afio", True)
+ if subprocess.call([os.path.abspath("b2"), "toolset="+sys.argv[1], "libs/afio/test", "-j", "8"]+config[0].split(" ")):
+ config[1]="FAILED"
+ continue
+ shutil.rmtree("bin.v2/libs/afio", True)
+ print("\n\nStarting benchmark ...")
+ begin=time.perf_counter()
+ subprocess.call([os.path.abspath("b2"), "toolset="+sys.argv[1], "libs/afio/test"]+config[0].split(" "))
+ end=time.perf_counter()
+ mins=int((end-begin)/60)
+ secs=int((end-begin)%60);
+ config[1]="%dm%ss" % (mins, secs)
+ print("Config %s took %dm%ss" % (config[0], mins, secs))
+
+print("\n\n")
+for config in configs:
+ print(config)
+ \ No newline at end of file
diff --git a/scripts/GenJenkinsMatrixDashboard.py b/scripts/GenJenkinsMatrixDashboard.py
new file mode 100755
index 00000000..5f87507b
--- /dev/null
+++ b/scripts/GenJenkinsMatrixDashboard.py
@@ -0,0 +1,96 @@
+#!/usr/bin/python3
+
+inputs=[
+ ((2, "Android 5.0", "clang 3.5", "libc++", "x86"), {"CPPSTD":["c++11"], "CXX":["g++-4.8"], "LINKTYPE":["standalone"], "label":"android-ndk"}),
+ ((0, "Android 5.0", "GCC 4.8", "libc++", "x86"), {"CPPSTD":["c++11"], "CXX":["g++-4.8"], "LINKTYPE":["standalone"], "label":"android-ndk"}),
+ ((1, "FreeBSD 10.1 on ZFS", "clang 3.4", "libc++", "x86"), {"CPPSTD":["c++11"], "CXX":["clang++-3.3"], "label":"freebsd10-clang3.3"}),
+
+ ((2, "Ubuntu Linux 12.04 LTS", "clang 3.3", "libstdc++ 4.8", "x86"), {"CPPSTD":["c++11"], "CXX":["clang++-3.3"], "label":"linux-gcc-clang"}),
+ ((0, "Ubuntu Linux 12.04 LTS", "clang 3.4", "libstdc++ 4.8", "x86"), {"CPPSTD":["c++11"], "CXX":["clang++-3.4"], "label":"linux-gcc-clang"}),
+
+ ((8, "Ubuntu Linux 14.04 LTS", "clang 3.5", "libstdc++ 4.9", "x64"), {"CPPSTD":["c++11"], "CXX":["clang++-3.5"], "label":"linux64-gcc-clang"}),
+ ((0, "Ubuntu Linux 14.04 LTS", "clang 3.5", "libstdc++ 4.9", "ARMv7"), {"CPPSTD":["c++11"], "CXX":["clang++-3.5"], "label":"arm-gcc-clang"}),
+ ((0, "Ubuntu Linux 14.04 LTS", "clang 3.6", "libstdc++ 4.9", "x64"), {"CXX":["clang++-3.6"], "label":"linux64-gcc-clang"}),
+ ((0, "Ubuntu Linux 14.04 LTS", "clang 3.7", "libstdc++ 4.9", "x64"), {"CXX":["clang++-3.7"], "label":"linux64-gcc-clang"}),
+ ((0, "Ubuntu Linux 14.04 LTS", "GCC 4.8", "libstdc++ 4.8", "x64"), {"CPPSTD":["c++11"], "CXX":["g++-4.8"], "label":"linux64-gcc-clang"}),
+ ((0, "Ubuntu Linux 14.04 LTS", "GCC 4.9", "libstdc++ 4.9", "x64"), {"CXX":["g++-4.9"], "label":"linux64-gcc-clang"}),
+ ((0, "Ubuntu Linux 14.04 LTS", "GCC 4.9", "libstdc++ 4.9", "ARMv7"), {"CXX":["g++-4.9"], "label":"arm-gcc-clang"}),
+ ((0, "Ubuntu Linux 14.04 LTS", "GCC 5.1", "libstdc++ 5.1", "x64"), {"CXX":["g++-5"], "label":"linux64-gcc-clang"}),
+
+ ((3, "Microsoft Windows 8.1", "Mingw-w64 GCC 4.8", "libstdc++ 4.8", "x64"), {"CPPSTD":["c++11"], "CXX":["mingw64"], "LINKTYPE":["static", "shared"], "label":"win8-msvc-mingw"}),
+ ((0, "Microsoft Windows 8.1", "Visual Studio 2015", "Dinkumware", "x64"), {"CPPSTD":["c++14"], "CXX":["msvc-14.0"], "label":"win8-msvc-mingw"}),
+]
+CPPSTDs=["c++11", "c++14"]
+CXXs=["g++-4.8", "g++-4.9", "g++-5"]
+LINKTYPEs=["static", "shared", "standalone"]
+
+print('''<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>VS2013</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>
+''')
+
+# <a href='https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=static,label=android-ndk/'><img src='https://ci.nedprod.com/job/Boost.AFIO%20Build/CPPSTD=c++11,CXX=g++-4.8,LINKTYPE=static,label=android-ndk/badge/icon'></a>
+
+for line, items in inputs:
+ if line[0]==0:
+ line=("",)+line[2:]
+ else:
+ line=('<td rowspan="'+str(line[0])+'">'+line[1]+'</td>',)+line[2:]
+ print('<tr align="center">%s<td>%s</td><td>%s</td><td>%s</td><td>' % line)
+ label=items["label"]
+ cppstds=items["CPPSTD"] if "CPPSTD" in items else CPPSTDs
+ cxxs=items["CXX"] if "CXX" in items else CXXs
+ linktypes=items["LINKTYPE"] if "LINKTYPE" in items else LINKTYPEs
+ for cppstd in cppstds:
+ for cxx in cxxs:
+ for linktype in linktypes:
+ print(' <div><a href="https://ci.nedprod.com/job/Boost.AFIO%%20Build/CPPSTD=%s,CXX=%s,LINKTYPE=%s,label=%s/"><img src="https://ci.nedprod.com/job/Boost.AFIO%%20Build/CPPSTD=%s,CXX=%s,LINKTYPE=%s,label=%s/badge/icon" /></a></div>' % (cppstd, cxx, linktype, label, cppstd, cxx, linktype, label))
+ print('</td><td>')
+ for cppstd in cppstds:
+ for cxx in cxxs:
+ for linktype in linktypes:
+ print(' <div><a href="https://ci.nedprod.com/job/Boost.AFIO%%20Test/CPPSTD=%s,CXX=%s,LINKTYPE=%s,label=%s/"><img src="https://ci.nedprod.com/job/Boost.AFIO%%20Test/CPPSTD=%s,CXX=%s,LINKTYPE=%s,label=%s/badge/icon" /></a></div>' % (cppstd, cxx, linktype, label, cppstd, cxx, linktype, label))
+ print('</td></tr>')
+
+print('''</table>
+
+</center>
+''')
diff --git a/scripts/JenkinsMatrixToDashboard.py b/scripts/JenkinsMatrixToDashboard.py
new file mode 100755
index 00000000..7ba67b7d
--- /dev/null
+++ b/scripts/JenkinsMatrixToDashboard.py
@@ -0,0 +1,83 @@
+#!/usr/bin/python3
+
+import sys
+from urllib.request import urlopen
+from lxml import etree
+from lxml.html import parse
+
+if len(sys.argv)<3:
+ print(sys.argv[0], "<output html file> <url>")
+ sys.exit(1)
+outfile=sys.argv[1]
+url=sys.argv[2]
+
+p=parse(urlopen(url)).getroot()
+p.make_links_absolute(url)
+matrix=p.xpath("//table[@id='configuration-matrix']")
+matrixrows=matrix[0].xpath("tr")
+
+# First <tr> is compilers, and colspan on first td is number of horizontal qualifiers
+# Second <tr> first <td> is each build target, and rowspan is number of vertical qualifiers
+columntocompiler=[]
+for x in matrixrows[0]:
+ for n in range(0, int(x.get("colspan"))):
+ columntocompiler.append(x.text)
+matrixrows=matrixrows[1:]
+print(columntocompiler)
+
+# Essentially we column collapse the matrix into HTML. Quite straightforward.
+class Platform:
+ def __init__(self, name):
+ self.name=name
+ self.rows=[]
+
+platforms=[]
+rowspan=0
+for row in matrixrows:
+ cols=row.xpath("td")
+ outrow=""
+ count=0
+ for x in range(0, len(cols)):
+ if x==0 and rowspan==0:
+ rowspan=int(cols[x].get("rowspan"))
+ platform=Platform(cols[x].text)
+ platforms.append(platform)
+ count+=0.5
+ col=cols[x]
+ atags=col.xpath("div/a")
+ imgs=col.xpath("div/img")
+ if len(atags)==0 and len(imgs)==0:
+ col.set('valign', 'top')
+ outrow+=etree.tostring(col).decode('utf-8')
+ elif len(atags)!=0 and len(imgs)==0:
+ # Format: <div><a class="model-link inside" href="CPPSTD=c++11,CXX=clang++-3.5,LINKTYPE=shared,label=linux64-gcc-clang/"><img height="24" alt="Success" width="24" src="/static/5585b9ed/images/24x24/blue.png" tooltip="Success" /></a></div>
+ #col=col[:]
+ # Replace img src with something dynamic
+ # Form: https://ci.nedprod.com/job/Maidsafe%20CRUX/CPPSTD=c++11,CXX=g++-4.9,LINKTYPE=shared,label=arm-gcc-clang/badge/icon
+ joburl=atags[0].get("href")
+ imgs=atags[0].xpath("img")
+ del imgs[0].attrib["width"]
+ del imgs[0].attrib["height"]
+ imgs[0].set('src', joburl+"badge/icon")
+ imgs[0].set('align', "top")
+ # Insert target name just inside end of div
+ col.getchildren()[-1].getchildren()[-1].tail=" "+columntocompiler[len(columntocompiler)-(len(cols)-x)]
+ outrow+=etree.tostring(col).decode('utf-8')
+ count+=1
+ if count==0.5:
+ platform.rows.append(outrow[:outrow.find("</td>")+5])
+ elif count:
+ platform.rows.append(outrow)
+ else:
+ platform.rows.append("<td/>")
+ rowspan-=1
+
+with open(outfile, 'wt') as oh:
+ oh.write('<html><body><table id="configuration-matrix" width="100%" border="1">\n')
+ for platform in platforms:
+ for y in range(0, len(platform.rows)):
+ oh.write(' <tr>\n')
+ oh.write(' '+platform.rows[y]+'\n')
+ oh.write(' </tr>\n')
+ oh.write('</table></body></html>\n')
+
diff --git a/scripts/SoakTest.py b/scripts/SoakTest.py
new file mode 100755
index 00000000..30dddc5f
--- /dev/null
+++ b/scripts/SoakTest.py
@@ -0,0 +1,16 @@
+#!/usr/bin/python
+# Repeatedly runs a process until it crashes
+# (C) 2014 Niall Douglas
+# File created: Feb 2014
+
+import subprocess, sys
+
+if len(sys.argv)<2:
+ raise Exception("Need to specify a process to run!")
+
+n=1
+while True:
+ print("\n"+str(n)+": Running process "+sys.argv[1]+" ...")
+ subprocess.check_call(sys.argv[1])
+ n=n+1
+
diff --git a/scripts/XMLTidy.py b/scripts/XMLTidy.py
new file mode 100755
index 00000000..8ff1838a
--- /dev/null
+++ b/scripts/XMLTidy.py
@@ -0,0 +1,28 @@
+#!/usr/bin/python3
+# Tidy and repair malformed XML like that produced by CATCH under multithreaded use :)
+# (C) 2015 Niall Douglas http://www.nedprod.com/
+# Created: 22nd Feb 2015
+
+import sys, lxml.etree
+
+if len(sys.argv)<2:
+ print("Usage: "+sys.argv[0]+" <input> [<output>]", file=sys.stderr)
+ sys.exit(1)
+inputpath=sys.argv[1]
+if len(sys.argv)>2:
+ outputpath=sys.argv[2]
+else:
+ outputpath=None
+
+parser=lxml.etree.XMLParser(recover=True)
+tree=lxml.etree.parse(inputpath, parser)
+if parser.error_log:
+ print("Errors during XML parsing:", file=sys.stderr)
+ print(parser.error_log, file=sys.stderr)
+newxml=lxml.etree.tostring(tree.getroot())
+
+if outputpath is None:
+ print(newxml)
+else:
+ with open(outputpath, 'wb') as oh:
+ oh.write(newxml)
diff --git a/scripts/readme_to_html.sh b/scripts/readme_to_html.sh
new file mode 100755
index 00000000..b27ee45f
--- /dev/null
+++ b/scripts/readme_to_html.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+echo '<html xmlns="http://www.w3.org/1999/xhtml"><body>
+' > Readme.html
+cat Readme.md >> Readme.html
+echo '
+</body></html>
+' >> Readme.html
+xsltproc scripts/xhtml_to_docbook.xsl Readme.html > Readme.docbook
diff --git a/scripts/travis_lldb.expect b/scripts/travis_lldb.expect
new file mode 100644
index 00000000..d5466fad
--- /dev/null
+++ b/scripts/travis_lldb.expect
@@ -0,0 +1,10 @@
+spawn lldb ./test_all
+expect "*\nCurrent executable set to*"
+send "run\r"
+set timeout 120
+expect "*\r\nProcess * stopped\r\n*"
+send "bt\rthread list\rthread backtrace all\rquit\r"
+set timeout 10
+expect "*Quitting LLDB will kill one or more processes. Do you really want to proceed*"
+send "y\r"
+expect default
diff --git a/scripts/xhtml_to_docbook.xsl b/scripts/xhtml_to_docbook.xsl
new file mode 100644
index 00000000..4b7bdbca
--- /dev/null
+++ b/scripts/xhtml_to_docbook.xsl
@@ -0,0 +1,537 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:fo="http://www.w3.org/1999/XSL/Format"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:saxon="http://icl.com/saxon"
+ exclude-result-prefixes="xsl fo html saxon">
+
+<xsl:output momit-xml-declaration="yes" method="xml" media-type="application/xhtml+xml"
+doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
+doctype-public="-//W3C//DTD XHTML 1.1//EN" encoding="UTF-8" indent="yes"/>
+<xsl:param name="filename"></xsl:param>
+<xsl:param name="prefix">wb</xsl:param>
+<xsl:param name="graphics_location"></xsl:param>
+
+<!-- Main block-level conversions -->
+<xsl:template match="html:html">
+ <xsl:apply-templates select="html:body"/>
+</xsl:template>
+
+<!-- This template converts each HTML file encountered into a DocBook
+ section. For a title, it selects the first h1 element -->
+<xsl:template match="html:body">
+ <section>
+ <xsl:if test="$filename != ''">
+ <xsl:attribute name="id">
+ <xsl:value-of select="$prefix"/>
+ <xsl:text>_</xsl:text>
+ <xsl:value-of select="translate($filename,' ()','__')"/>
+ </xsl:attribute>
+ </xsl:if>
+ <title>
+ <xsl:value-of select=".//html:h1[1]
+ |.//html:h2[1]
+ |.//html:h3[1]"/>
+ </title>
+ <xsl:apply-templates select="*"/>
+ </section>
+</xsl:template>
+
+<!-- This template matches on all HTML header items and makes them into
+ bridgeheads. It attempts to assign an ID to each bridgehead by looking
+ for a named anchor as a child of the header or as the immediate preceding
+ or following sibling -->
+<xsl:template match="html:h1
+ |html:h2
+ |html:h3
+ |html:h4
+ |html:h5
+ |html:h6">
+ <bridgehead>
+ <xsl:choose>
+ <xsl:when test="count(html:a/@name)">
+ <xsl:attribute name="id">
+ <xsl:value-of select="html:a/@name"/>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:when test="preceding-sibling::* = preceding-sibling::html:a[@name != '']">
+ <xsl:attribute name="id">
+ <xsl:value-of select="concat($prefix,preceding-sibling::html:a[1]/@name)"/>
+ </xsl:attribute>
+ </xsl:when>
+ <xsl:when test="following-sibling::* = following-sibling::html:a[@name != '']">
+ <xsl:attribute name="id">
+ <xsl:value-of select="concat($prefix,following-sibling::html:a[1]/@name)"/>
+ </xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:apply-templates/>
+ </bridgehead>
+</xsl:template>
+
+<!-- These templates perform one-to-one conversions of HTML elements into
+ DocBook elements -->
+<xsl:template match="html:p|html:center|html:div">
+<!-- if the paragraph has no text (perhaps only a child <img>), don't
+ make it a para -->
+ <xsl:choose>
+ <xsl:when test="normalize-space(.) = ''">
+ <xsl:apply-templates/>
+ </xsl:when>
+ <xsl:otherwise>
+ <para>
+ <xsl:apply-templates/>
+ </para>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+<xsl:template match="html:pre">
+ <programlisting>
+ <xsl:apply-templates/>
+ </programlisting>
+</xsl:template>
+
+<!-- Hyperlinks -->
+<xsl:template match="html:a[contains(@href,'http://')]" priority="1.5">
+ <ulink>
+ <xsl:attribute name="url">
+ <xsl:value-of select="normalize-space(@href)"/>
+ </xsl:attribute>
+ <xsl:apply-templates/>
+ </ulink>
+</xsl:template>
+
+<xsl:template match="html:a[contains(@href,'https://')]" priority="1.5">
+ <ulink>
+ <xsl:attribute name="url">
+ <xsl:value-of select="normalize-space(@href)"/>
+ </xsl:attribute>
+ <xsl:apply-templates/>
+ </ulink>
+</xsl:template>
+
+<xsl:template match="html:a[contains(@href,'#')]" priority="0.6">
+ <xref>
+ <xsl:attribute name="linkend">
+ <xsl:call-template name="make_id">
+ <xsl:with-param name="string" select="substring-after(@href,'#')"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ </xref>
+</xsl:template>
+
+<xsl:template match="html:a[@href != '']">
+ <xref>
+ <xsl:attribute name="linkend">
+ <xsl:value-of select="$prefix"/>
+ <xsl:text>_</xsl:text>
+ <xsl:call-template name="make_id">
+ <xsl:with-param name="string" select="@href"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ </xref>
+</xsl:template>
+
+<!-- Need to come up with good template for converting filenames into ID's -->
+<xsl:template name="make_id">
+ <xsl:param name="string" select="''"/>
+ <xsl:variable name="fixedname">
+ <xsl:call-template name="get_filename">
+ <xsl:with-param name="path" select="translate($string,' \()','_/_')"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="contains($fixedname,'.htm')">
+ <xsl:value-of select="substring-before($fixedname,'.htm')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$fixedname"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="string.subst">
+ <xsl:param name="string" select="''"/>
+ <xsl:param name="substitute" select="''"/>
+ <xsl:param name="with" select="''"/>
+ <xsl:choose>
+ <xsl:when test="contains($string,$substitute)">
+ <xsl:variable name="pre" select="substring-before($string,$substitute)"/>
+ <xsl:variable name="post" select="substring-after($string,$substitute)"/>
+ <xsl:call-template name="string.subst">
+ <xsl:with-param name="string" select="concat($pre,$with,$post)"/>
+ <xsl:with-param name="substitute" select="$substitute"/>
+ <xsl:with-param name="with" select="$with"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$string"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Images -->
+<!-- Images and image maps -->
+<xsl:template match="html:img">
+ <xsl:variable name="tag_name">
+ <xsl:choose>
+ <xsl:when test="boolean(parent::html:p) and
+ boolean(normalize-space(parent::html:p/text()))">
+ <xsl:text>inlinemediaobject</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>mediaobject</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:element name="{$tag_name}">
+ <imageobject>
+ <xsl:call-template name="process.image"/>
+ </imageobject>
+ </xsl:element>
+</xsl:template>
+
+<xsl:template name="process.image">
+ <imagedata>
+<xsl:attribute name="fileref">
+<!--
+ <xsl:call-template name="make_absolute">
+ <xsl:with-param name="filename" select="@src"/>
+ </xsl:call-template>
+ -->
+ <xsl:value-of select="@src"/>
+</xsl:attribute>
+<xsl:if test="@height != ''">
+ <xsl:attribute name="depth">
+ <xsl:value-of select="@height"/>
+ </xsl:attribute>
+</xsl:if>
+<xsl:if test="@width != ''">
+ <xsl:attribute name="width">
+ <xsl:value-of select="@width"/>
+ </xsl:attribute>
+</xsl:if>
+ </imagedata>
+</xsl:template>
+
+<xsl:template name="make_absolute">
+ <xsl:param name="filename"/>
+ <xsl:variable name="name_only">
+ <xsl:call-template name="get_filename">
+ <xsl:with-param name="path" select="$filename"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="$graphics_location"/><xsl:value-of select="$name_only"/>
+</xsl:template>
+
+<xsl:template match="html:ul[count(*) = 0]">
+ <xsl:message>Matched</xsl:message>
+ <blockquote>
+ <xsl:apply-templates/>
+ </blockquote>
+</xsl:template>
+
+<xsl:template name="get_filename">
+ <xsl:param name="path"/>
+ <xsl:choose>
+ <xsl:when test="contains($path,'/')">
+ <xsl:call-template name="get_filename">
+ <xsl:with-param name="path" select="substring-after($path,'/')"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$path"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- LIST ELEMENTS -->
+<xsl:template match="html:ul">
+ <itemizedlist>
+ <xsl:apply-templates/>
+ </itemizedlist>
+</xsl:template>
+
+<xsl:template match="html:ol">
+ <orderedlist>
+ <xsl:apply-templates/>
+ </orderedlist>
+</xsl:template>
+
+<!-- This template makes a DocBook variablelist out of an HTML definition list -->
+<xsl:template match="html:dl">
+ <variablelist>
+ <xsl:for-each select="html:dt">
+ <varlistentry>
+ <term>
+ <xsl:apply-templates/>
+ </term>
+ <listitem>
+ <xsl:apply-templates select="following-sibling::html:dd[1]"/>
+ </listitem>
+ </varlistentry>
+ </xsl:for-each>
+ </variablelist>
+</xsl:template>
+
+<xsl:template match="html:dd">
+ <xsl:choose>
+ <xsl:when test="boolean(html:p)">
+ <xsl:apply-templates/>
+ </xsl:when>
+ <xsl:otherwise>
+ <para>
+ <xsl:apply-templates/>
+ </para>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="html:li">
+ <listitem>
+ <xsl:choose>
+ <xsl:when test="count(html:p) = 0">
+ <para>
+ <xsl:apply-templates/>
+ </para>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:apply-templates/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </listitem>
+</xsl:template>
+
+<xsl:template match="*">
+ <xsl:message>No template for <xsl:value-of select="name()"/>
+ </xsl:message>
+ <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template match="@*">
+ <xsl:message>No template for <xsl:value-of select="name()"/>
+ </xsl:message>
+ <xsl:apply-templates/>
+</xsl:template>
+
+<!-- inline formatting -->
+<xsl:template match="html:b">
+ <emphasis role="bold">
+ <xsl:apply-templates/>
+ </emphasis>
+</xsl:template>
+<xsl:template match="html:i">
+ <emphasis>
+ <xsl:apply-templates/>
+ </emphasis>
+</xsl:template>
+<xsl:template match="html:u">
+ <citetitle>
+ <xsl:apply-templates/>
+ </citetitle>
+</xsl:template>
+
+<!-- Ignored elements -->
+<xsl:template match="html:hr"/>
+<xsl:template match="html:h1[1]|html:h2[1]|html:h3[1]" priority="1"/>
+<xsl:template match="html:br"/>
+<xsl:template match="html:p[normalize-space(.) = '' and count(*) = 0]"/>
+<xsl:template match="text()">
+ <xsl:choose>
+ <xsl:when test="normalize-space(.) = ''"></xsl:when>
+ <xsl:otherwise><xsl:copy/></xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Workbench Hacks -->
+<xsl:template match="html:div[contains(@style,'margin-left: 2em')]">
+ <blockquote><para>
+ <xsl:apply-templates/></para>
+ </blockquote>
+</xsl:template>
+
+<xsl:template match="html:a[@href != ''
+ and not(boolean(ancestor::html:p|ancestor::html:li))]"
+ priority="1">
+ <para>
+ <xref>
+ <xsl:attribute name="linkend">
+ <xsl:value-of select="$prefix"/>
+ <xsl:text>_</xsl:text>
+ <xsl:call-template name="make_id">
+ <xsl:with-param name="string" select="@href"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ </xref>
+ </para>
+</xsl:template>
+
+<xsl:template match="html:a[contains(@href,'#')
+ and not(boolean(ancestor::html:p|ancestor::html:li))]"
+ priority="1.1">
+ <para>
+ <xref>
+ <xsl:attribute name="linkend">
+ <xsl:value-of select="$prefix"/>
+ <xsl:text>_</xsl:text>
+ <xsl:call-template name="make_id">
+ <xsl:with-param name="string" select="substring-after(@href,'#')"/>
+ </xsl:call-template>
+ </xsl:attribute>
+ </xref>
+ </para>
+</xsl:template>
+
+<!-- Table conversion -->
+<xsl:template match="html:table">
+<!-- <xsl:variable name="column_count"
+ select="saxon:max(.//html:tr,saxon:expression('count(html:td)'))"/> -->
+ <xsl:variable name="column_count">
+ <xsl:call-template name="count_columns">
+ <xsl:with-param name="table" select="."/>
+ </xsl:call-template>
+ </xsl:variable>
+ <informaltable>
+ <tgroup>
+ <xsl:attribute name="cols">
+ <xsl:value-of select="$column_count"/>
+ </xsl:attribute>
+ <xsl:call-template name="generate-colspecs">
+ <xsl:with-param name="count" select="$column_count"/>
+ </xsl:call-template>
+ <thead>
+ <xsl:apply-templates select="html:tr[1]"/>
+ </thead>
+ <tbody>
+ <xsl:apply-templates select="html:tr[position() != 1]"/>
+ </tbody>
+ </tgroup>
+ </informaltable>
+</xsl:template>
+
+<xsl:template name="generate-colspecs">
+ <xsl:param name="count" select="0"/>
+ <xsl:param name="number" select="1"/>
+ <xsl:choose>
+ <xsl:when test="$count &lt; $number"/>
+ <xsl:otherwise>
+ <colspec>
+ <xsl:attribute name="colnum">
+ <xsl:value-of select="$number"/>
+ </xsl:attribute>
+ <xsl:attribute name="colname">
+ <xsl:value-of select="concat('col',$number)"/>
+ </xsl:attribute>
+ </colspec>
+ <xsl:call-template name="generate-colspecs">
+ <xsl:with-param name="count" select="$count"/>
+ <xsl:with-param name="number" select="$number + 1"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template match="html:tr">
+ <row>
+ <xsl:apply-templates/>
+ </row>
+</xsl:template>
+
+<xsl:template match="html:th|html:td">
+ <xsl:variable name="position" select="count(preceding-sibling::*) + 1"/>
+ <entry>
+ <xsl:if test="@colspan &gt; 1">
+ <xsl:attribute name="namest">
+ <xsl:value-of select="concat('col',$position)"/>
+ </xsl:attribute>
+ <xsl:attribute name="nameend">
+ <xsl:value-of select="concat('col',$position + number(@colspan) - 1)"/>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:if test="@rowspan &gt; 1">
+ <xsl:attribute name="morerows">
+ <xsl:value-of select="number(@rowspan) - 1"/>
+ </xsl:attribute>
+ </xsl:if>
+ <xsl:apply-templates/>
+ </entry>
+</xsl:template>
+
+<xsl:template match="html:td_null">
+ <xsl:apply-templates/>
+</xsl:template>
+
+<xsl:template name="count_columns">
+ <xsl:param name="table" select="."/>
+ <xsl:param name="row" select="$table/html:tr[1]"/>
+ <xsl:param name="max" select="0"/>
+ <xsl:choose>
+ <xsl:when test="local-name($table) != 'table'">
+ <xsl:message>Attempting to count columns on a non-table element</xsl:message>
+ </xsl:when>
+ <xsl:when test="local-name($row) != 'tr'">
+ <xsl:message>Row parameter is not a valid row</xsl:message>
+ </xsl:when>
+ <xsl:otherwise>
+ <!-- Count cells in the current row -->
+ <xsl:variable name="current_count">
+ <xsl:call-template name="count_cells">
+ <xsl:with-param name="cell" select="$row/html:td[1]|$row/html:th[1]"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- Check for the maximum value of $current_count and $max -->
+ <xsl:variable name="new_max">
+ <xsl:choose>
+ <xsl:when test="$current_count &gt; $max">
+ <xsl:value-of select="number($current_count)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="number($max)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <!-- If this is the last row, return $max, otherwise continue -->
+ <xsl:choose>
+ <xsl:when test="count($row/following-sibling::html:tr) = 0">
+ <xsl:value-of select="$new_max"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="count_columns">
+ <xsl:with-param name="table" select="$table"/>
+ <xsl:with-param name="row" select="$row/following-sibling::html:tr"/>
+ <xsl:with-param name="max" select="$new_max"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="count_cells">
+ <xsl:param name="cell"/>
+ <xsl:param name="count" select="0"/>
+ <xsl:variable name="new_count">
+ <xsl:choose>
+ <xsl:when test="$cell/@colspan &gt; 1">
+ <xsl:value-of select="number($cell/@colspan) + number($count)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="number('1') + number($count)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="count($cell/following-sibling::*) &gt; 0">
+ <xsl:call-template name="count_cells">
+ <xsl:with-param name="cell"
+ select="$cell/following-sibling::*[1]"/>
+ <xsl:with-param name="count" select="$new_count"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$new_count"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+</xsl:stylesheet>
+