LCOV - code coverage report
Current view: top level - src/support - lockedpool.h (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 7 7 100.0 %
Date: 2022-04-21 14:51:19 Functions: 3 3 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 4 4 100.0 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2016-2020 The Bitcoin Core developers
#       2                 :            : // Distributed under the MIT software license, see the accompanying
#       3                 :            : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#       4                 :            : 
#       5                 :            : #ifndef BITCOIN_SUPPORT_LOCKEDPOOL_H
#       6                 :            : #define BITCOIN_SUPPORT_LOCKEDPOOL_H
#       7                 :            : 
#       8                 :            : #include <stdint.h>
#       9                 :            : #include <list>
#      10                 :            : #include <map>
#      11                 :            : #include <mutex>
#      12                 :            : #include <memory>
#      13                 :            : #include <unordered_map>
#      14                 :            : 
#      15                 :            : /**
#      16                 :            :  * OS-dependent allocation and deallocation of locked/pinned memory pages.
#      17                 :            :  * Abstract base class.
#      18                 :            :  */
#      19                 :            : class LockedPageAllocator
#      20                 :            : {
#      21                 :            : public:
#      22                 :         67 :     virtual ~LockedPageAllocator() {}
#      23                 :            :     /** Allocate and lock memory pages.
#      24                 :            :      * If len is not a multiple of the system page size, it is rounded up.
#      25                 :            :      * Returns nullptr in case of allocation failure.
#      26                 :            :      *
#      27                 :            :      * If locking the memory pages could not be accomplished it will still
#      28                 :            :      * return the memory, however the lockingSuccess flag will be false.
#      29                 :            :      * lockingSuccess is undefined if the allocation fails.
#      30                 :            :      */
#      31                 :            :     virtual void* AllocateLocked(size_t len, bool *lockingSuccess) = 0;
#      32                 :            : 
#      33                 :            :     /** Unlock and free memory pages.
#      34                 :            :      * Clear the memory before unlocking.
#      35                 :            :      */
#      36                 :            :     virtual void FreeLocked(void* addr, size_t len) = 0;
#      37                 :            : 
#      38                 :            :     /** Get the total limit on the amount of memory that may be locked by this
#      39                 :            :      * process, in bytes. Return size_t max if there is no limit or the limit
#      40                 :            :      * is unknown. Return 0 if no memory can be locked at all.
#      41                 :            :      */
#      42                 :            :     virtual size_t GetLimit() = 0;
#      43                 :            : };
#      44                 :            : 
#      45                 :            : /* An arena manages a contiguous region of memory by dividing it into
#      46                 :            :  * chunks.
#      47                 :            :  */
#      48                 :            : class Arena
#      49                 :            : {
#      50                 :            : public:
#      51                 :            :     Arena(void *base, size_t size, size_t alignment);
#      52                 :            :     virtual ~Arena();
#      53                 :            : 
#      54                 :            :     Arena(const Arena& other) = delete; // non construction-copyable
#      55                 :            :     Arena& operator=(const Arena&) = delete; // non copyable
#      56                 :            : 
#      57                 :            :     /** Memory statistics. */
#      58                 :            :     struct Stats
#      59                 :            :     {
#      60                 :            :         size_t used;
#      61                 :            :         size_t free;
#      62                 :            :         size_t total;
#      63                 :            :         size_t chunks_used;
#      64                 :            :         size_t chunks_free;
#      65                 :            :     };
#      66                 :            : 
#      67                 :            :     /** Allocate size bytes from this arena.
#      68                 :            :      * Returns pointer on success, or 0 if memory is full or
#      69                 :            :      * the application tried to allocate 0 bytes.
#      70                 :            :      */
#      71                 :            :     void* alloc(size_t size);
#      72                 :            : 
#      73                 :            :     /** Free a previously allocated chunk of memory.
#      74                 :            :      * Freeing the zero pointer has no effect.
#      75                 :            :      * Raises std::runtime_error in case of error.
#      76                 :            :      */
#      77                 :            :     void free(void *ptr);
#      78                 :            : 
#      79                 :            :     /** Get arena usage statistics */
#      80                 :            :     Stats stats() const;
#      81                 :            : 
#      82                 :            : #ifdef ARENA_DEBUG
#      83                 :            :     void walk() const;
#      84                 :            : #endif
#      85                 :            : 
#      86                 :            :     /** Return whether a pointer points inside this arena.
#      87                 :            :      * This returns base <= ptr < (base+size) so only use it for (inclusive)
#      88                 :            :      * chunk starting addresses.
#      89                 :            :      */
#      90 [ +  + ][ +  + ]:    1752686 :     bool addressInArena(void *ptr) const { return ptr >= base && ptr < end; }
#      91                 :            : private:
#      92                 :            :     typedef std::multimap<size_t, char*> SizeToChunkSortedMap;
#      93                 :            :     /** Map to enable O(log(n)) best-fit allocation, as it's sorted by size */
#      94                 :            :     SizeToChunkSortedMap size_to_free_chunk;
#      95                 :            : 
#      96                 :            :     typedef std::unordered_map<char*, SizeToChunkSortedMap::const_iterator> ChunkToSizeMap;
#      97                 :            :     /** Map from begin of free chunk to its node in size_to_free_chunk */
#      98                 :            :     ChunkToSizeMap chunks_free;
#      99                 :            :     /** Map from end of free chunk to its node in size_to_free_chunk */
#     100                 :            :     ChunkToSizeMap chunks_free_end;
#     101                 :            : 
#     102                 :            :     /** Map from begin of used chunk to its size */
#     103                 :            :     std::unordered_map<char*, size_t> chunks_used;
#     104                 :            : 
#     105                 :            :     /** Base address of arena */
#     106                 :            :     char* base;
#     107                 :            :     /** End address of arena */
#     108                 :            :     char* end;
#     109                 :            :     /** Minimum chunk alignment */
#     110                 :            :     size_t alignment;
#     111                 :            : };
#     112                 :            : 
#     113                 :            : /** Pool for locked memory chunks.
#     114                 :            :  *
#     115                 :            :  * To avoid sensitive key data from being swapped to disk, the memory in this pool
#     116                 :            :  * is locked/pinned.
#     117                 :            :  *
#     118                 :            :  * An arena manages a contiguous region of memory. The pool starts out with one arena
#     119                 :            :  * but can grow to multiple arenas if the need arises.
#     120                 :            :  *
#     121                 :            :  * Unlike a normal C heap, the administrative structures are separate from the managed
#     122                 :            :  * memory. This has been done as the sizes and bases of objects are not in themselves sensitive
#     123                 :            :  * information, as to conserve precious locked memory. In some operating systems
#     124                 :            :  * the amount of memory that can be locked is small.
#     125                 :            :  */
#     126                 :            : class LockedPool
#     127                 :            : {
#     128                 :            : public:
#     129                 :            :     /** Size of one arena of locked memory. This is a compromise.
#     130                 :            :      * Do not set this too low, as managing many arenas will increase
#     131                 :            :      * allocation and deallocation overhead. Setting it too high allocates
#     132                 :            :      * more locked memory from the OS than strictly necessary.
#     133                 :            :      */
#     134                 :            :     static const size_t ARENA_SIZE = 256*1024;
#     135                 :            :     /** Chunk alignment. Another compromise. Setting this too high will waste
#     136                 :            :      * memory, setting it too low will facilitate fragmentation.
#     137                 :            :      */
#     138                 :            :     static const size_t ARENA_ALIGN = 16;
#     139                 :            : 
#     140                 :            :     /** Callback when allocation succeeds but locking fails.
#     141                 :            :      */
#     142                 :            :     typedef bool (*LockingFailed_Callback)();
#     143                 :            : 
#     144                 :            :     /** Memory statistics. */
#     145                 :            :     struct Stats
#     146                 :            :     {
#     147                 :            :         size_t used;
#     148                 :            :         size_t free;
#     149                 :            :         size_t total;
#     150                 :            :         size_t locked;
#     151                 :            :         size_t chunks_used;
#     152                 :            :         size_t chunks_free;
#     153                 :            :     };
#     154                 :            : 
#     155                 :            :     /** Create a new LockedPool. This takes ownership of the MemoryPageLocker,
#     156                 :            :      * you can only instantiate this with LockedPool(std::move(...)).
#     157                 :            :      *
#     158                 :            :      * The second argument is an optional callback when locking a newly allocated arena failed.
#     159                 :            :      * If this callback is provided and returns false, the allocation fails (hard fail), if
#     160                 :            :      * it returns true the allocation proceeds, but it could warn.
#     161                 :            :      */
#     162                 :            :     explicit LockedPool(std::unique_ptr<LockedPageAllocator> allocator, LockingFailed_Callback lf_cb_in = nullptr);
#     163                 :            :     ~LockedPool();
#     164                 :            : 
#     165                 :            :     LockedPool(const LockedPool& other) = delete; // non construction-copyable
#     166                 :            :     LockedPool& operator=(const LockedPool&) = delete; // non copyable
#     167                 :            : 
#     168                 :            :     /** Allocate size bytes from this arena.
#     169                 :            :      * Returns pointer on success, or 0 if memory is full or
#     170                 :            :      * the application tried to allocate 0 bytes.
#     171                 :            :      */
#     172                 :            :     void* alloc(size_t size);
#     173                 :            : 
#     174                 :            :     /** Free a previously allocated chunk of memory.
#     175                 :            :      * Freeing the zero pointer has no effect.
#     176                 :            :      * Raises std::runtime_error in case of error.
#     177                 :            :      */
#     178                 :            :     void free(void *ptr);
#     179                 :            : 
#     180                 :            :     /** Get pool usage statistics */
#     181                 :            :     Stats stats() const;
#     182                 :            : private:
#     183                 :            :     std::unique_ptr<LockedPageAllocator> allocator;
#     184                 :            : 
#     185                 :            :     /** Create an arena from locked pages */
#     186                 :            :     class LockedPageArena: public Arena
#     187                 :            :     {
#     188                 :            :     public:
#     189                 :            :         LockedPageArena(LockedPageAllocator *alloc_in, void *base_in, size_t size, size_t align);
#     190                 :            :         ~LockedPageArena();
#     191                 :            :     private:
#     192                 :            :         void *base;
#     193                 :            :         size_t size;
#     194                 :            :         LockedPageAllocator *allocator;
#     195                 :            :     };
#     196                 :            : 
#     197                 :            :     bool new_arena(size_t size, size_t align);
#     198                 :            : 
#     199                 :            :     std::list<LockedPageArena> arenas;
#     200                 :            :     LockingFailed_Callback lf_cb;
#     201                 :            :     size_t cumulative_bytes_locked;
#     202                 :            :     /** Mutex protects access to this pool's data structures, including arenas.
#     203                 :            :      */
#     204                 :            :     mutable std::mutex mutex;
#     205                 :            : };
#     206                 :            : 
#     207                 :            : /**
#     208                 :            :  * Singleton class to keep track of locked (ie, non-swappable) memory, for use in
#     209                 :            :  * std::allocator templates.
#     210                 :            :  *
#     211                 :            :  * Some implementations of the STL allocate memory in some constructors (i.e., see
#     212                 :            :  * MSVC's vector<T> implementation where it allocates 1 byte of memory in the allocator.)
#     213                 :            :  * Due to the unpredictable order of static initializers, we have to make sure the
#     214                 :            :  * LockedPoolManager instance exists before any other STL-based objects that use
#     215                 :            :  * secure_allocator are created. So instead of having LockedPoolManager also be
#     216                 :            :  * static-initialized, it is created on demand.
#     217                 :            :  */
#     218                 :            : class LockedPoolManager : public LockedPool
#     219                 :            : {
#     220                 :            : public:
#     221                 :            :     /** Return the current instance, or create it once */
#     222                 :            :     static LockedPoolManager& Instance()
#     223                 :    3436121 :     {
#     224                 :    3436121 :         static std::once_flag init_flag;
#     225                 :    3436121 :         std::call_once(init_flag, LockedPoolManager::CreateInstance);
#     226                 :    3436121 :         return *LockedPoolManager::_instance;
#     227                 :    3436121 :     }
#     228                 :            : 
#     229                 :            : private:
#     230                 :            :     explicit LockedPoolManager(std::unique_ptr<LockedPageAllocator> allocator);
#     231                 :            : 
#     232                 :            :     /** Create a new LockedPoolManager specialized to the OS */
#     233                 :            :     static void CreateInstance();
#     234                 :            :     /** Called when locking fails, warn the user here */
#     235                 :            :     static bool LockingFailed();
#     236                 :            : 
#     237                 :            :     static LockedPoolManager* _instance;
#     238                 :            : };
#     239                 :            : 
#     240                 :            : #endif // BITCOIN_SUPPORT_LOCKEDPOOL_H

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a