Logo AND Algorithmique Numérique Distribuée

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