text: /* Copyright 2019 Benjamin Worpitz
   *
   * This file is part of alpaka.
   *
   * This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/.
   */
  
  #pragma once
  
  #ifdef ALPAKA_ACC_CPU_B_SEQ_T_FIBERS_ENABLED
  
  #include <alpaka/block/sync/Traits.hpp>
  
  #include <alpaka/core/Fibers.hpp>
  
  #include <alpaka/core/Common.hpp>
  
  #include <mutex>
  #include <map>
  
  namespace alpaka
  {
      namespace block
      {
          namespace sync
          {
              //#############################################################################
              //! The thread id map barrier block synchronization.
              template<
                  typename TIdx>
              class BlockSyncBarrierFiber : public concepts::Implements<ConceptBlockSync, BlockSyncBarrierFiber<TIdx>>
              {
              public:
                  //-----------------------------------------------------------------------------
                  ALPAKA_FN_HOST BlockSyncBarrierFiber(
                      TIdx const & blockThreadCount) :
                          m_barrier(static_cast<std::size_t>(blockThreadCount)),
                          m_threadCount(blockThreadCount),
                          m_curThreadCount(static_cast<TIdx>(0u)),
                          m_generation(static_cast<TIdx>(0u))
                  {}
                  //-----------------------------------------------------------------------------
                  ALPAKA_FN_HOST BlockSyncBarrierFiber(BlockSyncBarrierFiber const &) = delete;
                  //-----------------------------------------------------------------------------
                  ALPAKA_FN_HOST BlockSyncBarrierFiber(BlockSyncBarrierFiber &&) = delete;
                  //-----------------------------------------------------------------------------
                  ALPAKA_FN_HOST auto operator=(BlockSyncBarrierFiber const &) -> BlockSyncBarrierFiber & = delete;
                  //-----------------------------------------------------------------------------
                  ALPAKA_FN_HOST auto operator=(BlockSyncBarrierFiber &&) -> BlockSyncBarrierFiber & = delete;
                  //-----------------------------------------------------------------------------
                  /*virtual*/ ~BlockSyncBarrierFiber() = default;
  
                  boost::fibers::barrier mutable m_barrier;
  
                  TIdx mutable m_threadCount;
                  TIdx mutable m_curThreadCount;
                  TIdx mutable m_generation;
                  int mutable m_result[2u];
              };
  
              namespace traits
              {
                  //#############################################################################
                  template<
                      typename TIdx>
                  struct SyncBlockThreads<
                      BlockSyncBarrierFiber<TIdx>>
                  {
                      //-----------------------------------------------------------------------------
                      ALPAKA_FN_HOST static auto syncBlockThreads(
                          block::sync::BlockSyncBarrierFiber<TIdx> const & blockSync)
                      -> void
                      {
                          blockSync.m_barrier.wait();
                      }
                  };
  
                  //#############################################################################
                  template<
                      typename TOp,
                      typename TIdx>
                  struct SyncBlockThreadsPredicate<
                      TOp,
                      BlockSyncBarrierFiber<TIdx>>
                  {
                      //-----------------------------------------------------------------------------
                      ALPAKA_NO_HOST_ACC_WARNING
                      ALPAKA_FN_ACC static auto syncBlockThreadsPredicate(
                          block::sync::BlockSyncBarrierFiber<TIdx> const & blockSync,
                          int predicate)
                      -> int
                      {
                          if(blockSync.m_curThreadCount == blockSync.m_threadCount)
                          {
                              blockSync.m_curThreadCount = static_cast<TIdx>(0u);
                              ++blockSync.m_generation;
                          }
  
                          auto const generationMod2(blockSync.m_generation % static_cast<TIdx>(2u));
  
                          // The first fiber will reset the value to the initial value.
                          if(blockSync.m_curThreadCount == static_cast<TIdx>(0u))
                          {
                              blockSync.m_result[generationMod2] = TOp::InitialValue;
                          }
  
                          ++blockSync.m_curThreadCount;
  
                          // We do not have to lock because there is only ever one fiber active per block.
                          blockSync.m_result[generationMod2] = TOp()(blockSync.m_result[generationMod2], predicate);
  
                          // After all block threads have combined their values ...
                          blockSync.m_barrier.wait();
  
                          // ... the result can be returned.
                          return blockSync.m_result[generationMod2];
                      }
                  };
              }
          }
      }
  }
  
  #endif
  