You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
To avoid concurrency bugs in your multithreaded program, always follow an appropriate locking discipline and use SAL annotations.
35
35
36
-
Concurrency bugs are notoriously hard to reproduce, diagnose, and debug because they are non-deterministic. Reasoning about thread interleaving is difficult at best, and becomes impractical when you are designing a body of code that has more than a few threads. Therefore, it's good practice to follow a locking discipline in your multithreaded programs. For example, obeying a lock order while acquiring multiple locks helps avoid deadlocks, and acquiring the proper guarding lock before accessing a shared resource helps prevent race conditions.
36
+
Concurrency bugs are notoriously hard to reproduce, diagnose, and debug because they're non-deterministic. Reasoning about thread interleaving is difficult at best, and becomes impractical when you're designing a body of code that has more than a few threads. Therefore, it's good practice to follow a locking discipline in your multithreaded programs. For example, obeying a lock order while acquiring multiple locks helps avoid deadlocks, and acquiring the proper guarding lock before accessing a shared resource helps prevent race conditions.
37
37
38
38
Unfortunately, seemingly simple locking rules can be surprisingly hard to follow in practice. A fundamental limitation in today's programming languages and compilers is that they do not directly support the specification and analysis of concurrency requirements. Programmers have to rely on informal code comments to express their intentions about how they use locks.
39
39
40
40
Concurrency SAL annotations are designed to help you specify locking side effects, locking responsibility, data guardianship, lock order hierarchy, and other expected locking behavior. By making implicit rules explicit, SAL concurrency annotations provide a consistent way for you to document how your code uses locking rules. Concurrency annotations also enhance the ability of code analysis tools to find race conditions, deadlocks, mismatched synchronization operations, and other subtle concurrency errors.
41
41
42
42
## General Guidelines
43
43
44
-
By using annotations, you can state the contracts that are implied by function definitions between implementations (callees) and clients (callers), and express invariants and other properties of the program that can further improve analysis.
44
+
By using annotations, you can state the contracts that are implied by function definitions between implementations (callees) and clients (callers). You can also express invariants and other properties of the program that can further improve analysis.
45
45
46
46
SAL supports many different kinds of locking primitives—for example, critical sections, mutexes, spin locks, and other resource objects. Many concurrency annotations take a lock expression as a parameter. By convention, a lock is denoted by the path expression of the underlying lock object.
47
47
@@ -51,7 +51,7 @@ Some thread ownership rules to keep in mind:
51
51
52
52
- Mutexes and critical sections are counted locks that have clear thread ownership.
53
53
54
-
- Semaphores and events are counted locks that do not have clear thread ownership.
54
+
- Semaphores and events are counted locks that don't have clear thread ownership.
55
55
56
56
## Locking Annotations
57
57
@@ -64,13 +64,13 @@ The following table lists the locking annotations.
64
64
|`_Acquires_nonreentrant_lock_(expr)`|The lock that's named by `expr` is acquired. An error is reported if the lock is already held.|
65
65
|`_Acquires_shared_lock_(expr)`|Annotates a function and indicates that in post state the function increments by one the shared lock count of the lock object that's named by `expr`.|
66
66
|`_Create_lock_level_(name)`|A statement that declares the symbol `name` to be a lock level so that it may be used in the annotations `_Has_Lock_level_` and `_Lock_level_order_`.|
67
-
|`_Has_lock_kind_(kind)`|Annotates any object to refine the type information of a resource object. Sometimes a common type is used for different kinds of resources and the overloaded type is not sufficient to distinguish the semantic requirements among various resources. Here's a list of pre-defined `kind` parameters:<br /><br /> `_Lock_kind_mutex_`<br /> Lock kind ID for mutexes.<br /><br /> `_Lock_kind_event_`<br /> Lock kind ID for events.<br /><br /> `_Lock_kind_semaphore_`<br /> Lock kind ID for semaphores.<br /><br /> `_Lock_kind_spin_lock_`<br /> Lock kind ID for spin locks.<br /><br /> `_Lock_kind_critical_section_`<br /> Lock kind ID for critical sections.|
67
+
|`_Has_lock_kind_(kind)`|Annotates any object to refine the type information of a resource object. Sometimes a common type is used for different kinds of resources and the overloaded type isn't sufficient to distinguish the semantic requirements among various resources. Here's a list of pre-defined `kind` parameters:<br /><br /> `_Lock_kind_mutex_`<br /> Lock kind ID for mutexes.<br /><br /> `_Lock_kind_event_`<br /> Lock kind ID for events.<br /><br /> `_Lock_kind_semaphore_`<br /> Lock kind ID for semaphores.<br /><br /> `_Lock_kind_spin_lock_`<br /> Lock kind ID for spin locks.<br /><br /> `_Lock_kind_critical_section_`<br /> Lock kind ID for critical sections.|
68
68
|`_Has_lock_level_(name)`|Annotates a lock object and gives it the lock level of `name`.|
69
69
|`_Lock_level_order_(name1, name2)`|A statement that gives the lock ordering between `name1` and `name2`. Locks that have level `name1` must be acquired before locks that have level `name2`.|
70
-
|`_Post_same_lock_(expr1, expr2)`|Annotates a function and indicates that in post state the two locks, `expr1` and `expr2`, are treated as if they are the same lock object.|
70
+
|`_Post_same_lock_(expr1, expr2)`|Annotates a function and indicates that in post state the two locks, `expr1` and `expr2`, are treated as if they're the same lock object.|
71
71
|`_Releases_exclusive_lock_(expr)`|Annotates a function and indicates that in post state the function decrements by one the exclusive lock count of the lock object that's named by `expr`.|
72
72
|`_Releases_lock_(expr)`|Annotates a function and indicates that in post state the function decrements by one the lock count of the lock object that's named by `expr`.|
73
-
|`_Releases_nonreentrant_lock_(expr)`|The lock that's named by `expr` is released. An error is reported if the lock is not currently held.|
73
+
|`_Releases_nonreentrant_lock_(expr)`|The lock that's named by `expr` is released. An error is reported if the lock isn't currently held.|
74
74
|`_Releases_shared_lock_(expr)`|Annotates a function and indicates that in post state the function decrements by one the shared lock count of the lock object that's named by `expr`.|
75
75
|`_Requires_lock_held_(expr)`|Annotates a function and indicates that in pre state the lock count of the object that's named by `expr` is at least one.|
76
76
|`_Requires_lock_not_held_(expr)`|Annotates a function and indicates that in pre state the lock count of the object that's named by `expr` is zero.|
@@ -80,7 +80,7 @@ The following table lists the locking annotations.
80
80
81
81
## SAL Intrinsics For Unexposed Locking Objects
82
82
83
-
Certain lock objects are not exposed by the implementation of the associated locking functions. The following table lists SAL intrinsic variables that enable annotations on functions that operate on those unexposed lock objects.
83
+
Certain lock objects aren't exposed by the implementation of the associated locking functions. The following table lists SAL intrinsic variables that enable annotations on functions that operate on those unexposed lock objects.
84
84
85
85
|Annotation|Description|
86
86
|----------------|-----------------|
@@ -106,12 +106,12 @@ Smart locks typically wrap native locks and manage their lifetime. The following
106
106
107
107
|Annotation|Description|
108
108
|----------------|-----------------|
109
-
|`_Analysis_assume_smart_lock_acquired_`|Tells the analyzer to assume that a smart lock has been acquired. This annotation expects a reference lock type as its parameter.|
110
-
|`_Analysis_assume_smart_lock_released_`|Tells the analyzer to assume that a smart lock has been released. This annotation expects a reference lock type as its parameter.|
111
-
|`_Moves_lock_(target, source)`|Describes `move constructor` operation which transfers lock state from the `source` object to the `target`. The `target` is considered a newly constructed object, so any state it had before is lost and replaced by the `source` state. The `source` is also reset to a clean state with no lock counts or aliasing target, but aliases pointing to it remain unchanged.|
112
-
|`_Replaces_lock_(target, source)`|Describes `move assignment operator` semantics where the target lock is released before transferring the state from the source. This can be regarded as a combination of `_Moves_lock_(target, source)` preceded by a `_Releases_lock_(target)`.|
113
-
|`_Swaps_locks_(left, right)`|Describes the standard `swap` behavior which assumes that objects `left` and `right` exchange their state. The state exchanged includes lock count and aliasing target, if present. Aliases that point to the `left` and `right` objects remain unchanged.|
114
-
|`_Detaches_lock_(detached, lock)`|Describes a scenario in which a lock wrapper type allows dissociation with its contained resource. This is similar to how `std::unique_ptr` works with its internal pointer: it allows programmers to extract the pointer and leave its smart pointer container in a clean state. Similar logic is supported by `std::unique_lock` and can be implemented in custom lock wrappers. The detached lock retains its state (lock count and aliasing target, if any), while the wrapper is reset to contain zero lock count and no aliasing target, while retaining its own aliases. There's no operation on lock counts (releasing and acquiring). This annotation behaves exactly as `_Moves_lock_` except that the detached argument should be **`return`** rather than **`this`**.|
109
+
|`_Analysis_assume_smart_lock_acquired_(lock)`|Tells the analyzer to assume that a smart lock has been acquired. This annotation expects a reference lock type as its parameter.|
110
+
|`_Analysis_assume_smart_lock_released_(lock)`|Tells the analyzer to assume that a smart lock has been released. This annotation expects a reference lock type as its parameter.|
111
+
|`_Moves_lock_(target, source)`|Describes a `move constructor` operation, which transfers lock state from the `source` object to the `target`. The `target` is considered a newly constructed object, so any state it had before is lost and replaced by the `source` state. The `source` is also reset to a clean state with no lock counts or aliasing target, but aliases pointing to it remain unchanged.|
112
+
|`_Replaces_lock_(target, source)`|Describes `move assignment operator` semantics where the target lock is released before transferring the state from the source. You can regard it as a combination of `_Moves_lock_(target, source)` preceded by a `_Releases_lock_(target)`.|
113
+
|`_Swaps_locks_(left, right)`|Describes the standard `swap` behavior, which assumes that objects `left` and `right` exchange their state. The state exchanged includes lock count and aliasing target, if present. Aliases that point to the `left` and `right` objects remain unchanged.|
114
+
|`_Detaches_lock_(detached, lock)`|Describes a scenario in which a lock wrapper type allows dissociation with its contained resource. It's similar to how `std::unique_ptr` works with its internal pointer: it allows programmers to extract the pointer and leave its smart pointer container in a clean state. Similar logic is supported by `std::unique_lock` and can be implemented in custom lock wrappers. The detached lock retains its state (lock count and aliasing target, if any), while the wrapper is reset to contain zero lock count and no aliasing target, while retaining its own aliases. There's no operation on lock counts (releasing and acquiring). This annotation behaves exactly as `_Moves_lock_` except that the detached argument should be **`return`** rather than **`this`**.|
0 commit comments