X-Git-Url: http://info.iut-bm.univ-fcomte.fr/pub/gitweb/simgrid.git/blobdiff_plain/614890d870519622efbffad18e0bad9643c95019..c529a2898e4b141aa20eaf1561ff8afb5e0cc259:/src/simix/BoostContext.cpp diff --git a/src/simix/BoostContext.cpp b/src/simix/BoostContext.cpp new file mode 100644 index 0000000000..f2e5ceeeda --- /dev/null +++ b/src/simix/BoostContext.cpp @@ -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 + +#include + +#include +#include + +#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::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( + code, argc, argv, cleanup_func, process); +#else + xbt_die("No support for parallel execution"); +#endif + else + context = this->new_context( + 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(arg); + BoostContext* context = static_cast(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(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(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( + 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(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( + 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(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(); +} + +} +}