Skip to content

Commit 00401f2

Browse files
authored
Merge pull request dealii#18921 from gassmoeller/increase_safety_of_timer
2 parents 228ece5 + cf2a35c commit 00401f2

File tree

4 files changed

+109
-33
lines changed

4 files changed

+109
-33
lines changed

include/deal.II/base/timer.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,12 @@ class Timer
203203
void
204204
restart();
205205

206+
/**
207+
* Returns true if the timer is currently running, false otherwise.
208+
*/
209+
bool
210+
is_running() const;
211+
206212
/**
207213
* Return the current accumulated wall time (including the current lap, if
208214
* the timer is running) in seconds without stopping the timer.
@@ -384,6 +390,17 @@ class Timer
384390
* the current lap is included in the count.
385391
*/
386392
unsigned int n_timed_laps;
393+
394+
/**
395+
* A lock that makes sure that this class gives reasonable results even when
396+
* used with several threads. Note that thread-safety with a timer that is
397+
* shared between different threads is hard to establish, and it is in
398+
* particular discouraged to have multiple threads starting and stopping
399+
* the timer. This case only works if each thread makes sure the timer
400+
* is not already running before starting it, and stopping the timer
401+
* before any other thread can use it.
402+
*/
403+
Threads::Mutex mutex;
387404
};
388405

389406

source/base/timer.cc

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,10 @@ Timer::Timer(const MPI_Comm mpi_communicator, const bool sync_lap_times_)
175175
void
176176
Timer::start()
177177
{
178-
if (running == false)
178+
// Make sure only one thread at a time starts or resets the timer
179+
std::lock_guard<std::mutex> lock(mutex);
180+
181+
if (is_running() == false)
179182
++n_timed_laps;
180183

181184
running = true;
@@ -195,47 +198,57 @@ Timer::start()
195198
void
196199
Timer::stop()
197200
{
198-
if (running)
199-
{
200-
running = false;
201+
// Make sure only one thread at a time stops the timer
202+
std::lock_guard<std::mutex> lock(mutex);
201203

202-
wall_times.last_lap_time =
203-
wall_clock_type::now() - wall_times.current_lap_start_time;
204-
cpu_times.last_lap_time =
205-
cpu_clock_type::now() - cpu_times.current_lap_start_time;
204+
AssertThrow(is_running() == true,
205+
ExcMessage(
206+
"This timer is not running and hence cannot be stopped."));
206207

207-
last_lap_wall_time_data =
208-
Utilities::MPI::min_max_avg(internal::TimerImplementation::to_seconds(
209-
wall_times.last_lap_time),
210-
mpi_communicator);
211-
if (sync_lap_times)
212-
{
213-
wall_times.last_lap_time =
214-
internal::TimerImplementation::from_seconds<
215-
decltype(wall_times)::duration_type>(last_lap_wall_time_data.max);
216-
cpu_times.last_lap_time = internal::TimerImplementation::from_seconds<
217-
decltype(cpu_times)::duration_type>(
218-
Utilities::MPI::min_max_avg(
219-
internal::TimerImplementation::to_seconds(
220-
cpu_times.last_lap_time),
221-
mpi_communicator)
222-
.max);
223-
}
224-
wall_times.accumulated_time += wall_times.last_lap_time;
225-
cpu_times.accumulated_time += cpu_times.last_lap_time;
226-
accumulated_wall_time_data =
208+
running = false;
209+
210+
wall_times.last_lap_time =
211+
wall_clock_type::now() - wall_times.current_lap_start_time;
212+
cpu_times.last_lap_time =
213+
cpu_clock_type::now() - cpu_times.current_lap_start_time;
214+
215+
last_lap_wall_time_data =
216+
Utilities::MPI::min_max_avg(internal::TimerImplementation::to_seconds(
217+
wall_times.last_lap_time),
218+
mpi_communicator);
219+
if (sync_lap_times)
220+
{
221+
wall_times.last_lap_time = internal::TimerImplementation::from_seconds<
222+
decltype(wall_times)::duration_type>(last_lap_wall_time_data.max);
223+
cpu_times.last_lap_time = internal::TimerImplementation::from_seconds<
224+
decltype(cpu_times)::duration_type>(
227225
Utilities::MPI::min_max_avg(internal::TimerImplementation::to_seconds(
228-
wall_times.accumulated_time),
229-
mpi_communicator);
226+
cpu_times.last_lap_time),
227+
mpi_communicator)
228+
.max);
230229
}
230+
wall_times.accumulated_time += wall_times.last_lap_time;
231+
cpu_times.accumulated_time += cpu_times.last_lap_time;
232+
accumulated_wall_time_data =
233+
Utilities::MPI::min_max_avg(internal::TimerImplementation::to_seconds(
234+
wall_times.accumulated_time),
235+
mpi_communicator);
236+
}
237+
238+
239+
240+
bool
241+
Timer::is_running() const
242+
{
243+
return running;
231244
}
232245

233246

234247

235248
double
236249
Timer::cpu_time() const
237250
{
238-
if (running)
251+
if (is_running())
239252
{
240253
const double running_time = internal::TimerImplementation::to_seconds(
241254
cpu_clock_type::now() - cpu_times.current_lap_start_time +
@@ -264,7 +277,7 @@ double
264277
Timer::wall_time() const
265278
{
266279
wall_clock_type::duration current_elapsed_wall_time;
267-
if (running)
280+
if (is_running())
268281
current_elapsed_wall_time = wall_clock_type::now() -
269282
wall_times.current_lap_start_time +
270283
wall_times.accumulated_time;
@@ -435,7 +448,13 @@ TimerOutput::enter_subsection(const std::string &section_name)
435448
sections[section_name] = Timer(mpi_communicator, false);
436449
}
437450
else
438-
sections[section_name].start();
451+
{
452+
Assert(sections[section_name].is_running() == false,
453+
ExcMessage("Cannot enter the already active section <" +
454+
section_name + ">."));
455+
456+
sections[section_name].start();
457+
}
439458

440459
active_sections.push_back(section_name);
441460
}
@@ -465,6 +484,10 @@ TimerOutput::leave_subsection(const std::string &section_name)
465484
const std::string actual_section_name =
466485
(section_name.empty() ? active_sections.back() : section_name);
467486

487+
Assert(sections[actual_section_name].is_running() == true,
488+
ExcMessage("Cannot leave section <" + actual_section_name +
489+
">, because it has not been entered."));
490+
468491
sections[actual_section_name].stop();
469492

470493
// in case we have to print out something, do that here...

tests/base/timer_11.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// ------------------------------------------------------------------------
2+
//
3+
// SPDX-License-Identifier: LGPL-2.1-or-later
4+
// Copyright (C) 2017 - 2018 by the deal.II authors
5+
//
6+
// This file is part of the deal.II library.
7+
//
8+
// Part of the source code is dual licensed under Apache-2.0 WITH
9+
// LLVM-exception OR LGPL-2.1-or-later. Detailed license information
10+
// governing the source code and code contributions can be found in
11+
// LICENSE.md and CONTRIBUTING.md at the top level directory of deal.II.
12+
//
13+
// ------------------------------------------------------------------------
14+
15+
// test that a timer cannot be stopped while not running
16+
17+
#include <deal.II/base/timer.h>
18+
19+
#include "../tests.h"
20+
21+
int
22+
main()
23+
{
24+
initlog();
25+
26+
Timer t;
27+
t.stop();
28+
29+
// this will throw an exception
30+
t.stop();
31+
32+
deallog << "OK" << std::endl;
33+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
2+
DEAL::we should not get here
3+
DEAL::

0 commit comments

Comments
 (0)