Electroneum
mlocker.cpp
Go to the documentation of this file.
1 // Copyright (c) 2018, The Monero Project
2 //
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification, are
6 // permitted provided that the following conditions are met:
7 //
8 // 1. Redistributions of source code must retain the above copyright notice, this list of
9 // conditions and the following disclaimer.
10 //
11 // 2. Redistributions in binary form must reproduce the above copyright notice, this list
12 // of conditions and the following disclaimer in the documentation and/or other
13 // materials provided with the distribution.
14 //
15 // 3. Neither the name of the copyright holder nor the names of its contributors may be
16 // used to endorse or promote products derived from this software without specific
17 // prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
20 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
21 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
22 // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
27 // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 
29 #if defined __GNUC__ && !defined _WIN32
30 #define HAVE_MLOCK 1
31 #endif
32 
33 #include <unistd.h>
34 #if defined HAVE_MLOCK
35 #include <sys/mman.h>
36 #endif
37 #include "misc_log_ex.h"
38 #include "syncobj.h"
39 #include "mlocker.h"
40 
41 #include <atomic>
42 
43 #undef ELECTRONEUM_DEFAULT_LOG_CATEGORY
44 #define ELECTRONEUM_DEFAULT_LOG_CATEGORY "mlocker"
45 
46 // did an mlock operation previously fail? we only
47 // want to log an error once and be done with it
48 static std::atomic<bool> previously_failed{ false };
49 
50 static size_t query_page_size()
51 {
52 #if defined HAVE_MLOCK
53  long ret = sysconf(_SC_PAGESIZE);
54  if (ret <= 0)
55  {
56  MERROR("Failed to determine page size");
57  return 0;
58  }
59  return ret;
60 #else
61 #warning Missing query_page_size implementation
62 #endif
63  return 0;
64 }
65 
66 static void do_lock(void *ptr, size_t len)
67 {
68 #if defined HAVE_MLOCK
69  int ret = mlock(ptr, len);
70  if (ret < 0 && !previously_failed.exchange(true))
71  MERROR("Error locking page at " << ptr << ": " << strerror(errno) << ", subsequent mlock errors will be silenced");
72 #else
73 #warning Missing do_lock implementation
74 #endif
75 }
76 
77 static void do_unlock(void *ptr, size_t len)
78 {
79 #if defined HAVE_MLOCK
80  int ret = munlock(ptr, len);
81  // check whether we previously failed, but don't set it, this is just
82  // to pacify the errors of mlock()ing failed, in which case unlocking
83  // is also not going to work of course
84  if (ret < 0 && !previously_failed.load())
85  MERROR("Error unlocking page at " << ptr << ": " << strerror(errno));
86 #else
87 #warning Missing implementation of page size detection
88 #endif
89 }
90 
91 namespace epee
92 {
93  size_t mlocker::page_size = 0;
94  size_t mlocker::num_locked_objects = 0;
95 
96  boost::mutex &mlocker::mutex()
97  {
98  static boost::mutex *vmutex = new boost::mutex();
99  return *vmutex;
100  }
101  std::map<size_t, unsigned int> &mlocker::map()
102  {
103  static std::map<size_t, unsigned int> *vmap = new std::map<size_t, unsigned int>();
104  return *vmap;
105  }
106 
108  {
109  CRITICAL_REGION_LOCAL(mutex());
110  if (page_size == 0)
111  page_size = query_page_size();
112  return page_size;
113  }
114 
115  mlocker::mlocker(void *ptr, size_t len): ptr(ptr), len(len)
116  {
117  lock(ptr, len);
118  }
119 
121  {
122  try { unlock(ptr, len); }
123  catch (...) { /* ignore and do not propagate through the dtor */ }
124  }
125 
126  void mlocker::lock(void *ptr, size_t len)
127  {
128  TRY_ENTRY();
129 
130  size_t page_size = get_page_size();
131  if (page_size == 0)
132  return;
133 
134  CRITICAL_REGION_LOCAL(mutex());
135  const size_t first = ((uintptr_t)ptr) / page_size;
136  const size_t last = (((uintptr_t)ptr) + len - 1) / page_size;
137  for (size_t page = first; page <= last; ++page)
138  lock_page(page);
139  ++num_locked_objects;
140 
141  CATCH_ENTRY_L1("mlocker::lock", void());
142  }
143 
144  void mlocker::unlock(void *ptr, size_t len)
145  {
146  TRY_ENTRY();
147 
148  size_t page_size = get_page_size();
149  if (page_size == 0)
150  return;
151  CRITICAL_REGION_LOCAL(mutex());
152  const size_t first = ((uintptr_t)ptr) / page_size;
153  const size_t last = (((uintptr_t)ptr) + len - 1) / page_size;
154  for (size_t page = first; page <= last; ++page)
155  unlock_page(page);
156  --num_locked_objects;
157 
158  CATCH_ENTRY_L1("mlocker::lock", void());
159  }
160 
162  {
163  CRITICAL_REGION_LOCAL(mutex());
164  return map().size();
165  }
166 
168  {
169  CRITICAL_REGION_LOCAL(mutex());
170  return num_locked_objects;
171  }
172 
173  void mlocker::lock_page(size_t page)
174  {
175  std::pair<std::map<size_t, unsigned int>::iterator, bool> p = map().insert(std::make_pair(page, 1));
176  if (p.second)
177  {
178  do_lock((void*)(page * page_size), page_size);
179  }
180  else
181  {
182  ++p.first->second;
183  }
184  }
185 
186  void mlocker::unlock_page(size_t page)
187  {
188  std::map<size_t, unsigned int>::iterator i = map().find(page);
189  if (i == map().end())
190  {
191  MERROR("Attempt to unlock unlocked page at " << (void*)(page * page_size));
192  }
193  else
194  {
195  if (!--i->second)
196  {
197  map().erase(i);
198  do_unlock((void*)(page * page_size), page_size);
199  }
200  }
201  }
202 }
#define MERROR(x)
Definition: misc_log_ex.h:73
static void lock(void *ptr, size_t len)
Definition: mlocker.cpp:126
static size_t get_page_size()
Definition: mlocker.cpp:107
mlocker(void *ptr, size_t len)
Definition: mlocker.cpp:115
#define TRY_ENTRY()
Definition: misc_log_ex.h:151
_W64 unsigned int uintptr_t
Definition: stdint.h:165
#define CRITICAL_REGION_LOCAL(x)
Definition: syncobj.h:228
static size_t get_num_locked_objects()
Definition: mlocker.cpp:167
static size_t get_num_locked_pages()
Definition: mlocker.cpp:161
#define CATCH_ENTRY_L1(lacation, return_val)
Definition: misc_log_ex.h:166
static void unlock(void *ptr, size_t len)
Definition: mlocker.cpp:144