Document number: N3267=11-0037
Date: 2011-03-23
J. Daniel Garcia, Michael Wong
Project: Programming Language C++, Library Working Group
Reply To: josedaniel.garcia@uc3m.es

N3267 - A review of noexcept in the threads library (revised)

This paper studies possible changes to the Threads Library to make broad use of noexcept. The paper addresses National Body comments CH 16 and GB 60. It is a revision of  N3252 in the mailing based on discussions in Madrid, after the update of new criteria  for noexcept.

Changes in this paper are restricted to chapter 30 (threads library).

All changes in this paper are relative to N3242.

Discussion

Several swap() functions have been made noexcept as they are defined in terms of the corresponding noexcept member function.

In 30.4.1 mutex, recursive_mutex, timed_mutex and recursive_timed_mutex constructors have been made noexcept In section 30.6.1 some operations have been made noexcept to be consistent with the disgnostics library. In section 30.6.10 member function valid is not consistent with its definition in 30.6.10.1. It has been updated. In general all the swap member functions have been revised regarding to its noexcept specification. Member function unlock() has lost its noexcept specification because it has a precondition.

Acknowledgments

The following people provided very useful comments to improve this paper: Jonathan Wakely, Daniel Krügler, Alisdair Meredith, and the rest of the Concurrent subgroup.

Proposed Wording

30.3 Threads [thread.threads]

After p. 1
namespace std {
#define __STDCPP_THREADS__ __cplusplus

class thread;

void swap(thread& x, thread& y) noexcept;

namespace this_thread {
thread::id get_id() noexcept;
void yield() noexcept;
template <class Clock, class Duration>
void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);
template <class Rep, class Period>
void sleep_for(const chrono::duration<Rep, Period>& rel_time);
}
}

30.3.2 Namespace this_thread [thread.thread.this]

Before p. 1
namespace std {
namespace this_thread {
thread::id get_id() noexcept;
void yield() noexcept;
template <class Clock, class Duration>
void sleep_until(const chrono::time_point<Clock, Duration>& abs_time) noexcept;
template <class Rep, class Period>
void sleep_for(const chrono::duration<Rep, Period>& rel_time) noexcept;
}
}
After p. 3
template <class Clock, class Duration>
void sleep_until(const chrono::time_point<Clock, Duration>& abs_time) noexcept;
After p. 5
template <class Rep, class Period>
void sleep_for(const chrono::duration<Rep, Period>& rel_time) noexcept;

30.3.1.7 thread specialized algorithms [thread.thread.algorithm]

Before p.1
void swap(thread& x, thread& y) noexcept;

30.4 Mutual exclusion [thread.mutex]

After p. 1
namespace std {
...
template <class Mutex>
void swap(unique_lock<Mutex>& x, unique_lock<Mutex>& y) noexcept;
...
}

30.4.1.2.1 Class mutex [thread.mutex.class]

Before p. 1
namespace std {
class mutex {
public:
constexpr mutex() noexcept;
~mutex();

mutex(const mutex&) = delete;
mutex& operator=(const mutex&) = delete;

void lock();
bool try_lock() noexcept;
void unlock() noexcept;

typedef implementation-defined native_handle_type; // See 30.2.3
native_handle_type native_handle(); // See 30.2.3
};
}

30.4.1.2.2 Class recursive_mutex [thread.mutex.recursive]

Before p.1
namespace std {
class recursive_mutex {
public:
recursive_mutex();
~recursive_mutex();

recursive_mutex(const recursive_mutex&) = delete;
recursive_mutex& operator=(const recursive_mutex&) = delete;

void lock();
bool try_lock() noexcept;
void unlock() noexcept;

typedef implementation-defined native_handle_type; // See 30.2.3
native_handle_type native_handle(); // See 30.2.3
};
}

30.4.1.3.1 Class timed_mutex [thread.timedmutex.class]

Before p. 1
namespace std {
class timed_mutex {
public:
timed_mutex();
~timed_mutex();

timed_mutex(const timed_mutex&) = delete;
timed_mutex& operator=(const timed_mutex&) = delete;

void lock();
bool try_lock();
template <class Rep, class Period>
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) noexcept;
template <class Clock, class Duration>
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) noexcept;
void unlock();

typedef implementation-defined native_handle_type; // See 30.2.3
native_handle_type native_handle(); // See 30.2.3
};
}

30.4.1.3.2 Class recursive_timed_mutex [thread.timedmutex.recursive]

Before p. 1
namespace std {
class recursive_timed_mutex {
public:
recursive_timed_mutex();
~recursive_timed_mutex();

recursive_timed_mutex(const recursive_timed_mutex&) = delete;
recursive_timed_mutex& operator=(const recursive_timed_mutex&) = delete;

void lock();
bool try_lock() noexcept;
template <class Rep, class Period>
bool try_lock_for(const chrono::duration<Rep, Period>& rel_time) noexcept;
template <class Clock, class Duration>
bool try_lock_until(const chrono::time_point<Clock, Duration>& abs_time) noexcept;
void unlock();

typedef implementation-defined native_handle_type; // See 30.2.3
native_handle_type native_handle(); // See 30.2.3
};
}

30.4.2.1 Class template lock_guard [thread.lock.guard]

Before p. 1
namespace std {
template <class Mutex>
class lock_guard {
public:
...
lock_guard(mutex_type& m, adopt_lock_t) noexcept;
...
};
}
After p. 5
lock_guard(mutex_type& m, adopt_lock_t) noexcept;
Requires: The calling thread owns the mutex m.
Postcondition: &pm == &m
Throws: Nothing.

30.4.2.2 Class template unique_lock [thread.lock.unique]

Before p. 1
namespace std {
template <class Mutex>
class unique_lock {
public:
...
// 30.4.2.2.1, construct/copy/destroy:
unique_lock() noexcept;
explicit unique_lock(mutex_type& m);
unique_lock(mutex_type& m, defer_lock_t) noexcept;
unique_lock(mutex_type& m, try_to_lock_t) noexcept;
unique_lock(mutex_type& m, adopt_lock_t) noexcept;
template <class Clock, class Duration>
unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time) noexcept;
template <class Rep, class Period>
unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time) noexcept;
~unique_lock();
...
};
}

30.4.2.2.1 unique_lock constructors, destructor, and assignment [thread.lock.unique.cons]

After p. 7
unique_lock(mutex_type& m, try_to_lock_t) noexcept;

After p. 10
unique_lock(mutex_type& m, adopt_lock_t) noexcept;
Requires: The calling thread own the mutex.
Effects: Constructs an object of type unique_lock.
Postconditions: pm
Throws: Nothing.
After p. 13
template <class Clock, class Duration>
unique_lock(mutex_type& m, const chrono::time_point<Clock, Duration>& abs_time) noexcept;
After p. 16
template <class Rep, class Period>
unique_lock(mutex_type& m, const chrono::duration<Rep, Period>& rel_time) noexcept;

30.6.1 Overview [futures.overview]

After p. 1
namespace std {
...
template <class R>
void swap(promise<R>& x, promise<R>& y) noexcept;
...
template <class R>
void swap(packaged_task<R(ArgTypes...)>&, packaged_task<R(ArgTypes...)>&) noexcept;
...
}

30.6.2 Error handling

Before p.1
const error_category& future_category() noexcept;
After p. 2
error_code make_error_code(future_errc e) noexcept;
After p. 3
error_condition make_error_condition(future_errc e) noexcept;

30.6.5 Class template promise [futures.promise]

After p. 27
template <class R>
void swap(promise<R>& x, promise<R>& y) noexcept;

30.6.6 Class template future [futures.unique_future]

After p. 3
namespace std {
template <class R>
class future {
public:
future() noexcept;
future(future &&) noexcept;
future(const future& rhs) = delete;
~future();
future& operator=(const future& rhs) = delete;
future& operator=(future&&) noexcept;
shared_future share() &&;

// retrieving the value
see below get();

// functions to check state
bool valid() const noexcept;

void wait() const;
...
};
}
After p. 4
future() noexcept;
After p. 9
future& operator=(future&& rhs) noexcept;
After p. 17
bool valid() const noexcept;

30.6.7 Class template shared_future [futures.shared_future]

After p. 3
namespace std {
template <class R>
class shared_future {
public:
shared_future() noexcept;
shared_future(const shared_future& rhs);
shared_future(future<R>&&) noexcept;
shared_future(shared_future&& rhs) noexcept;
~shared_future();
shared_future& operator=(const shared_future& rhs);
shared_future& operator=(shared_future&& rhs) noexcept;

// retrieving the value
see below get() const;

// functions to check state
bool valid() const noexcept;

void wait() const;
...
};
}
After p. 11
shared_future& operator=(shared_future&& rhs) noexcept;
After p. 20
bool valid() const noexcept;

30.6.9 Class template packaged_task [futures.task]

After p.2
namespace std {
template<class R, class... ArgTypes>
class packaged_task<R(ArgTypes...)> {
public:
...
// move support
packaged_task(packaged_task&& other) noexcept;
packaged_task& operator=(packaged_task&& other) noexcept;
void swap(packaged_task& other) noexcept;
...
};
}
After p. 6
packaged_task& operator=(packaged_task&& other) noexcept;