5

I'm trying to use a mutex to prevent multiple threads from reading variables at the same time.

My code generates multiple "Carriers" objects all having the same "SMSDetector" and the code crashes (segmentation fault) when trying to evaluate some Dolfin functions (yes I use Fenics libraries for some FEM calculations); so I'm trying to lock the mutex before evaluation and unlock afterwards.

My problem now is that I need copy/move constructors for Carrier class to be mov/copi-able but I don't seem to get them right. Without the mutex I get: Carrier.cpp:203:42: error: ‘Carrier& operator=(const Carrier&)’ must be a nonstatic member function Carrier& operator = (const Carrier& other)

and with the mutex I get: Carrier.cpp:200:49: error: no matching function for call to ‘std::lock_guard<std::mutex>::lock_guard(const std::mutex&)’ std::lock_guard<std::mutex> lock(other.safeRead);

I'm trying to write the whole program in c++11 standard, so if anyone has a c++11 solution would be really appreciated.

Here is the conflicting code:

std::valarray<double> Carrier::simulate_drift(double dt, double max_time, double x_init, double y_init )

{
      _x[0] = x_init;
      _x[1] = y_init;

      // get number of steps from time
  int max_steps = (int) std::floor(max_time / dt);

  std::valarray<double>  i_n(max_steps); // valarray to save intensity

  runge_kutta4<std::array< double,2>> stepper;

  // wrapper for the arrays using dolphin array class
  Array<double> wrap_x(2, _x.data());
  Array<double> wrap_e_field(2, _e_field.data());
  Array<double> wrap_w_field(2, _w_field.data());

  double t=0.0; // Start at time = 0

  for ( int i = 0 ; i < max_steps; i++)
  {

    if (t < _gen_time) // If CC not yet generated
    {   
      i_n[i] = 0;
    }   
    else if (_detector->is_out(_x)) // If CC outside detector
    {   
      i_n[i] = 0;
      break; // Finish (CC gone out)
    }   
    else
    {   
      safeRead.lock();
      _detector->get_d_f_grad()->eval(wrap_e_field, wrap_x);
      _detector->get_w_f_grad()->eval(wrap_w_field, wrap_x);
      safeRead.unlock();
      _e_field_mod = sqrt(_e_field[0]*_e_field[0] + _e_field[1]*_e_field[1]);
      i_n[i] = _q *_sign* _mu.obtain_mobility(_e_field_mod) * (_e_field[0]*_w_field[0] + _e_field[1]*_w_field[1]);
      stepper.do_step(_drift, _x, t, dt);
      // Trapping effects due to radiation-induced defects (traps) implemented in CarrierColleciton.cpp
    }   
    t+=dt;
  }
    return i_n;
}
/*
 * Copy initialization
 */
Carrier::Carrier(const Carrier& other)
{
  _carrier_type = other._carrier_type;
    _q = other._q;
    _gen_time = other._gen_time;
    _x = other._x; 
    _e_field = other._e_field; 
    _w_field = other._w_field;
    _e_field_mod = other._e_field_mod;
    _sign = other._sign; 
    _detector = other._detector;
    _myTemp = other._myTemp;
    _drift = other._drift;
    _mu = other._mu;
    _trapping_time = other._trapping_time;
    std::lock_guard<std::mutex> lock(other.safeRead);
}

/*
 * Copy assignment
 */
Carrier& operator = (const Carrier& other) 
{
//  std::lock(safeRead, other.safeRead;
//  std::lock_guard<std::mutex> self_lock(safeRead, std::adopt_lock;
//  std::lock_guard<std::mutex> other_lock(other.safeRead, std::adopt_lock;
    std::lock_guard<std::mutex> self_lock(safeRead, std::adopt_lock);
    std::lock_guard<std::mutex> other_lock(other.safeRead, std::adopt_lock);
  _carrier_type = other._carrier_type;
    _q = other._q;
    _gen_time = other._gen_time;
    _x = other._x; 
    _e_field = other._e_field; 
    _w_field = other._w_field;
    _e_field_mod = other._e_field_mod;
    _sign = other._sign; 
    _detector = other._detector;
    _myTemp = other._myTemp;
    _drift = other._drift;
    _trapping_time = other._trapping_time;
    return *this;
}


/*
 * Move initialization
 */
Carrier::Carrier(Carrier&& other)
{
  _carrier_type = std::move(other._carrier_type);
    _q = std::move(other._q);
    _gen_time = std::move(other._gen_time);
    _x = std::move(other._x); 
    _e_field = std::move(other._e_field); 
    _w_field = std::move(other._w_field);
    _e_field_mod = std::move(other._e_field_mod);
    _sign = std::move(other._sign); 
    _detector = std::move(other._detector);
    _myTemp = std::move(other._myTemp);
    _drift = std::move(other._drift);
    _mu = std::move(other._mu);
    _trapping_time = std::move(other._trapping_time);
    std::lock_guard<std::mutex> lock(other.safeRead);
}

/*
 * Move assignment
 */
Carrier& operator = ( Carrier&& other) 
{
    std::lock(safeRead, other.safeRead);
    std::lock_guard<std::mutex> self_lock(safeRead, std::adopt_lock);
    std::lock_guard<std::mutex> other_lock(other.safeRead, std::adopt_lock);
  _carrier_type = std::move(other._carrier_type);
    other._carrier_type = NULL;
    _q = std::move(other._q);
    other._q = 0;
    _gen_time = std::move(other._gen_time);
    other._gen_time = 0;
    _x = std::move(other._x); 
    other._x = {0,0};
    _e_field = std::move(other._e_field); 
    other._e_field = {0,0};
    _w_field = std::move(other._w_field);
    other._w_field = {0,0};
    _e_field_mod = std::move(other._e_field_mod);
    other._e_field_mod = 0;
    _sign = std::move(other._sign); 
    other._sign = 0;
    _detector = std::move(other._detector);
    other._detector = NULL;
    _myTemp = std::move(other._myTemp);
    other._myTemp = 0;
    _drift = std::move(other._drift);
    _mu = std::move(other._mu);
    _trapping_time = std::move(other._trapping_time);
    other._trapping_time = 1e300;
    return *this;
}

I would upload more of the code if helpful but I feel this is enough.

Thanks in advance for the help

EDIT: Class declaration (full header file):

#ifndef CARRIER_H
#define CARRIER_H

#include  <valarray>
#include  <mutex>

#include <CarrierTransport.h>
#include <SMSDetector.h>

#ifndef Q_MOC_RUN  // See: https://bugreports.qt-project.org/browse/QTBUG-22829
#include <boost/numeric/odeint/stepper/runge_kutta4.hpp>
#endif

using namespace boost::numeric::odeint;

class Carrier
{
  private:
    char _carrier_type;
    double _q; // charge
    double _gen_time; // instant of generation of the carrier
    std::array< double,2> _x; // carrier position array
    std::array< double,2> _e_field; // electric field at the carrier position
    std::array< double,2> _w_field; // weighting field at the carrier positions
    double _e_field_mod;
    int _sign; // sign to describe if carrier moves in e field direction or opposite
//      std::mutex safeRead;

    SMSDetector * _detector;
    double _myTemp; // Temperature of the detector
    DriftTransport _drift;
    JacoboniMobility _mu;
    double _trapping_time;

  public:
    Carrier( char carrier_type, double q, double x_init, double y_init, SMSDetector * detector, double gen_time);
        Carrier(Carrier&& other); // Move declaration
        Carrier& operator = (Carrier&& other); // Move assignment
        Carrier(const Carrier& other); // Copy declaration
        Carrier& operator = (const Carrier& other); // Copy Assignment
        ~Carrier();

    char get_carrier_type();

    std::array< double,2> get_x();
    double get_q();

    std::valarray<double> simulate_drift( double dt, double max_time);
    std::valarray<double> simulate_drift(double dt, double max_time, double x_init, double y_init );
};

#endif // CARRIER_H
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
Al Gepé
  • 53
  • 1
  • 5

1 Answers1

1

In your implementation of operator=, you need to specify the class name:

Carrier& Carrier::operator = (const Carrier& other).

You're getting the compilation error you described when locking the mutex because std::mutex::lock() is not a const method. In the assignment operator, you take the other parameter by const reference, as you should. Thus, it's trying to call a non-const function on a const object, causing the compilation error.

The workaround is to declare the member as mutable in the declaration of the Carrier class:

mutable std::mutex safeRead;

This allows you to work around the compile error.

Jason R
  • 11,159
  • 6
  • 50
  • 81