Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
[simix] Move BoostContext to C++
[simgrid.git] / src / simix / BoostContext.cpp
diff --git a/src/simix/BoostContext.cpp b/src/simix/BoostContext.cpp
new file mode 100644 (file)
index 0000000..f2e5cee
--- /dev/null
@@ -0,0 +1,298 @@
+/* Copyright (c) 2015. The SimGrid Team.
+ * All rights reserved.                                                     */
+
+/* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the license (GNU LGPL) which comes with this package. */
+
+/** @file BoostContext.cpp Userspace context switching implementation based on Boost.Context */
+
+#include <cstdint>
+
+#include <boost/context/all.hpp>
+
+#include <xbt/log.h>
+#include <xbt/xbt_os_thread.h>
+
+#include "smx_private.h"
+#include "smx_private.hpp"
+#include "src/internal_config.h"
+#include "src/simix/BoostContext.hpp"
+
+XBT_LOG_EXTERNAL_DEFAULT_CATEGORY(simix_context);
+
+namespace simgrid {
+namespace simix {
+
+class BoostSerialContext : public BoostContext {
+public:
+  BoostSerialContext(xbt_main_func_t code,
+      int argc, char **argv,
+      void_pfn_smxprocess_t cleanup_func,
+      smx_process_t process)
+    : BoostContext(code, argc, argv, cleanup_func, process) {}
+  void stop() override;
+  void suspend() override;
+  void resume();
+};
+
+#ifdef CONTEXT_THREADS
+class BoostParallelContext : public BoostContext {
+public:
+  BoostParallelContext(xbt_main_func_t code,
+      int argc, char **argv,
+      void_pfn_smxprocess_t cleanup_func,
+      smx_process_t process)
+    : BoostContext(code, argc, argv, cleanup_func, process) {}
+  void stop() override;
+  void suspend() override;
+  void resume();
+};
+#endif
+
+// BoostContextFactory
+
+bool                BoostContext::parallel_        = false;
+xbt_parmap_t        BoostContext::parmap_          = nullptr;
+unsigned long       BoostContext::threads_working_ = 0;
+xbt_os_thread_key_t BoostContext::worker_id_key_;
+unsigned long       BoostContext::process_index_   = 0;
+BoostContext*       BoostContext::maestro_context_ = nullptr;
+std::vector<BoostContext*> BoostContext::workers_context_;
+
+BoostContextFactory::BoostContextFactory()
+  : ContextFactory("BoostContextFactory")
+{
+  BoostContext::parallel_ = SIMIX_context_is_parallel();
+  if (BoostContext::parallel_) {
+#ifndef CONTEXT_THREADS
+    xbt_die("No thread support for parallel context execution");
+#else
+    int nthreads = SIMIX_context_get_nthreads();
+    BoostContext::parmap_ = xbt_parmap_new(nthreads, SIMIX_context_get_parallel_mode());
+    BoostContext::workers_context_.clear();
+    BoostContext::workers_context_.resize(nthreads, nullptr);
+    BoostContext::maestro_context_ = nullptr;
+    xbt_os_thread_key_create(&BoostContext::worker_id_key_);
+#endif
+  }
+}
+
+BoostContextFactory::~BoostContextFactory()
+{
+#ifdef CONTEXT_THREADS
+  if (BoostContext::parmap_) {
+    xbt_parmap_destroy(BoostContext::parmap_);
+    BoostContext::parmap_ = nullptr;
+  }
+  BoostContext::workers_context_.clear();
+#endif
+}
+
+smx_context_t BoostContextFactory::create_context(
+  xbt_main_func_t code, int argc, char ** argv,
+  void_pfn_smxprocess_t cleanup_func, smx_process_t process)
+{
+  BoostContext* context = nullptr;
+  if (BoostContext::parallel_)
+#ifdef CONTEXT_THREADS
+    context = this->new_context<BoostParallelContext>(
+      code, argc, argv, cleanup_func, process);
+#else
+    xbt_die("No support for parallel execution");
+#endif
+  else
+    context = this->new_context<BoostSerialContext>(
+      code, argc, argv, cleanup_func, process);
+  return context;
+}
+
+void BoostContextFactory::run_all()
+{
+#ifdef CONTEXT_THREADS
+  if (BoostContext::parallel_) {
+    BoostContext::threads_working_ = 0;
+    xbt_parmap_apply(BoostContext::parmap_,
+      [](void* arg) {
+        smx_process_t process = static_cast<smx_process_t>(arg);
+        BoostContext* context  = static_cast<BoostContext*>(process->context);
+        return context->resume();
+      },
+      simix_global->process_to_run);
+  } else
+#endif
+  {
+    smx_process_t first_process =
+        xbt_dynar_get_as(simix_global->process_to_run, 0, smx_process_t);
+    BoostContext::process_index_ = 1;
+    /* execute the first process */
+    static_cast<BoostContext*>(first_process->context)->resume();
+  }
+}
+
+
+// BoostContext
+
+static void smx_ctx_boost_wrapper(std::intptr_t arg)
+{
+  BoostContext* context = (BoostContext*) arg;
+  (*context)();
+  context->stop();
+}
+
+BoostContext::BoostContext(xbt_main_func_t code,
+    int argc, char **argv,
+    void_pfn_smxprocess_t cleanup_func,
+    smx_process_t process)
+  : Context(code, argc, argv, cleanup_func, process)
+{
+
+  /* if the user provided a function for the process then use it,
+     otherwise it is the context for maestro */
+  if (code) {
+    this->stack_ = SIMIX_context_stack_new();
+    // We need to pass the bottom of the stack to make_fcontext,
+    // depending on the stack direction it may be the lower or higher address:
+  #if PTH_STACKGROWTH == -1
+    void* stack = (char*) this->stack_ + smx_context_usable_stack_size - 1;
+  #else
+    void* stack = this->stack_;
+  #endif
+    this->fc_ = boost::context::make_fcontext(
+                      stack,
+                      smx_context_usable_stack_size,
+                      smx_ctx_boost_wrapper);
+  } else {
+    #if HAVE_BOOST_CONTEXT == 1
+    this->fc_ = new boost::context::fcontext_t();
+    #endif
+    if (BoostContext::maestro_context_ == nullptr)
+      BoostContext::maestro_context_ = this;
+  }
+}
+
+BoostContext::~BoostContext()
+{
+#if HAVE_BOOST_CONTEXT == 1
+  if (!this->stack_)
+    delete this->fc_;
+#endif
+  if (this == maestro_context_)
+    maestro_context_ = nullptr;
+  SIMIX_context_stack_delete(this->stack_);
+}
+
+// BoostSerialContext
+
+void BoostContext::resume()
+{
+  SIMIX_context_set_current(this);
+#if HAVE_BOOST_CONTEXT == 1
+  boost::context::jump_fcontext(
+    maestro_context_->fc_, this->fc_,
+    (intptr_t) this);
+#else
+  boost::context::jump_fcontext(
+    &boost_maestro_context_->fc_, this->fc_,
+    (intptr_t) this);
+#endif
+}
+
+void BoostSerialContext::suspend()
+{
+  /* determine the next context */
+  BoostSerialContext* next_context = nullptr;
+  unsigned long int i = process_index_++;
+
+  if (i < xbt_dynar_length(simix_global->process_to_run)) {
+    /* execute the next process */
+    XBT_DEBUG("Run next process");
+    next_context = static_cast<BoostSerialContext*>(xbt_dynar_get_as(
+        simix_global->process_to_run, i, smx_process_t)->context);
+  }
+  else {
+    /* all processes were run, return to maestro */
+    XBT_DEBUG("No more process to run");
+    next_context = static_cast<BoostSerialContext*>(
+      maestro_context_);
+  }
+  SIMIX_context_set_current((smx_context_t) next_context);
+  #if HAVE_BOOST_CONTEXT == 1
+  boost::context::jump_fcontext(
+    this->fc_, next_context->fc_, (intptr_t) next_context);
+  #else
+  boost::context::jump_fcontext(
+    &this->fc_, next_context->fc_, (intptr_t) next_context);
+  #endif
+}
+
+void BoostSerialContext::stop()
+{
+  BoostContext::stop();
+  this->suspend();
+}
+
+// BoostParallelContext
+
+#ifdef CONTEXT_THREADS
+
+void BoostParallelContext::suspend()
+{
+  smx_process_t next_work = (smx_process_t) xbt_parmap_next(parmap_);
+  BoostParallelContext* next_context = nullptr;
+
+  if (next_work != nullptr) {
+    XBT_DEBUG("Run next process");
+    next_context = static_cast<BoostParallelContext*>(next_work->context);
+  }
+  else {
+    XBT_DEBUG("No more processes to run");
+    unsigned long worker_id =
+      (unsigned long) xbt_os_thread_get_specific(worker_id_key_);
+    next_context = static_cast<BoostParallelContext*>(
+      workers_context_[worker_id]);
+  }
+
+  SIMIX_context_set_current((smx_context_t) next_context);
+#if HAVE_BOOST_CONTEXT == 1
+  boost::context::jump_fcontext(
+    this->fc_, next_context->fc_, (intptr_t)next_context);
+#else
+  boost::context::jump_fcontext(
+    &this->fc_, next_context->fc_, (intptr_t)next_context);
+#endif
+}
+
+void BoostParallelContext::stop()
+{
+  BoostContext::stop();
+  this->suspend();
+}
+
+void BoostParallelContext::resume()
+{
+  unsigned long worker_id = __sync_fetch_and_add(&threads_working_, 1);
+  xbt_os_thread_set_specific(worker_id_key_, (void*) worker_id);
+
+  BoostParallelContext* worker_context =
+    static_cast<BoostParallelContext*>(SIMIX_context_self());
+  workers_context_[worker_id] = worker_context;
+
+  SIMIX_context_set_current(this);
+#if HAVE_BOOST_CONTEXT == 1
+  boost::context::jump_fcontext(
+    worker_context->fc_, this->fc_, (intptr_t) this);
+#else
+  boost::context::jump_fcontext(
+    &worker_context->fc_, this->fc_, (intptr_t) this);
+#endif
+}
+
+#endif
+
+XBT_PRIVATE ContextFactory* boost_factory()
+{
+  return new BoostContextFactory();
+}
+
+}
+}