Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Copy-on-write support #78

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions include/mio/detail/mmap.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@

#include <algorithm>

#ifdef _WIN32
# include <vector>
#endif

#ifndef _WIN32
# include <unistd.h>
# include <fcntl.h>
Expand All @@ -52,7 +56,7 @@ inline DWORD int64_low(int64_t n) noexcept
return n & 0xffffffff;
}

std::wstring s_2_ws(const std::string& s)
inline std::wstring s_2_ws(const std::string& s)
{
if (s.empty())
return{};
Expand All @@ -70,7 +74,7 @@ template<
> file_handle_type open_file_helper(const String& path, const access_mode mode)
{
return ::CreateFileW(s_2_ws(path).c_str(),
mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
mode == access_mode::write ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
Expand All @@ -85,7 +89,7 @@ typename std::enable_if<
>::type open_file_helper(const String& path, const access_mode mode)
{
return ::CreateFileW(c_str(path),
mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
mode == access_mode::write ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
Expand Down Expand Up @@ -125,7 +129,7 @@ file_handle_type open_file(const String& path, const access_mode mode,
const auto handle = win::open_file_helper(path, mode);
#else // POSIX
const auto handle = ::open(c_str(path),
mode == access_mode::read ? O_RDONLY : O_RDWR);
mode == access_mode::write ? O_RDWR : O_RDONLY);
#endif
if(handle == invalid_handle)
{
Expand Down Expand Up @@ -176,7 +180,8 @@ inline mmap_context memory_map(const file_handle_type file_handle, const int64_t
const auto file_mapping_handle = ::CreateFileMapping(
file_handle,
0,
mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE,
mode == access_mode::read ? PAGE_READONLY :
mode == access_mode::write ? PAGE_READWRITE : PAGE_WRITECOPY,
win::int64_high(max_file_size),
win::int64_low(max_file_size),
0);
Expand All @@ -187,7 +192,8 @@ inline mmap_context memory_map(const file_handle_type file_handle, const int64_t
}
char* mapping_start = static_cast<char*>(::MapViewOfFile(
file_mapping_handle,
mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE,
mode == access_mode::read ? FILE_MAP_READ :
mode == access_mode::write ? FILE_MAP_WRITE : FILE_MAP_COPY,
win::int64_high(aligned_offset),
win::int64_low(aligned_offset),
length_to_map));
Expand All @@ -203,7 +209,7 @@ inline mmap_context memory_map(const file_handle_type file_handle, const int64_t
0, // Don't give hint as to where to map.
length_to_map,
mode == access_mode::read ? PROT_READ : PROT_WRITE,
MAP_SHARED,
mode == access_mode::copy_on_write ? MAP_PRIVATE : MAP_SHARED,
file_handle,
aligned_offset));
if(mapping_start == MAP_FAILED)
Expand Down Expand Up @@ -474,7 +480,7 @@ basic_mmap<AccessMode, ByteT>::conditional_sync()

template<access_mode AccessMode, typename ByteT>
template<access_mode A>
typename std::enable_if<A == access_mode::read, void>::type
typename std::enable_if<A != access_mode::write, void>::type
basic_mmap<AccessMode, ByteT>::conditional_sync()
{
// noop
Expand Down
50 changes: 41 additions & 9 deletions include/mio/mmap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ struct basic_mmap
*/
template<
access_mode A = AccessMode,
typename = typename std::enable_if<A == access_mode::write>::type
typename = typename std::enable_if<A != access_mode::read>::type
> pointer data() noexcept { return data_; }
const_pointer data() const noexcept { return data_; }

Expand All @@ -202,7 +202,7 @@ struct basic_mmap
*/
template<
access_mode A = AccessMode,
typename = typename std::enable_if<A == access_mode::write>::type
typename = typename std::enable_if<A != access_mode::read>::type
> iterator begin() noexcept { return data(); }
const_iterator begin() const noexcept { return data(); }
const_iterator cbegin() const noexcept { return data(); }
Expand All @@ -213,7 +213,7 @@ struct basic_mmap
*/
template<
access_mode A = AccessMode,
typename = typename std::enable_if<A == access_mode::write>::type
typename = typename std::enable_if<A != access_mode::read>::type
> iterator end() noexcept { return data() + length(); }
const_iterator end() const noexcept { return data() + length(); }
const_iterator cend() const noexcept { return data() + length(); }
Expand All @@ -225,7 +225,7 @@ struct basic_mmap
*/
template<
access_mode A = AccessMode,
typename = typename std::enable_if<A == access_mode::write>::type
typename = typename std::enable_if<A != access_mode::read>::type
> reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const noexcept
{ return const_reverse_iterator(end()); }
Expand All @@ -238,7 +238,7 @@ struct basic_mmap
*/
template<
access_mode A = AccessMode,
typename = typename std::enable_if<A == access_mode::write>::type
typename = typename std::enable_if<A != access_mode::read>::type
> reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
const_reverse_iterator rend() const noexcept
{ return const_reverse_iterator(begin()); }
Expand Down Expand Up @@ -359,7 +359,7 @@ struct basic_mmap
private:
template<
access_mode A = AccessMode,
typename = typename std::enable_if<A == access_mode::write>::type
typename = typename std::enable_if<A != access_mode::read>::type
> pointer get_mapping_start() noexcept
{
return !data() ? nullptr : data() - mapping_offset();
Expand All @@ -372,14 +372,15 @@ struct basic_mmap

/**
* The destructor syncs changes to disk if `AccessMode` is `write`, but not
* if it's `read`, but since the destructor cannot be templated, we need to
* do SFINAE in a dedicated function, where one syncs and the other is a noop.
* if it's `read` or `copy_on_write`, but since the destructor cannot be
* templated, we need to do SFINAE in a dedicated function, where one syncs
* and the other is a noop.
*/
template<access_mode A = AccessMode>
typename std::enable_if<A == access_mode::write, void>::type
conditional_sync();
template<access_mode A = AccessMode>
typename std::enable_if<A == access_mode::read, void>::type conditional_sync();
typename std::enable_if<A != access_mode::write, void>::type conditional_sync();
};

template<access_mode AccessMode, typename ByteT>
Expand Down Expand Up @@ -420,6 +421,13 @@ using basic_mmap_source = basic_mmap<access_mode::read, ByteT>;
template<typename ByteT>
using basic_mmap_sink = basic_mmap<access_mode::write, ByteT>;

/**
* This is the basis for all copy-on-write mmap objects and should be preferred over
* directly using `basic_mmap`.
*/
template<typename ByteT>
using basic_mmap_cow_sink = basic_mmap<access_mode::copy_on_write, ByteT>;

/**
* These aliases cover the most common use cases, both representing a raw byte stream
* (either with a char or an unsigned char/uint8_t).
Expand All @@ -430,6 +438,9 @@ using ummap_source = basic_mmap_source<unsigned char>;
using mmap_sink = basic_mmap_sink<char>;
using ummap_sink = basic_mmap_sink<unsigned char>;

using mmap_cow_sink = basic_mmap_cow_sink<char>;
using ummap_cow_sink = basic_mmap_cow_sink<unsigned char>;

/**
* Convenience factory method that constructs a mapping for any `basic_mmap` or
* `basic_mmap` type.
Expand Down Expand Up @@ -485,6 +496,27 @@ mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error)
return make_mmap_sink(token, 0, map_entire_file, error);
}

/**
* Convenience factory method.
*
* MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
* `std::filesystem::path`, `std::vector<char>`, or similar), or a
* `mmap_sink::handle_type`.
*/
template<typename MappingToken>
mmap_cow_sink make_mmap_cow_sink(const MappingToken& token,
mmap_cow_sink::size_type offset, mmap_cow_sink::size_type length,
std::error_code& error)
{
return make_mmap<mmap_cow_sink>(token, offset, length, error);
}

template<typename MappingToken>
mmap_cow_sink make_mmap_cow_sink(const MappingToken& token, std::error_code& error)
{
return make_mmap_cow_sink(token, 0, map_entire_file, error);
}

} // namespace mio

#include "detail/mmap.ipp"
Expand Down
7 changes: 4 additions & 3 deletions include/mio/page.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,14 @@
namespace mio {

/**
* This is used by `basic_mmap` to determine whether to create a read-only or
* a read-write memory mapping.
* This is used by `basic_mmap` to determine whether to create a read-only,
* a read-write or a copy-on-write memory mapping.
*/
enum class access_mode
{
read,
write
write,
copy_on_write
};

/**
Expand Down
18 changes: 14 additions & 4 deletions include/mio/shared_mmap.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ template<
*/
template<
access_mode A = AccessMode,
typename = typename std::enable_if<A == access_mode::write>::type
typename = typename std::enable_if<A != access_mode::read>::type
> pointer data() noexcept { return pimpl_->data(); }
const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; }

Expand All @@ -186,7 +186,7 @@ template<
*/
template<
access_mode A = AccessMode,
typename = typename std::enable_if<A == access_mode::write>::type
typename = typename std::enable_if<A != access_mode::read>::type
> iterator end() noexcept { return pimpl_->end(); }
const_iterator end() const noexcept { return pimpl_->end(); }
const_iterator cend() const noexcept { return pimpl_->cend(); }
Expand All @@ -198,7 +198,7 @@ template<
*/
template<
access_mode A = AccessMode,
typename = typename std::enable_if<A == access_mode::write>::type
typename = typename std::enable_if<A != access_mode::read>::type
> reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); }
const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); }
const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); }
Expand All @@ -209,7 +209,7 @@ template<
*/
template<
access_mode A = AccessMode,
typename = typename std::enable_if<A == access_mode::write>::type
typename = typename std::enable_if<A != access_mode::read>::type
> reverse_iterator rend() noexcept { return pimpl_->rend(); }
const_reverse_iterator rend() const noexcept { return pimpl_->rend(); }
const_reverse_iterator crend() const noexcept { return pimpl_->crend(); }
Expand Down Expand Up @@ -391,6 +391,13 @@ using basic_shared_mmap_source = basic_shared_mmap<access_mode::read, ByteT>;
template<typename ByteT>
using basic_shared_mmap_sink = basic_shared_mmap<access_mode::write, ByteT>;

/**
* This is the basis for all copy-on-write mmap objects and should be preferred over
* directly using basic_shared_mmap.
*/
template<typename ByteT>
using basic_shared_mmap_cow_sink = basic_shared_mmap<access_mode::copy_on_write, ByteT>;

/**
* These aliases cover the most common use cases, both representing a raw byte stream
* (either with a char or an unsigned char/uint8_t).
Expand All @@ -401,6 +408,9 @@ using shared_ummap_source = basic_shared_mmap_source<unsigned char>;
using shared_mmap_sink = basic_shared_mmap_sink<char>;
using shared_ummap_sink = basic_shared_mmap_sink<unsigned char>;

using shared_mmap_cow_sink = basic_shared_mmap_cow_sink<char>;
using shared_ummap_cow_sink = basic_shared_mmap_cow_sink<unsigned char>;

} // namespace mio

#endif // MIO_SHARED_MMAP_HEADER
Loading