Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
sonar don't like comments ending with ';'
[simgrid.git] / src / simix / smx_context.cpp
1 /* a fast and simple context switching library                              */
2
3 /* Copyright (c) 2009-2017. The SimGrid Team. All rights reserved.          */
4
5 /* This program is free software; you can redistribute it and/or modify it
6  * under the terms of the license (GNU LGPL) which comes with this package. */
7
8 #include <cerrno>
9 #include <cstring>
10
11 #include <utility>
12 #include <string>
13
14 #include <xbt/config.hpp>
15 #include <xbt/log.h>
16 #include <xbt/range.hpp>
17 #include <xbt/sysdep.h>
18
19 #include "simgrid/modelchecker.h"
20 #include "simgrid/sg_config.h"
21 #include "smx_private.hpp"
22 #include "src/internal_config.h"
23 #include "xbt/log.h"
24 #include "xbt/xbt_os_thread.h"
25
26 #ifdef _WIN32
27 #include <windows.h>
28 #include <malloc.h>
29 #else
30 #include <sys/mman.h>
31 #endif
32
33 #ifdef __MINGW32__
34 #define _aligned_malloc __mingw_aligned_malloc
35 #define _aligned_free  __mingw_aligned_free
36 #endif /*MINGW*/
37
38 #if HAVE_VALGRIND_H
39 # include <valgrind/valgrind.h>
40 #endif
41
42 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(simix_context, simix, "Context switching mechanism");
43
44 static std::pair<const char*, simgrid::kernel::context::ContextFactoryInitializer> context_factories[] = {
45 #if HAVE_RAW_CONTEXTS
46   { "raw", &simgrid::kernel::context::raw_factory },
47 #endif
48 #if HAVE_UCONTEXT_CONTEXTS
49   { "ucontext", &simgrid::kernel::context::sysv_factory },
50 #endif
51 #if HAVE_BOOST_CONTEXTS
52   { "boost", &simgrid::kernel::context::boost_factory },
53 #endif
54 #if HAVE_THREAD_CONTEXTS
55   { "thread", &simgrid::kernel::context::thread_factory },
56 #endif
57 };
58
59 static_assert(sizeof(context_factories) != 0, "No context factories are enabled for this build");
60
61 // Create the list of possible contexts:
62 static inline
63 std::string contexts_list()
64 {
65   std::string res;
66   const std::size_t n = sizeof(context_factories) / sizeof(context_factories[0]);
67   for (std::size_t i = 1; i != n; ++i) {
68     res += ", ";
69     res += context_factories[i].first;
70   }
71   return res;
72 }
73
74 static simgrid::config::Flag<std::string> context_factory_name(
75   "contexts/factory",
76   (std::string("Possible values: ")+contexts_list()).c_str(),
77   context_factories[0].first);
78
79 unsigned smx_context_stack_size;
80 int smx_context_stack_size_was_set = 0;
81 unsigned smx_context_guard_size;
82 int smx_context_guard_size_was_set = 0;
83 #if HAVE_THREAD_LOCAL_STORAGE
84 static XBT_THREAD_LOCAL smx_context_t smx_current_context_parallel;
85 #else
86 static xbt_os_thread_key_t smx_current_context_key = 0;
87 #endif
88 static smx_context_t smx_current_context_serial;
89 static int smx_parallel_contexts = 1;
90 static int smx_parallel_threshold = 2;
91 static e_xbt_parmap_mode_t smx_parallel_synchronization_mode = XBT_PARMAP_DEFAULT;
92
93 /**
94  * This function is called by SIMIX_global_init() to initialize the context module.
95  */
96 void SIMIX_context_mod_init()
97 {
98   xbt_assert(simix_global->context_factory == nullptr);
99
100 #if HAVE_THREAD_CONTEXTS && not HAVE_THREAD_LOCAL_STORAGE
101   /* the __thread storage class is not available on this platform:
102    * use getspecific/setspecific instead to store the current context in each thread */
103   xbt_os_thread_key_create(&smx_current_context_key);
104 #endif
105
106 #if defined(__APPLE__) || defined(__NetBSD__)
107   std::string priv = xbt_cfg_get_string("smpi/privatization");
108   if (context_factory_name == "thread" && (priv == "dlopen" || priv == "yes" || priv == "default" || priv == "1")) {
109     XBT_WARN("dlopen+thread broken on Apple and BSD. Switching to raw contexts.");
110     context_factory_name = "raw";
111   }
112 #endif
113 #if defined(__FreeBSD__)
114   if (xbt_cfg_get_string("smpi/privatization") == "mmap") {
115     xbt_cfg_set_string("smpi/privatization", "dlopen");
116   }
117
118   if (context_factory_name == "thread" && xbt_cfg_get_string("smpi/privatization") != "no"){
119     XBT_WARN("mmap broken on FreeBSD, but dlopen+thread broken too. Switching to dlopen+raw contexts.");
120     context_factory_name = "raw";
121   }
122
123 #endif
124
125   /* select the context factory to use to create the contexts */
126   if (simgrid::kernel::context::factory_initializer) { // Give Java a chance to hijack the factory mechanism
127     simix_global->context_factory = simgrid::kernel::context::factory_initializer();
128     return;
129   }
130   /* use the factory specified by --cfg=contexts/factory:value */
131   for (auto const& factory : context_factories)
132     if (context_factory_name == factory.first) {
133       simix_global->context_factory = factory.second();
134       break;
135     }
136
137   if (simix_global->context_factory == nullptr) {
138     XBT_ERROR("Invalid context factory specified. Valid factories on this machine:");
139 #if HAVE_RAW_CONTEXTS
140     XBT_ERROR("  raw: high performance context factory implemented specifically for SimGrid");
141 #else
142     XBT_ERROR("  (raw contexts were disabled at compilation time on this machine -- check configure logs for details)");
143 #endif
144 #if HAVE_UCONTEXT_CONTEXTS
145     XBT_ERROR("  ucontext: classical system V contexts (implemented with makecontext, swapcontext and friends)");
146 #else
147     XBT_ERROR("  (ucontext was disabled at compilation time on this machine -- check configure logs for details)");
148 #endif
149 #if HAVE_BOOST_CONTEXTS
150     XBT_ERROR("  boost: this uses the boost libraries context implementation");
151 #else
152     XBT_ERROR("  (boost was disabled at compilation time on this machine -- check configure logs for details. Did you install the libboost-context-dev package?)");
153 #endif
154     XBT_ERROR("  thread: slow portability layer using pthreads as provided by gcc");
155     xbt_die("Please use a valid factory.");
156   }
157 }
158
159 /**
160  * This function is called by SIMIX_clean() to finalize the context module.
161  */
162 void SIMIX_context_mod_exit()
163 {
164   delete simix_global->context_factory;
165   simix_global->context_factory = nullptr;
166 }
167
168 void *SIMIX_context_stack_new()
169 {
170   void *stack;
171
172   /* FIXME: current code for stack overflow protection assumes that stacks are
173    * growing downward (PTH_STACKGROWTH == -1).  Protected pages need to be put
174    * after the stack when PTH_STACKGROWTH == 1. */
175
176   if (smx_context_guard_size > 0 && not MC_is_active()) {
177
178 #if !defined(PTH_STACKGROWTH) || (PTH_STACKGROWTH != -1)
179     static int warned_once = 0;
180     if (not warned_once) {
181       XBT_WARN("Stack overflow protection is known to be broken on your system.  Either stack grows upwards, or it was not even tested properly.");
182       warned_once = 1;
183     }
184 #endif
185
186     size_t size = smx_context_stack_size + smx_context_guard_size;
187 #if SIMGRID_HAVE_MC
188     /* Cannot use posix_memalign when SIMGRID_HAVE_MC. Align stack by hand, and save the
189      * pointer returned by xbt_malloc0. */
190     char *alloc = (char*)xbt_malloc0(size + xbt_pagesize);
191     stack = alloc - ((uintptr_t)alloc & (xbt_pagesize - 1)) + xbt_pagesize;
192     *((void **)stack - 1) = alloc;
193 #elif !defined(_WIN32)
194     if (posix_memalign(&stack, xbt_pagesize, size) != 0)
195       xbt_die("Failed to allocate stack.");
196 #else
197     stack = _aligned_malloc(size, xbt_pagesize);
198 #endif
199
200 #ifndef _WIN32
201     if (mprotect(stack, smx_context_guard_size, PROT_NONE) == -1) {
202       xbt_die(
203           "Failed to protect stack: %s.\n"
204           "If you are running a lot of actors, you may be exceeding the amount of mappings allowed per process.\n"
205           "On Linux systems, change this value with sudo sysctl -w vm.max_map_count=newvalue (default value: 65536)\n"
206           "Please see http://simgrid.gforge.inria.fr/simgrid/latest/doc/html/options.html#options_virt for more info.",
207           strerror(errno));
208       /* This is fatal. We are going to fail at some point when we try reusing this. */
209     }
210 #endif
211     stack = (char *)stack + smx_context_guard_size;
212   } else {
213     stack = xbt_malloc0(smx_context_stack_size);
214   }
215
216 #if HAVE_VALGRIND_H
217   unsigned int valgrind_stack_id = VALGRIND_STACK_REGISTER(stack, (char *)stack + smx_context_stack_size);
218   memcpy((char *)stack + smx_context_usable_stack_size, &valgrind_stack_id, sizeof valgrind_stack_id);
219 #endif
220
221   return stack;
222 }
223
224 void SIMIX_context_stack_delete(void *stack)
225 {
226   if (not stack)
227     return;
228
229 #if HAVE_VALGRIND_H
230   unsigned int valgrind_stack_id;
231   memcpy(&valgrind_stack_id, (char *)stack + smx_context_usable_stack_size, sizeof valgrind_stack_id);
232   VALGRIND_STACK_DEREGISTER(valgrind_stack_id);
233 #endif
234
235 #ifndef _WIN32
236   if (smx_context_guard_size > 0 && not MC_is_active()) {
237     stack = (char *)stack - smx_context_guard_size;
238     if (mprotect(stack, smx_context_guard_size, PROT_READ | PROT_WRITE) == -1) {
239       XBT_WARN("Failed to remove page protection: %s", strerror(errno));
240       /* try to pursue anyway */
241     }
242 #if SIMGRID_HAVE_MC
243     /* Retrieve the saved pointer.  See SIMIX_context_stack_new above. */
244     stack = *((void **)stack - 1);
245 #endif
246   }
247 #endif /* not windows */
248
249   xbt_free(stack);
250 }
251
252 /** @brief Returns whether some parallel threads are used for the user contexts. */
253 int SIMIX_context_is_parallel() {
254   return smx_parallel_contexts > 1;
255 }
256
257 /**
258  * @brief Returns the number of parallel threads used for the user contexts.
259  * \return the number of threads (1 means no parallelism)
260  */
261 int SIMIX_context_get_nthreads() {
262   return smx_parallel_contexts;
263 }
264
265 /**
266  * \brief Sets the number of parallel threads to use
267  * for the user contexts.
268  *
269  * This function should be called before initializing SIMIX.
270  * A value of 1 means no parallelism (1 thread only).
271  * If the value is greater than 1, the thread support must be enabled.
272  *
273  * \param nb_threads the number of threads to use
274  */
275 void SIMIX_context_set_nthreads(int nb_threads) {
276   if (nb_threads<=0) {
277      nb_threads = xbt_os_get_numcores();
278      XBT_INFO("Auto-setting contexts/nthreads to %d",nb_threads);
279   }
280 #if !HAVE_THREAD_CONTEXTS
281   xbt_assert(nb_threads == 1, "Parallel runs are impossible when the pthreads are missing.");
282 #endif
283   smx_parallel_contexts = nb_threads;
284 }
285
286 /**
287  * \brief Returns the threshold above which user processes are run in parallel.
288  *
289  * If the number of threads is set to 1, there is no parallelism and this
290  * threshold has no effect.
291  *
292  * \return when the number of user processes ready to run is above
293  * this threshold, they are run in parallel
294  */
295 int SIMIX_context_get_parallel_threshold() {
296   return smx_parallel_threshold;
297 }
298
299 /**
300  * \brief Sets the threshold above which user processes are run in parallel.
301  *
302  * If the number of threads is set to 1, there is no parallelism and this
303  * threshold has no effect.
304  *
305  * \param threshold when the number of user processes ready to run is above
306  * this threshold, they are run in parallel
307  */
308 void SIMIX_context_set_parallel_threshold(int threshold) {
309   smx_parallel_threshold = threshold;
310 }
311
312 /**
313  * \brief Returns the synchronization mode used when processes are run in
314  * parallel.
315  * \return how threads are synchronized if processes are run in parallel
316  */
317 e_xbt_parmap_mode_t SIMIX_context_get_parallel_mode() {
318   return smx_parallel_synchronization_mode;
319 }
320
321 /**
322  * \brief Sets the synchronization mode to use when processes are run in
323  * parallel.
324  * \param mode how to synchronize threads if processes are run in parallel
325  */
326 void SIMIX_context_set_parallel_mode(e_xbt_parmap_mode_t mode) {
327   smx_parallel_synchronization_mode = mode;
328 }
329
330 /**
331  * \brief Returns the current context of this thread.
332  * \return the current context of this thread
333  */
334 smx_context_t SIMIX_context_get_current()
335 {
336   if (SIMIX_context_is_parallel()) {
337 #if HAVE_THREAD_LOCAL_STORAGE
338     return smx_current_context_parallel;
339 #else
340     return xbt_os_thread_get_specific(smx_current_context_key);
341 #endif
342   }
343   else {
344     return smx_current_context_serial;
345   }
346 }
347
348 /**
349  * \brief Sets the current context of this thread.
350  * \param context the context to set
351  */
352 void SIMIX_context_set_current(smx_context_t context)
353 {
354   if (SIMIX_context_is_parallel()) {
355 #if HAVE_THREAD_LOCAL_STORAGE
356     smx_current_context_parallel = context;
357 #else
358     xbt_os_thread_set_specific(smx_current_context_key, context);
359 #endif
360   }
361   else {
362     smx_current_context_serial = context;
363   }
364 }