Error Handling Specification for Chapter 30 (Threads)

Document: N2636=08-0146

Date: 2008-05-16

Authors: Jeff Garland <jeff@crystalclearsoftware.com>

              Nick Stoughton <nick@usenix.org>

              Benjamin Kosnik <bkoz@redhat.com>

              Beman Dawes <bdawes@acm.org>



Motivation


New facilities have recently been added to C++0x working draft in the diagnostics library to provide mechanisms for the reporting of system errors as part of the C++ library.  These facilities include both portable error codes as well as operating system specific error reporting capabilities, thus enabling implementations to provide a full range of error information.

This paper provides error condition specifications for the recently added threading API (ie, Chapter 30), as suggested during discussions at the Bellevue meeting.

For programmers that want to write portable code that does detailed error handling, knowing the specific error codes an implementation may produce for a particular method is vital.  Without these specifications a programmer must be prepared for any possible error code in the entire set of portable codes (over 70 total).

Note that this type of specification has precedent in other international standards.  The POSIX specification provides the specific error codes that can occur for each function and a description of the condition that would cause them.

Overview of Changes


In Chapter 17, introduce an Error Condition clause, with specification and description.

In Chapter 30, add error condition clauses where appropriate.

Proposed Wording


Change 17.3.1.3 Specifications [structure.specifications], paragraph 3, as indicated:


Descriptions of function semantics contain the following elements (as appropriate):153)

Requires: the preconditions for calling the function

Effects: the actions performed by the function

Postconditions: the observable results established by the function

Returns: a description of the value(s) returned by the function

Throws: any exceptions thrown by the function, and the conditions that would cause the exception

Complexity: the time and/or space complexity of the function

Remarks: additional semantic constraints on the function

Error conditions: the default error conditions for error codes reported by the function.


For non-reserved replacement and handler functions, clause 18 specifies two behaviors for the functions in question: their required and default behavior. The default behavior describes a function definition provided by the implementation. The required behavior describes the semantics of a function definition provided by either the implementation or a C++ program. Where no distinction is explicitly made in the description, the behavior described is the required behavior.


Complexity requirements specified in the library clauses are upper bounds, and implementations that provide better
complexity guarantees satisfy the requirements.


Error conditions specify two types of failure: conditions where a function shall fail, and conditions where a function may fail. The conditions are listed, together with a suitable explanation, as the enum class errc constants ([syserr]) that could be used as an argument to function make_error_condition([syserr.errcondition.nonmembers]).

[Note: the class  error_condition ([syserr.errcondition.overview]) object is reported via the default_error_condition function ([syserr.errcode.observers])  of the class error_code object representing the error. --end note.



Change 30.1.2 Exceptions [thread.req.exception] paragraph 2, as indicated:

The error_category (19.4.1.1) of the error_code reported by such an exception’s code() member function is implementation-defined.
 [Note: The category is typically system_category (19.4.1.1) since these error codes usually originate
  from the underlying operating system application program interface (API). — end note ]
as specified in the error condition clause.

Change 30.2.1.2 thread constructors [thread.thread.constr], paragraph 9, as indicated:

Throws: std::system_error or std::bad_alloc if unable to start the new thread.

Error conditions:

    * resource_unavailable_try_again - The system lacked the necessary resources to create another thread, or the system-imposed     limit on the total number of threads in a process would be exceeded.


Change 30.2.1.5 thread members [thread.thread.member] paragraph 8 of the joint description as follows:

Throws: std::system_error when the postconditions cannot be achieved.
Error conditions:
    * resource_deadlock_would_occur - If deadlock is detected or this->get_id() == std::this_thread::get_id()

    * no_such_process - If the thread is not valid.
    * invalid_argument - If the thread is not joinable.

Change paragraph 12 of the detach description

Throws: std::system_error when the effects or postconditions cannot be achieved.
Error conditions:
    * no_such_process: Not a valid thread.
    * invalid_argument: Not a detachable thread.

Change 30.3.1 Mutex requirements [thread.mutex.requirements] change paragraph 3 on construction

If initialization of an object of a Mutex type fails, an exception of type std::system_error shall be thrown.
Error conditions:
   
* not_enough_memory: If there is not enough memory to construct the mutex object.
    * resource_unavailable_try_again: If any native_handle_type manipulated is not available.
    * operation_not_permitted:
If the thread does not have the necessary permission to change state of the mutex.
    * device_or_resource_busy: If any native_handle_type manipulated is already locked.
    * invalid_argument: If any native_handle_type manipulated as part of mutex construction is incorrect.

Append after paragraph 10 on m.lock()

Throws: std::system_error when the effects or postcondition cannot be achieved.
Error conditions:
    * operation_not_permitted: If the thread does not have the necessary permission to change state of the mutex.
    * resource_deadlock_would_occur: If the current thread already owns the mutex, and is able to detect it.
    * device_or_resource_busy: If the mutex is already locked and blocking is not possible.

Change 30.3.3 paragraph 3:

An exception class lock_error is derived from std::exception and is used to indicate improper usage of locks such as locking a mutex that the lock already owns or unlocking a mutex that the lock does not own.
    namespace std {
       class lock_error : public std::exception {
       public:
         virtual const char* what() const throw();
       };
    }

Change 30.3.3.2.2 unique_lock locking [thread.lock.unique.locking] paragraph 3 lock() description

Throws: lock_error if on entry owns is true or pm is null.   std::
system_error when the postcondition cannot be achieved.
Error conditions:
    * operation_not_permitted: If pm is null.
    * resource_deadlock_would_occur: If the current thread already owns the mutex (on entry owns is true).


Change 30.3.3.2.2 unique_lock locking [thread.lock.unique.locking] paragraph 7 try_lock() description

Throws: lock_error if on entry owns is true or pm is null.   std::system_error when the postcondition cannot be achieved.
Error conditions:
    * operation_not_permitted: If pm is null.
    * resource_deadlock_would_occur: If the current thread already owns the mutex (on entry owns is true).


Change 30.3.3.2.2 unique_lock locking [thread.lock.unique.locking] paragraph 11 timed_lock() description

Throws: lock_error if on entry owns is true or pm is null.   std::system_error when the postcondition cannot be achieved.
Error conditions:
    * operation_not_permitted: If pm is null.
    * resource_deadlock_would_occur: If the current thread already owns the mutex (on entry owns is true).


Change 30.3.3.2.2 unique_lock locking [thread.lock.unique.locking] paragraph 15 templatized timed_lock() description

Throws: lock_error if on entry owns is true or pm is null.   std::system_error when the postcondition cannot be achieved.
Error conditions:
    * operation_not_permitted: If pm is null.
    * resource_deadlock_would_occur: If the current thread already owns the mutex (on entry owns is true).


Change 30.3.3.2.2 unique_lock locking [thread.lock.unique.locking] paragraph 18 unlock() description

Throws: lock_error if on entry owns is false.   std::system_error when the postcondition cannot be achieved.
Error conditions:
    * operation_not_permitted: If on entry owns is false.

Change 30.3.5.1 Struct once_flag append after paragraph 3

Throws: Nothing.

Change 30.3.5.2 Function call_once [thread.once.callonce] paragraph 4

Throws: std::system_error when the effects cannot be achieved, or any exception thrown by func.
Error conditions:
    * invalid_argument: If once_flag object is no longer valid.

Change 30.4.1 Class condition_variable [thread.condition.condvar] paragraph 2 construction

Effects: Constructs an object of type condition_variable.
Throws: std::system_error when conditions cannot be achieved.
Error conditions:
    * not_enough_memory: If a memory limitation prevents initialization.
    * resource_unavailable_try_again: If some non-memory limitation prevents initialization
    * device_or_resource_busy: If attempting to initialize a previously-initialized but as of yet undestroyed condition_variable.

Change 30.4.1 Class condition_variable [thread.condition.condvar] paragraph 11 of wait description

Throws: std::system_error when the effects, 
pre-conditions, or postcondition cannot be achieved.
Error conditions:
    * operation_not_permitted: If the thread does not own the lock.
    * equivalent error_condition from lock.lock() or lock.unlock()

Change 30.4.1 Class condition_variable [thread.condition.condvar] paragraph 16 of templatized wait description

Returns:
true false if the call is returning because the time specified by abs_time was reached, otherwise true false.

Change 30.4.1 Class condition_variable [thread.condition.condvar] paragraph 17 of templatized wait description

Throws: std::system_error when the returned value, effects, or postcondition cannot be achieved.
Error conditions:
    * operation_not_permitted: If the thread does not own the lock.
    * equivalent error_condition from lock.lock() or lock.unlock()


Change 30.4.2 Class condition_variable_any [thread.condition.condvarany] paragraph 10 of wait description

Throws: std::system_error when the effects or postcondition cannot be achieved.
Error conditions:
    * equivalent error_condition from lock.lock() or lock.unlock()


Change 30.4.2 Class condition_variable_any [thread.condition.condvarany] paragraph 15 of templatized wait description

Throws: std::system_error when the returned value, effects, or postcondition cannot be achieved.
Error conditions:
    * equivalent error_condition from lock.lock() or lock.unlock()


Acknowedgements


Special thanks to Anthony Williams for reviewing drafts and clarifying error specifications.