File: virtual_memory.hpp

package info (click to toggle)
foonathan-memory 0.7.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,644 kB
  • sloc: cpp: 12,425; xml: 139; sh: 48; makefile: 25
file content (203 lines) | stat: -rw-r--r-- 9,474 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
// Copyright (C) 2015-2025 Jonathan Müller and foonathan/memory contributors
// SPDX-License-Identifier: Zlib

#ifndef FOONATHAN_MEMORY_VIRTUAL_MEMORY_HPP_INCLUDED
#define FOONATHAN_MEMORY_VIRTUAL_MEMORY_HPP_INCLUDED

/// \file
/// Virtual memory api and (low-level) allocator classes.

#include <cstddef>
#include <type_traits>

#include "detail/debug_helpers.hpp"
#include "detail/utility.hpp"
#include "config.hpp"

#if FOONATHAN_MEMORY_EXTERN_TEMPLATE
#include "allocator_traits.hpp"
#endif

namespace foonathan
{
    namespace memory
    {
        namespace detail
        {
            struct virtual_memory_allocator_leak_handler
            {
                void operator()(std::ptrdiff_t amount);
            };

            FOONATHAN_MEMORY_GLOBAL_LEAK_CHECKER(virtual_memory_allocator_leak_handler,
                                                 virtual_memory_allocator_leak_checker)
        } // namespace detail

        /// The page size of the virtual memory.
        /// All virtual memory allocations must be multiple of this size.
        /// It is usually 4KiB.
        /// \ingroup allocator
        /// \deprecated use \ref get_virtual_memory_page_size instead.
        extern const std::size_t virtual_memory_page_size;

        /// \returns the page size of the virtual memory.
        /// All virtual memory allocations must be multiple of this size.
        /// It is usually 4KiB.
        /// \ingroup allocator
        std::size_t get_virtual_memory_page_size() noexcept;

        /// Reserves virtual memory.
        /// \effects Reserves the given number of pages.
        /// Each page is \ref virtual_memory_page_size big.
        /// \returns The address of the first reserved page,
        /// or \c nullptr in case of error.
        /// \note The memory may not be used, it must first be commited.
        /// \ingroup allocator
        void* virtual_memory_reserve(std::size_t no_pages) noexcept;

        /// Releases reserved virtual memory.
        /// \effects Returns previously reserved pages to the system.
        /// \requires \c pages must come from a previous call to \ref virtual_memory_reserve with the same \c calc_no_pages,
        /// it must not be \c nullptr.
        /// \ingroup allocator
        void virtual_memory_release(void* pages, std::size_t no_pages) noexcept;

        /// Commits reserved virtual memory.
        /// \effects Marks \c calc_no_pages pages starting at the given address available for use.
        /// \returns The beginning of the committed area, i.e. \c memory, or \c nullptr in case of error.
        /// \requires The memory must be previously reserved.
        /// \ingroup allocator
        void* virtual_memory_commit(void* memory, std::size_t no_pages) noexcept;

        /// Decommits commited virtual memory.
        /// \effects Puts commited memory back in the reserved state.
        /// \requires \c memory must come from a previous call to \ref virtual_memory_commit with the same \c calc_no_pages
        /// it must not be \c nullptr.
        /// \ingroup allocator
        void virtual_memory_decommit(void* memory, std::size_t no_pages) noexcept;

        /// A stateless \concept{concept_rawallocator,RawAllocator} that allocates memory using the virtual memory allocation functions.
        /// It does not prereserve any memory and will always reserve and commit combined.
        /// \ingroup allocator
        class virtual_memory_allocator
        : FOONATHAN_EBO(detail::global_leak_checker<detail::virtual_memory_allocator_leak_handler>)
        {
        public:
            using is_stateful = std::false_type;

            virtual_memory_allocator() noexcept = default;
            virtual_memory_allocator(virtual_memory_allocator&&) noexcept {}
            ~virtual_memory_allocator() noexcept = default;

            virtual_memory_allocator& operator=(virtual_memory_allocator&&) noexcept
            {
                return *this;
            }

            /// \effects A \concept{concept_rawallocator,RawAllocator} allocation function.
            /// It uses \ref virtual_memory_reserve followed by \ref virtual_memory_commit for the allocation.
            /// The number of pages allocated will be the minimum to hold \c size continuous bytes,
            /// i.e. \c size will be rounded up to the next multiple.
            /// If debug fences are activated, one additional page before and after the memory will be allocated.
            /// \returns A pointer to a \concept{concept_node,node}, it will never be \c nullptr.
            /// It will always be aligned on a fence boundary, regardless of the alignment parameter.
            /// \throws An exception of type \ref out_of_memory or whatever is thrown by its handler if the allocation fails.
            void* allocate_node(std::size_t size, std::size_t alignment);

            /// \effects A \concept{concept_rawallocator,RawAllocator} deallocation function.
            /// It calls \ref virtual_memory_decommit followed by \ref virtual_memory_release for the deallocation.
            void deallocate_node(void* node, std::size_t size, std::size_t alignment) noexcept;

            /// \returns The maximum node size by returning the maximum value.
            std::size_t max_node_size() const noexcept;

            /// \returns The maximum alignment which is the same as the \ref virtual_memory_page_size.
            std::size_t max_alignment() const noexcept;
        };

#if FOONATHAN_MEMORY_EXTERN_TEMPLATE
        extern template class allocator_traits<virtual_memory_allocator>;
#endif

        struct memory_block;
        struct allocator_info;

        /// A \concept{concept_blockallocator,BlockAllocator} that reserves virtual memory and commits it part by part.
        /// It is similar to \ref memory_stack but does not support growing and uses virtual memory,
        /// also meant for big blocks not small allocations.
        /// \ingroup allocator
        class virtual_block_allocator
        {
        public:
            /// \effects Creates it giving it the block size and the total number of blocks it can allocate.
            /// It reserves enough virtual memory for <tt>block_size * no_blocks</tt>.
            /// \requires \c block_size must be non-zero and a multiple of the \ref virtual_memory_page_size.
            /// \c no_blocks must be bigger than \c 1.
            /// \throws \ref out_of_memory if it cannot reserve the virtual memory.
            explicit virtual_block_allocator(std::size_t block_size, std::size_t no_blocks);

            /// \effects Releases the reserved virtual memory.
            /// \requires All previously \ref allocate_block() committed blocks must be decommitted via
            /// \ref deallocate_block(), otherwise the blocks that have not been deallocated are leaked.
            ~virtual_block_allocator() noexcept;

            /// @{
            /// \effects Moves the block allocator, it transfers ownership over the reserved area.
            /// This does not invalidate any memory blocks.
            virtual_block_allocator(virtual_block_allocator&& other) noexcept
            : cur_(other.cur_), end_(other.end_), block_size_(other.block_size_)
            {
                other.cur_ = other.end_ = nullptr;
                other.block_size_       = 0;
            }

            virtual_block_allocator& operator=(virtual_block_allocator&& other) noexcept
            {
                virtual_block_allocator tmp(detail::move(other));
                swap(*this, tmp);
                return *this;
            }
            /// @}

            /// \effects Swaps the ownership over the reserved memory.
            /// This does not invalidate any memory blocks.
            friend void swap(virtual_block_allocator& a, virtual_block_allocator& b) noexcept
            {
                detail::adl_swap(a.cur_, b.cur_);
                detail::adl_swap(a.end_, b.end_);
                detail::adl_swap(a.block_size_, b.block_size_);
            }

            /// \effects Allocates a new memory block by committing the next \ref next_block_size() number of bytes.
            /// \returns The \ref memory_block committed.
            /// \throws \ref out_of_memory if it cannot commit the memory or the \ref capacity_left() is exhausted.
            memory_block allocate_block();

            /// \effects Deallocates the last allocated memory block by decommitting it.
            /// This block will be returned again on the next call to \ref allocate_block().
            /// \requires \c block must be the current top block of the memory,
            /// this is guaranteed by \ref memory_arena.
            void deallocate_block(memory_block block) noexcept;

            /// \returns The next block size, this is the block size of the constructor.
            std::size_t next_block_size() const noexcept
            {
                return block_size_;
            }

            /// \returns The number of blocks that can be committed until it runs out of memory.
            std::size_t capacity_left() const noexcept
            {
                return static_cast<std::size_t>(end_ - cur_) / block_size_;
            }

        private:
            allocator_info info() noexcept;

            char *      cur_, *end_;
            std::size_t block_size_;
        };
    } // namespace memory
} // namespace foonathan

#endif //FOONATHAN_MEMORY_VIRTUAL_MEMORY_HPP_INCLUDED