samedi 29 janvier 2022

Problem displaying more of my progress bar class indicators simultaneously using threads, why?

I am trying to implement a feature to print multiple progress bars on the screen using threads. I have a ProgressBar class which produces progress bars (in case you need more info about it you can look here), you need to know the definition of its update method (again, more info about the meanings of all the variables are in the link above):

  template <typename bar_type>
  void ProgressBar <bar_type>::update_output( bar_type iterating_var, std::string output )
   {
    std::unique_lock <std::mutex> lock{mutex_};
    std::cout << check_condition( [ = ]{ return iterating_var == min_; }, feat( tcs, "hcrs" ), null_str )
              << output
              << getColor()
              << empty_space + message_
              << reset( "color" )
              << std::flush
              << check_condition( [ = ]{ return iterating_var == max_ - 1; }, feat( tcs, "scrs" ), null_str );
   }
 
  template <typename bar_type>
  void ProgressBar <bar_type>::update( bar_type iterating_var )
   {
    std::unique_lock <std::mutex> lock{mutex_};
    iterating_var_ = 100 * ( iterating_var - min_ ) / ( max_ - min_ - one( iterating_var ) );
    width_ = ( iterating_var_ + 1 ) / 4;

    if( styles_map_.at( "indicator" ).find( style_ ) != styles_map_.at( "indicator" ).end() )
     {
      output_ = feat( crs, "left", 100 ) + 
                getColor() +
                std::to_string( static_cast <int> ( round( iterating_var_ ++ ) ) ) +
                reset( "color" ) + 
                getStyle();

      update_output( iterating_var, output_ );
     }

    else if( styles_map_.at( "loader" ).find( style_ ) != styles_map_.at( "loader" ).end() )
     {
      output_ = feat( crs, "left", 100 ) + 
                getBrackets_open() + 
                getColor() + 
                getStyle() * width_ + 
                empty_space * ( 25 - width_ ) + 
                reset( "color" ) + 
                getBrackets_close();  
                     
      update_output( iterating_var, output_ );
     }

    else if ( style_.find( style_p_ ) != std::string::npos && style_.find( style_l_ ) != std::string::npos &&
              type_ == "complete"  )
     {
      output_= feat( crs, "left", 100 ) + 
               getBrackets_open() + 
               getColor() + 
               style_l_ * width_ + 
               empty_space * ( 25 - width_ ) + 
               reset( "color" ) +
               getBrackets_close() + 
               getColor() + 
               empty_space + 
               std::to_string( iterating_var_ ++ ) + 
               reset( "color" ) + 
               style_p_; 

      update_output( iterating_var, output_ );
     }
     
    else
     {
      throw std::runtime_error( "ProgressBar style has not been set!" );
     }
   }

The function feat( crs, "left", 100 ) is used to move cursor left and so on. This ProgressBar is used in main in this way for example:

  ProgressBar <int> percentage_bar;
  percentage_bar.setMin( 5 );
  percentage_bar.setMax ( 46 );
  percentage_bar.setStyle( "indicator", "%" );

  cout << "This is a normal percentage bar: " << endl;
  for ( int i = percentage_bar.getMin(); i < percentage_bar.getMax(); i++ )
   {
    sleep_for( milliseconds( 100 ) );
    percentage_bar.update( i );
    //Do some operations...
   }
  cout << endl << endl;

Then, I am creating a new MultiProgressBar class to manage multiple progress bars with threads and display them at the same time in the terminal.

#ifndef MULTIPROGRESSBAR_H
#define MULTIPROGRESSBAR_h

#include <iostream>
#include <type_traits>
#include <tuple>
#include <functional>
#include <mutex>
#include <atomic>
#include <utility>

namespace osm
 {
  template <size_t... Is>
  struct indices {};
  
  template <size_t N, size_t... Is>
  struct gen_indices: gen_indices <N - 1, N - 1, Is...> {};
  
  template <size_t... Is>
  struct gen_indices <0, Is...>: indices<Is...> {};
  
  template <class... Indicators>
  class make_MultiProgressBar 
   {
    public:
  
     template <class... Inds>
     make_MultiProgressBar( Inds&&... bars ): bars_{ std::forward <Inds> ( bars )... } {}
  
     static size_t size() { return sizeof...( Indicators ); }
  
     template <class Func, class... Args>
     void for_one( size_t idx, Func&& func, Args&&... args )
      {
       std::lock_guard<std::mutex> lock{mutex_};
       call_one( idx, gen_indices <sizeof...( Indicators )> (), std::forward <Func> ( func ), std::forward <Args> ( args )... );
      }
  
     template <class Func, class... Args>
     void for_each( Func&& func, Args&&... args ) 
      {
       std::lock_guard<std::mutex> lock{mutex_};
       call_all( gen_indices <sizeof...( Indicators )> (), std::forward <Func> ( func ), std::forward <Args> ( args )... );
      }
  
    private:
  
     template <size_t... Ids, class Func, class... Args>
     void call_one( size_t idx, indices <Ids...>, Func func, Args&&... args )
      {
       std::lock_guard<std::mutex> lock{mutex_};
       [](...) {} 
        (
         (idx == Ids &&
          ( ( void ) std::forward <Func> ( func )( std::get <Ids> ( bars_ ), 
              std::forward <Args> ( args )... ), false ) )...
        );   
      }
  
     template <size_t... Ids, class Func, class... Args>
     void call_all( indices <Ids...>, Func func, Args&&... args )
      {
       std::lock_guard<std::mutex> lock{mutex_};
       auto dummy = { ( func( std::get <Ids>( bars_ ), args...), 0 )... };
       ( void )dummy;
      } 
  
     std::tuple <Indicators&...> bars_;
     std::mutex mutex_;
     std::atomic <bool> started_{ false };
   };
  
  template <class... Indicators>
  make_MultiProgressBar <typename std::remove_reference <Indicators>::type...>
  MultiProgressBar( Indicators&&... inds ) 
   {
    return { std::forward <Indicators> ( inds )... };
   }
  
  template <class T> 
  struct type_identity 
   {
    using type = T;
   };
  
  struct updater 
   { 
    template <template <class> class PB, class bar_type>
    auto operator()( PB <bar_type>& pb, typename type_identity <bar_type>::type v ) const
        -> decltype( pb.update( bar_type{} ) ) 
     {
      return pb.update( v );
     }
   };
 }

#endif

and in a main should work as:

#include <iostream>
#include <thread>
#include <chrono>
#include <cmath>
#include <iomanip>
#include "../include/osmanip.h"

using namespace osm;
using namespace std;
using namespace std::this_thread;
using namespace std::chrono;
  ProgressBar<int> prog_int;
  prog_int.setMin( 0 );
  prog_int.setMax ( 100 );
  prog_int.setStyle( "complete", "%", "#" );
  prog_int.setBrackets( "[", "]" );

  ProgressBar<int> prog_int_2;
  prog_int_2.setMin( 5 );
  prog_int_2.setMax ( 25 );
  prog_int_2.setStyle( "complete", "%", "#" );
  prog_int_2.setBrackets( "[", "]" );

  ProgressBar<float> prog_float;
  prog_float.setMin( 0.1f );
  prog_float.setMax ( 12.1f );
  prog_float.setStyle( "complete", "%", "#" );
  prog_float.setBrackets( "[", "]" );

  auto bars = MultiProgressBar( prog_int, prog_int_2, prog_float );

  // Job for the first bar
  auto job1 = [&bars]() {
    for (int i = 0; i <= 100; i++) {
      bars.for_one(0, updater{}, i);
      sleep_for( milliseconds( 100 ) );
    }
    cout << endl;
  };

  // Job for the second bar
  auto job2 = [&bars]() {
    for (int i = 5; i <= 25; i++) {
      bars.for_one(1, updater{}, i);
      sleep_for(std::chrono::milliseconds(200));
    }
    cout << endl;
  };

  // Job for the third bar
  auto job3 = [&bars]() {
    for (float i = 0.1f; i <= 12.1f; i += 0.1f) {
      bars.for_one(2, updater{}, i);
      sleep_for(std::chrono::milliseconds(60));
    }
    cout << endl;
  };

  thread first_job(job1);
  thread second_job(job2);
  thread third_job(job3);

  first_job.join();
  second_job.join();
  third_job.join();

The problem is that when I compile with -pthread option (I am using c++17) all is fine, but when I run the code anything is displayed and the prompt continue waiting, like this:

130 [23:03] gianluca@ubuntu:~/osmanip (main)$ ./bin/main.exe 

doing anything. The makefile I use for compilation is:

TARGET_EXEC := main.exe
TEST_EXEC := tests.exe
LIB := libosmanip.a
CC := g++

BUILD_DIR := bin
SRC_DIR := src
OBJ_DIR := obj
TEST_DIR := test
LIB_DIR := lib

SRC := $(shell find $(SRC_DIR) -name '*.cpp')
SRC_LIB := $(shell find $(SRC_DIR) -type f | grep -v 'main.cpp')
TEST := $(shell find $(SRC_DIR) -type f | grep -v 'main.cpp') $(shell find $(TEST_DIR) -name '*.cpp')

OBJ := $(SRC:%=$(OBJ_DIR)/%.o)
OBJ_LIB := $(SRC_LIB:%=$(OBJ_DIR)/%.o)
TEST_OBJ := $(TEST:%=$(OBJ_DIR)/%.o)

DEPS := $(OBJ:.o=.d)
INC_DIR := $(shell find $(SRC_DIR) -type d)
INC_FLAGS := $(addprefix -I,$(INC_DIR))
CPPFLAGS := -std=c++17 -g $(INC_FLAGS) -MMD -MP
LDFLAGS := -pthread

.PHONY: clean all

#Building all:
all: $(BUILD_DIR)/$(TARGET_EXEC) $(BUILD_DIR)/$(TEST_EXEC) $(LIB_DIR)/$(LIB)

#Building main executable:
$(BUILD_DIR)/$(TARGET_EXEC): $(OBJ)
    @ mkdir -p $(dir $@)
    $(CC) $(OBJ) -o $@ $(LDFLAGS)

#Building test executable:
$(BUILD_DIR)/$(TEST_EXEC): $(TEST_OBJ)
    @ mkdir -p $(dir $@)
    $(CC) $(TEST_OBJ) -o $@ $(LDFLAGS)

#Put object files into the object dir:
$(OBJ_DIR)/%.cpp.o: %.cpp
    @ mkdir -p $(dir $@)
    $(CXX) $(CPPFLAGS) $(CXXFLAGS) -c $< -o $@

#Create a static library from object files and put it in the library dir:
$(LIB_DIR)/$(LIB): $(OBJ_LIB)
    @ mkdir -p $(dir $@)
    ar rcs $(LIB_DIR)/$(LIB) $(OBJ_LIB)

clean:
    rm -r $(OBJ_DIR) $(BUILD_DIR) $(LIB_DIR)

-include $(DEPS)

What could be the problem?

Aucun commentaire:

Enregistrer un commentaire