* reason to provide such a function is that the program can be
* compiled both in MT and non-MT mode without difference.
*
- * @author Wolfgang Bangerth, 2000
+ * @author Wolfgang Bangerth, 2000, 2003
*/
class DummyThreadMutex
{
public:
+ /**
+ * Scoped lock class. When you
+ * declare an object of this
+ * type, you have to pass it a
+ * mutex, which is locked in
+ * the constructor of this
+ * class and unlocked in the
+ * destructor. The lock is thus
+ * held during the entire
+ * lifetime of this object,
+ * i.e. until the end of the
+ * present scope, which
+ * explains where the name
+ * comes from. This pattern of
+ * using locks with mutexes
+ * follows the
+ * resource-acquisition-is-initialization
+ * pattern, and was used first
+ * for mutexes by Doug
+ * Schmidt. It has the
+ * advantage that locking a
+ * mutex this way is
+ * thread-safe, i.e. when an
+ * exception is thrown between
+ * the locking and unlocking
+ * point, the destructor makes
+ * sure that the mutex is
+ * unlocked; this would not
+ * automatically be the case
+ * when you lock and unlock the
+ * mutex "by hand", i.e. using
+ * @p{acquire} and @p{release}.
+ */
+ class ScopedLock
+ {
+ public:
+ /**
+ * Constructor. Lock the
+ * mutex. Since this is a
+ * dummy mutex class, this
+ * of course does nothing.
+ */
+ ScopedLock (DummyThreadMutex &) {};
+
+ /**
+ * Destructor. Unlock the
+ * mutex. Since this is a
+ * dummy mutex class, this
+ * of course does nothing.
+ */
+ ~ScopedLock () {};
+ };
+
/**
* Simulate acquisition of the
* mutex. As this class does
* Class implementing a Mutex with
* the help of POSIX functions.
*
- * @author Wolfgang Bangerth, 2002
+ * @author Wolfgang Bangerth, 2002, 2003
*/
class PosixThreadMutex
{
public:
+ /**
+ * Scoped lock class. When you
+ * declare an object of this
+ * type, you have to pass it a
+ * mutex, which is locked in
+ * the constructor of this
+ * class and unlocked in the
+ * destructor. The lock is thus
+ * held during the entire
+ * lifetime of this object,
+ * i.e. until the end of the
+ * present scope, which
+ * explains where the name
+ * comes from. This pattern of
+ * using locks with mutexes
+ * follows the
+ * resource-acquisition-is-initialization
+ * pattern, and was used first
+ * for mutexes by Doug
+ * Schmidt. It has the
+ * advantage that locking a
+ * mutex this way is
+ * thread-safe, i.e. when an
+ * exception is thrown between
+ * the locking and unlocking
+ * point, the destructor makes
+ * sure that the mutex is
+ * unlocked; this would not
+ * automatically be the case
+ * when you lock and unlock the
+ * mutex "by hand", i.e. using
+ * @p{acquire} and @p{release}.
+ */
+ class ScopedLock
+ {
+ public:
+ /**
+ * Constructor. Lock the
+ * mutex.
+ */
+ ScopedLock (PosixThreadMutex &m) : mutex(m) { mutex.acquire(); };
+
+ /**
+ * Destructor. Unlock the
+ * mutex. Since this is a
+ * dummy mutex class, this
+ * of course does nothing.
+ */
+ ~ScopedLock () { mutex.release (); };
+
+ private:
+ /**
+ * Store the address of the
+ * mutex object.
+ */
+ PosixThreadMutex &mutex;
+ };
+
/**
* Constructor. Initialize the
* underlying POSIX mutex data
unsigned int k = k_;
// first make sure that no other
- // thread intercepts the operation
- // of this function
- coefficients_lock.acquire ();
+ // thread intercepts the
+ // operation of this function;
+ // for this, acquire the lock
+ // until we quit this function
+ Threads::ThreadMutex::ScopedLock lock(coefficients_lock);
// The first 2 coefficients are hard-coded
if (k==0)
shifted_coefficients[k] = ck;
};
};
-
- // now, everything is done, so
- // release the lock again
- coefficients_lock.release ();
}
// then get a pointer to the array
// of coefficients. do that in a MT
// safe way
- coefficients_lock.acquire ();
- const std::vector<number> *p = shifted_coefficients[k];
- coefficients_lock.release ();
-
- // return the object pointed
- // to. since this object does not
- // change any more once computed,
- // this is MT safe
- return *p;
+ Threads::ThreadMutex::ScopedLock lock (coefficients_lock);
+ return *shifted_coefficients[k];
}
{
unsigned int k = k_;
- // first make sure that no other
- // thread intercepts the operation
- // of this function
- coefficients_lock.acquire ();
+ // first make sure that no other
+ // thread intercepts the operation
+ // of this function
+ // for this, acquire the lock
+ // until we quit this function
+ Threads::ThreadMutex::ScopedLock lock(coefficients_lock);
- // The first 2 coefficients
- // are hard-coded
+ // The first 2 coefficients
+ // are hard-coded
if (k==0)
k=1;
- // check: does the information
- // already exist?
+ // check: does the information
+ // already exist?
if ( (recursive_coefficients.size() < k+1) ||
((recursive_coefficients.size() >= k+1) && (recursive_coefficients[k] == 0)) )
- // no, then generate the
- // respective coefficients
+ // no, then generate the
+ // respective coefficients
{
recursive_coefficients.resize (k+1, 0);
if (k<=1)
{
- // create coefficients
- // vectors for k=0 and k=1
- //
- // allocate the respective
- // amount of memory and
- // later assign it to the
- // coefficients array to
- // make it const
+ // create coefficients
+ // vectors for k=0 and k=1
+ //
+ // allocate the respective
+ // amount of memory and
+ // later assign it to the
+ // coefficients array to
+ // make it const
std::vector<number> *c0 = new std::vector<number>(2);
(*c0)[0] = 1.0;
(*c0)[1] = -1.0;
(*c1)[0] = 0.0;
(*c1)[1] = 1.0;
- // now make these arrays
- // const
+ // now make these arrays
+ // const
recursive_coefficients[0] = c0;
recursive_coefficients[1] = c1;
}
}
else
{
- // for larger numbers,
- // compute the coefficients
- // recursively. to do so,
- // we have to release the
- // lock temporarily to
- // allow the called
- // function to acquire it
- // itself
+ // for larger numbers,
+ // compute the coefficients
+ // recursively. to do so,
+ // we have to release the
+ // lock temporarily to
+ // allow the called
+ // function to acquire it
+ // itself
coefficients_lock.release ();
compute_coefficients(k-1);
coefficients_lock.acquire ();
- (*recursive_coefficients[k-1])[i] );
(*ck)[k] = 2.*(*recursive_coefficients[k-1])[k-1];
- // for even degrees, we need
- // to add a multiple of
- // basis fcn phi_2
+ // for even degrees, we need
+ // to add a multiple of
+ // basis fcn phi_2
if ( (k%2) == 0 )
{
(*ck)[1] += (*recursive_coefficients[2])[1];
(*ck)[2] += (*recursive_coefficients[2])[2];
}
- // finally assign the newly
- // created vector to the
- // const pointer in the
- // coefficients array
+ // finally assign the newly
+ // created vector to the
+ // const pointer in the
+ // coefficients array
recursive_coefficients[k] = ck;
};
};
-
- // now, everything is done, so
- // release the lock again
- coefficients_lock.release ();
}
// $Id$
// Version: $Name$
//
-// Copyright (C) 1998, 1999, 2000, 2001, 2002 by the deal authors
+// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 by the deal authors
//
// This file is subject to QPL and may not be distributed
// without copyright and license information. Please refer
object_info = &typeid(*this);
#endif
- subscription_lock.acquire();
+ Threads::ThreadMutex::ScopedLock lock (subscription_lock);
++counter;
- subscription_lock.release();
}
void Subscriptor::unsubscribe () const {
Assert (counter>0, ExcNotUsed());
- subscription_lock.acquire();
+ Threads::ThreadMutex::ScopedLock lock (subscription_lock);
--counter;
- subscription_lock.release();
}
void register_new_thread ()
{
- n_existing_threads_mutex.acquire ();
+ ThreadMutex::ScopedLock lock (n_existing_threads_mutex);
++n_existing_threads_counter;
- n_existing_threads_mutex.release ();
}
void deregister_new_thread ()
{
- n_existing_threads_mutex.acquire ();
+ ThreadMutex::ScopedLock lock (n_existing_threads_mutex);
--n_existing_threads_counter;
Assert (n_existing_threads_counter >= 1,
ExcInternalError());
- n_existing_threads_mutex.release ();
}
unsigned int n_existing_threads ()
{
- n_existing_threads_mutex.acquire ();
+ ThreadMutex::ScopedLock lock (n_existing_threads_mutex);
const unsigned int n = n_existing_threads_counter;
- n_existing_threads_mutex.release ();
return n;
}
// wait for all threads, and
// release memory
wait ();
- list_mutex.acquire ();
+ ThreadMutex::ScopedLock lock (list_mutex);
if (thread_id_list != 0)
delete reinterpret_cast<std::list<pthread_t>*>(thread_id_list);
- list_mutex.release ();
}
{
std::list<pthread_t> &tid_list
= *reinterpret_cast<std::list<pthread_t>*>(thread_id_list);
-
- list_mutex.acquire ();
- tid_list.push_back (pthread_t());
- pthread_t *tid = &tid_list.back();
- list_mutex.release ();
-
+
+ {
+ ThreadMutex::ScopedLock lock (list_mutex);
+ tid_list.push_back (pthread_t());
+ pthread_t *tid = &tid_list.back();
+ }
+
// start new thread. retry until
// we either succeed or get an
// error other than EAGAIN
void
PosixThreadManager::wait () const
{
- list_mutex.acquire ();
+ ThreadMutex::ScopedLock lock (list_mutex);
std::list<pthread_t> &tid_list
= *reinterpret_cast<std::list<pthread_t>*>(thread_id_list);
// for expired threads with their
// invalid handles again
tid_list.clear ();
-
- list_mutex.release ();
}
# endif
<h3>deal.II</h3>
<ol>
+ <li> <p>
+ New: The <code class="class">ThreadMutex</code> classes now have a
+ member class <code class="class">ScopedLock</code> that implements the
+ scoped thread-safe locking pattern of Doug Schmidt. It is also used in
+ various places of the code now.
+ <br>
+ (WB 2003/01/28)
+ </p>
+
<li> <p>
Fixed: The <code class="class">GridReordering</code> tried to be
thread-safe in the initialization of some data, but was not due to a
// the domain. Since it's actions
// have all been explained in
// previous programs, we do not
- // comment on it any more.
+ // comment on it any more, except
+ // for one pointe below.
template <int dim>
void
Solver<dim>::assemble_matrix (LinearSystem &linear_system,
cell->get_dof_indices (local_dof_indices);
- mutex.acquire ();
+
+ // In the step-9 program, we
+ // have shown that you have
+ // to use the mutex to lock
+ // the matrix when copying
+ // the elements from the
+ // local to the global
+ // matrix. This was necessary
+ // to avoid that two threads
+ // access it at the same
+ // time, eventually
+ // overwriting their
+ // respective
+ // work. Previously, we have
+ // used the `acquire'' and
+ // ``release'' functions of
+ // the mutex to lock and
+ // unlock the mutex,
+ // respectively. While this
+ // is valid, there is one
+ // possible catch: if between
+ // the locking operation and
+ // the unlocking operation an
+ // exception is thrown, the
+ // mutex remains in the
+ // locked state, and in some
+ // cases this might lead to
+ // deadlocks. A similar
+ // situation arises, when one
+ // changes the code to have a
+ // return statement somewhere
+ // in the middle of the
+ // locked block, and forgets
+ // that before we call
+ // ``return'', we also have
+ // to unlock the mutex. This
+ // all is not be a problem
+ // here, but we want to show
+ // the general technique to
+ // cope with these problems
+ // nevertheless: have an
+ // object that upon
+ // initialization (i.e. in
+ // its constructor) locks the
+ // mutex, and on running the
+ // destructor unlocks it
+ // again. This is called the
+ // ``scoped lock'' pattern
+ // (apparently invented by
+ // Doug Schmidt originally),
+ // and it works because
+ // destructors of local
+ // objects are also run when
+ // we exit the function
+ // either through a
+ // ``return'' statement, or
+ // when an exception is
+ // raised. Thus, it is
+ // guaranteed that the mutex
+ // will always be unlocked
+ // when we exit this part of
+ // the program, whether the
+ // operation completed
+ // successfully or not,
+ // whether the exit path was
+ // something we implemented
+ // willfully or whether the
+ // function was exited by an
+ // exception that we did not
+ // forsee.
+ //
+ // deal.II implements the
+ // scoped locking pattern in
+ // the
+ // ThreadMutex::ScopedLock
+ // class: it takes the mutex
+ // in the constructor and
+ // locks it; in its
+ // destructor, it unlocks it
+ // again. So here is how it
+ // is used:
+ Threads::ThreadMutex::ScopedLock lock (mutex);
for (unsigned int i=0; i<dofs_per_cell; ++i)
for (unsigned int j=0; j<dofs_per_cell; ++j)
linear_system.matrix.add (local_dof_indices[i],
local_dof_indices[j],
cell_matrix(i,j));
- mutex.release ();
+ // Here, at the brace, the
+ // current scope ends, so the
+ // ``lock'' variable goes out
+ // of existence and its
+ // destructor the mutex is
+ // unlocked.
};
}
cell->get_dof_indices (local_dof_indices);
- mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (mutex);
for (unsigned int i=0; i<dofs_per_cell; ++i)
for (unsigned int j=0; j<dofs_per_cell; ++j)
linear_system.matrix.add (local_dof_indices[i],
local_dof_indices[j],
cell_matrix(i,j));
- mutex.release ();
};
}
// if in MT mode, block all other
// operations. if not in MT mode,
// this is a no-op
- lock.acquire ();
+ Threads::ThreadMutex::ScopedLock lock(this->lock);
Vector<number>::operator = (v);
data_is_preloaded = false;
- lock.release ();
-
return *this;
}
// if in MT mode, block all other
// operations. if not in MT mode,
// this is a no-op
- lock.acquire ();
+ Threads::ThreadMutex::ScopedLock lock(this->lock);
// check that we have not called
// @p{alert} without the respective
tmp_out.close ();
this->reinit (0);
-
- lock.release ();
}
// (there should be none, but who
// knows). if not in MT mode,
// this is a no-op
- lock.acquire();
+ Threads::ThreadMutex::ScopedLock lock(this->lock);
// this is too bad: someone
// requested the vector in advance,
filename = "";
};
-
- lock.release ();
}
GrowingVectorMemory<Vector>::GrowingVectorMemory(const unsigned int initial_size)
: pool(initial_size)
{
- mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock(mutex);
for (typename std::vector<entry_type>::iterator i=pool.begin();
i != pool.end();
++i)
// no vectors yet claimed
n_alloc = 0;
- mutex.release ();
}
template<typename Vector>
GrowingVectorMemory<Vector>::~GrowingVectorMemory()
{
- mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock(mutex);
// deallocate all vectors and count
// number of vectors that is still
deallog << "GrowingVectorMemory:Maximum allocated vectors: "
<< pool.size() << std::endl;
pool.clear ();
- mutex.release ();
// write out warning if memory leak
if (n!=0)
template<typename Vector>
-Vector*
+Vector *
GrowingVectorMemory<Vector>::alloc()
{
- mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock(mutex);
++n_alloc;
for (typename std::vector<entry_type>::iterator i=pool.begin();
i != pool.end();
if (i->first == false)
{
i->first = true;
- mutex.release ();
return (i->second);
}
}
- entry_type t;
- t.first = true;
- t.second = new Vector;
+
+ const entry_type t (true, new Vector);
pool.push_back(t);
- mutex.release ();
return t.second;
}
void
GrowingVectorMemory<Vector>::free(const Vector* const v)
{
- mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock(mutex);
for (typename std::vector<entry_type>::iterator i=pool.begin();i != pool.end() ;++i)
{
if (v == (i->second))
{
i->first = false;
- mutex.release ();
return;
}
}
- mutex.release ();
-
Assert(false, typename VectorMemory<Vector>::ExcNotAllocatedHere());
}
FilteredMatrix<SparseMatrix<double>,Vector<double> >::
allocate_tmp_vector ()
{
- tmp_mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (tmp_mutex);
tmp_vector.reinit (matrix->n(), true);
- tmp_mutex.release ();
}
FilteredMatrix<SparseMatrix<float>,Vector<float> >::
allocate_tmp_vector ()
{
- tmp_mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (tmp_mutex);
tmp_vector.reinit (matrix->n(), true);
- tmp_mutex.release ();
}
for (unsigned int i=0; i<block_sizes.size(); ++i)
block_sizes[i] = matrix->block(i,i).n();
- tmp_mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (tmp_mutex);
tmp_vector.reinit (block_sizes, true);
- tmp_mutex.release ();
}
for (unsigned int i=0; i<block_sizes.size(); ++i)
block_sizes[i] = matrix->block(i,i).n();
- tmp_mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (tmp_mutex);
tmp_vector.reinit (block_sizes, true);
- tmp_mutex.release ();
}
{
Record record = {child_pid, direction, &typeid(T), count,
sizeof(T)*count, completed_bytes, descr};
- list_access_lock.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (list_access_lock);
communication_log.push_back (record);
- list_access_lock.release ();
}
*/
void list_communication (const pid_t /*child_pid*/)
{
- list_access_lock.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (list_access_lock);
std::cerr << "++++++++++++++++++++++++++++++" << std::endl
<< "Communication log history:" << std::endl;
<< i->description
<< std::endl;
std::cerr << "++++++++++++++++++++++++++++++" << std::endl;
-
- list_access_lock.release ();
}
*/
void remove_history (const pid_t &child_pid)
{
- list_access_lock.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (list_access_lock);
for (std::list<Record>::iterator i=communication_log.begin();
i!=communication_log.end(); ++i)
if (i->child_pid == child_pid)
communication_log.erase (i);
- list_access_lock.release ();
}
}
CommunicationsLog::remove_history (detached_mode_data->child_pid);
// next close down client
- detached_mode_data->mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (detached_mode_data->mutex);
write (detached_mode_data->server_client_pipe[1], "7", 1);
- detached_mode_data->mutex.release ();
// then also delete data
delete detached_mode_data;
IKEEP, IW1, NSTEPS, IFLAG);
else
{
- detached_mode_data->mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (detached_mode_data->mutex);
// first write the data we have
// to push over, i.e. first
// function index, then array
// next get back what we need
// to know
detached_mode_data->get (IFLAG, 1, "IFLAG");
-
- detached_mode_data->mutex.release ();
};
}
// basically, everything is
// already over the line,
// except for A and LA
- detached_mode_data->mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (detached_mode_data->mutex);
detached_mode_data->put ("2", 1, "ACTION 2");
detached_mode_data->put (LA, 1, "LA");
// next get back what we need
// to know
detached_mode_data->get (IFLAG, 1, "IFLAG");
-
- detached_mode_data->mutex.release ();
};
}
HSL::MA27::ma27x1_ (NRLNEC);
else
{
- detached_mode_data->mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (detached_mode_data->mutex);
// ma27x1 only reads data, so
// don't send anything except
// for the id
detached_mode_data->put ("4", 1, "ACTION 4");
detached_mode_data->get (NRLNEC, 1, "NRLNEC");
- detached_mode_data->mutex.release ();
};
}
HSL::MA27::ma27x2_ (NIRNEC);
else
{
- detached_mode_data->mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (detached_mode_data->mutex);
// ma27x2 only reads data, so
// don't send anything except
// for the id
detached_mode_data->put ("5", 1, "ACTION 5");
detached_mode_data->get (NIRNEC, 1, "NIRNEC");
- detached_mode_data->mutex.release ();
};
}
HSL::MA27::ma27x3_ (LP);
else
{
- detached_mode_data->mutex.acquire ();
+ Threads::ThreadMutex::ScopedLock lock (detached_mode_data->mutex);
// ma27x2 only reads data, so
// don't send anything except
// for the id
detached_mode_data->put ("6", 1, "ACTION 6");
detached_mode_data->put (LP, 1, "LP");
- detached_mode_data->mutex.release ();
};
}