Skip to content

Commit 115b4ef

Browse files
committed
Add Clang Thread Safety Annotations
1 parent e673261 commit 115b4ef

File tree

14 files changed

+172
-60
lines changed

14 files changed

+172
-60
lines changed

CMakeLists.txt

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ set(C_WARNING_GNU -Wall -Wextra -Wpedantic -pedantic-errors
6060
-Wno-empty-body -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits)
6161
set(C_WARNING_Clang -Wall -Wextra -Wpedantic
6262
-Wstrict-prototypes -Wold-style-definition
63-
-Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-language-extension-token)
63+
-Wthread-safety -Wno-unused-parameter -Wno-missing-field-initializers -Wno-sign-compare -Wno-language-extension-token)
6464

6565
# Set default build type. Must use FORCE because project() sets default to ""
6666
if (NOT CMAKE_BUILD_TYPE)
@@ -137,22 +137,23 @@ if(NOT DEFINED VERSION)
137137
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
138138
OUTPUT_VARIABLE BRANCH_NAME
139139
OUTPUT_STRIP_TRAILING_WHITESPACE)
140-
140+
141141
if (BRANCH_NAME STREQUAL "HEAD")
142142
# You are perhaps on a detached tag or commit
143143
# Get the tag name and use it as the version
144144
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --dirty=-modified --always
145-
OUTPUT_VARIABLE DEFAULT_VERSION
146-
RESULT_VARIABLE GIT_RESULT
147-
OUTPUT_STRIP_TRAILING_WHITESPACE)
148-
145+
OUTPUT_VARIABLE DEFAULT_VERSION
146+
RESULT_VARIABLE GIT_RESULT
147+
OUTPUT_STRIP_TRAILING_WHITESPACE)
148+
149+
149150
# You might sometimes get a fatal error when running the above command (when this is not a git repo).
150151
# GIT_RESULT will contain the result of last child process. It will be zero if successful.
151152
if (GIT_RESULT EQUAL 0)
152153
# Git succeeded, we will use the DEFAULT_VERSION as the QPID_DISPATCH_VERSION
153154
set(QPID_DISPATCH_VERSION ${DEFAULT_VERSION})
154155
else()
155-
# The git command failed, set QPID_DISPATCH_VERSION to "UNKNOWN"
156+
# The git command failed, set QPID_DISPATCH_VERSION to "UNKNOWN"
156157
set(QPID_DISPATCH_VERSION "UNKNOWN")
157158
endif(GIT_RESULT EQUAL 0)
158159
else(BRANCH_NAME STREQUAL "HEAD")
@@ -164,13 +165,13 @@ if(NOT DEFINED VERSION)
164165
OUTPUT_VARIABLE COMMIT_SHA
165166
OUTPUT_STRIP_TRAILING_WHITESPACE)
166167
message(STATUS "COMMIT_SHA is ${COMMIT_SHA}")
167-
set(QPID_DISPATCH_VERSION "${COMMIT_SHA}(${BRANCH_NAME})")
168+
set(QPID_DISPATCH_VERSION "${COMMIT_SHA}(${BRANCH_NAME})")
168169
endif(BRANCH_NAME STREQUAL "HEAD")
169170
else(Git_FOUND)
170171
# Git executable was not available, we will not be able to determine the version, just set it to "UNKNOWN"
171172
set(QPID_DISPATCH_VERSION "UNKNOWN")
172173
endif(Git_FOUND)
173-
174+
174175
else(NOT DEFINED VERSION)
175176

176177
# What if VERSION is defined but someone passed in an empty value for VERSION? Deal with that case here.
@@ -183,7 +184,7 @@ else(NOT DEFINED VERSION)
183184
else()
184185
set(QPID_DISPATCH_VERSION ${VERSION})
185186
endif()
186-
endif(VERSION STREQUAL "")
187+
endif(VERSION STREQUAL "")
187188
endif(NOT DEFINED VERSION)
188189

189190
message(STATUS "Setting skupper-router version to ${QPID_DISPATCH_VERSION}")
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
// Copyright 2017 The Fuchsia Authors. All rights reserved.
21+
// Use of this source code is governed by a BSD-style license that can be
22+
// found in the LICENSE file.
23+
24+
#pragma once
25+
26+
// This header defines thread annotation macros to be used everywhere in Zircon
27+
// outside of publicly exposed headers. See system/public/zircon/compiler.h for
28+
// the publicly exported macros.
29+
30+
// The thread safety analysis system is documented at
31+
// http://clang.llvm.org/docs/ThreadSafetyAnalysis.html and its use in Zircon is documented at
32+
// docs/thread_annotations.md. The macros we use are:
33+
//
34+
// TA_CAP(x) |x| is the capability this type represents, e.g. "mutex".
35+
// TA_GUARDED(x) the annotated variable is guarded by the capability (e.g. lock) |x|
36+
// TA_ACQ(x) function acquires the mutex |x|
37+
// TA_ACQ_SHARED(x) function acquires the mutex |x| for shared reading
38+
// TA_ACQ_BEFORE(x) Indicates that if both this mutex and muxex |x| are to be acquired,
39+
// that this mutex must be acquired before mutex |x|.
40+
// TA_ACQ_AFTER(x) Indicates that if both this mutex and muxex |x| are to be acquired,
41+
// that this mutex must be acquired after mutex |x|.
42+
// TA_TRY_ACQ(bool, x) function acquires the mutex |x| if the function returns |bool|
43+
// TA_TRY_ACQ_SHARED(bool, x) function acquires the mutex |x| for shared reading if the function
44+
// returns |bool|
45+
// TA_REL(x) function releases the mutex |x|
46+
// TA_REL_SHARED(x) function releases the shared for reading mutex |x|
47+
// TA_ASSERT(x) function asserts that |x| is held
48+
// TA_ASSERT_SHARED(x) function asserts that |x| is held for shared reading
49+
// TA_REQ(x) function requires that the caller hold the mutex |x|
50+
// TA_REQ_SHARED(x) function requires that the caller hold the mutex |x| for shared
51+
// reading TA_EXCL(x) function requires that the caller not be holding the mutex
52+
// |x| TA_RET_CAP(x) function returns a reference to the mutex |x| TA_SCOPED_CAP type
53+
// represents a scoped or RAII-style wrapper around a capability TA_NO_THREAD_SAFETY_ANALYSIS
54+
// function is excluded entirely from thread safety analysis
55+
56+
#ifdef __clang__
57+
#define TA_SUPPRESS _Pragma("clang diagnostic ignored \"-Wthread-safety-analysis\"")
58+
#else
59+
#define TA_SUPPRESS
60+
#endif
61+
62+
#ifdef __clang__
63+
#define THREAD_ANNOTATION(x) __attribute__((x))
64+
#else
65+
#define THREAD_ANNOTATION(x)
66+
#endif
67+
68+
#define TA_CAP(x) THREAD_ANNOTATION(capability(x))
69+
#define TA_GUARDED(x) THREAD_ANNOTATION(guarded_by(x))
70+
#define TA_ACQ(...) THREAD_ANNOTATION(acquire_capability(__VA_ARGS__))
71+
#define TA_ACQ_SHARED(...) THREAD_ANNOTATION(acquire_shared_capability(__VA_ARGS__))
72+
#define TA_ACQ_BEFORE(...) THREAD_ANNOTATION(acquired_before(__VA_ARGS__))
73+
#define TA_ACQ_AFTER(...) THREAD_ANNOTATION(acquired_after(__VA_ARGS__))
74+
#define TA_TRY_ACQ(...) THREAD_ANNOTATION(try_acquire_capability(__VA_ARGS__))
75+
#define TA_TRY_ACQ_SHARED(...) THREAD_ANNOTATION(try_acquire_shared_capability(__VA_ARGS__))
76+
#define TA_REL(...) THREAD_ANNOTATION(release_capability(__VA_ARGS__))
77+
#define TA_REL_SHARED(...) THREAD_ANNOTATION(release_shared_capability(__VA_ARGS__))
78+
#define TA_REL_GENERIC(...) THREAD_ANNOTATION(release_generic_capability(__VA_ARGS__))
79+
#define TA_ASSERT(...) THREAD_ANNOTATION(assert_capability(__VA_ARGS__))
80+
#define TA_ASSERT_SHARED(...) THREAD_ANNOTATION(assert_shared_capability(__VA_ARGS__))
81+
#define TA_REQ(...) THREAD_ANNOTATION(requires_capability(__VA_ARGS__))
82+
#define TA_REQ_SHARED(...) THREAD_ANNOTATION(requires_shared_capability(__VA_ARGS__))
83+
#define TA_EXCL(...) THREAD_ANNOTATION(locks_excluded(__VA_ARGS__))
84+
#define TA_RET_CAP(x) THREAD_ANNOTATION(lock_returned(x))
85+
#define TA_SCOPED_CAP THREAD_ANNOTATION(scoped_lockable)
86+
#define TA_NO_THREAD_SAFETY_ANALYSIS THREAD_ANNOTATION(no_thread_safety_analysis)

include/qpid/dispatch/threading.h

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,38 +26,40 @@
2626
#include <assert.h>
2727
#include <pthread.h>
2828

29-
typedef struct sys_mutex_t sys_mutex_t;
29+
#include "qpid/dispatch/internal/thread_annotations.h"
30+
31+
typedef struct sys_mutex_t TA_CAP("mutex") sys_mutex_t;
3032
struct sys_mutex_t {
3133
pthread_mutex_t mutex;
3234
};
3335

3436
void sys_mutex_init(sys_mutex_t *mutex);
3537
void sys_mutex_free(sys_mutex_t *mutex);
36-
void sys_mutex_lock(sys_mutex_t *mutex);
37-
void sys_mutex_unlock(sys_mutex_t *mutex);
38+
void sys_mutex_lock(sys_mutex_t *mutex) TA_ACQ(*mutex);
39+
void sys_mutex_unlock(sys_mutex_t *mutex) TA_REL(*mutex);
3840

39-
typedef struct sys_cond_t sys_cond_t;
41+
typedef struct sys_cond_t TA_CAP("cond") sys_cond_t;
4042
struct sys_cond_t {
4143
pthread_cond_t cond;
4244
};
4345

4446
void sys_cond_init(sys_cond_t *cond);
4547
void sys_cond_free(sys_cond_t *cond);
46-
void sys_cond_wait(sys_cond_t *cond, sys_mutex_t *held_mutex);
48+
void sys_cond_wait(sys_cond_t *cond, sys_mutex_t *held_mutex) TA_REQ(*held_mutex);
4749
void sys_cond_signal(sys_cond_t *cond);
4850
void sys_cond_signal_all(sys_cond_t *cond);
4951

5052

51-
typedef struct sys_rwlock_t sys_rwlock_t;
53+
typedef struct sys_rwlock_t TA_CAP("rwlock") sys_rwlock_t;
5254
struct sys_rwlock_t {
5355
pthread_rwlock_t lock;
5456
};
5557

5658
void sys_rwlock_init(sys_rwlock_t *lock);
5759
void sys_rwlock_free(sys_rwlock_t *lock);
58-
void sys_rwlock_wrlock(sys_rwlock_t *lock);
59-
void sys_rwlock_rdlock(sys_rwlock_t *lock);
60-
void sys_rwlock_unlock(sys_rwlock_t *lock);
60+
void sys_rwlock_wrlock(sys_rwlock_t *lock) TA_ACQ(*lock);
61+
void sys_rwlock_rdlock(sys_rwlock_t *lock) TA_ACQ_SHARED(*lock);
62+
void sys_rwlock_unlock(sys_rwlock_t *lock) TA_REL_GENERIC(*lock);
6163

6264
typedef enum {
6365
SYS_THREAD_MAIN,
@@ -108,16 +110,16 @@ typedef enum {
108110
SYS_THREAD_PROACTOR_MODE_TIMER = SYS_THREAD_PROACTOR_MODE_OTHER,
109111
} sys_thread_proactor_mode_t;
110112

111-
typedef struct sys_spinlock_t sys_spinlock_t;
113+
typedef struct sys_spinlock_t TA_CAP("spinlock") sys_spinlock_t;
112114
struct sys_spinlock_t {
113115
pthread_mutexattr_t attr;
114116
pthread_mutex_t lock;
115117
};
116118

117119
void sys_spinlock_init(sys_spinlock_t *lock);
118120
void sys_spinlock_free(sys_spinlock_t *lock);
119-
void sys_spinlock_lock(sys_spinlock_t *lock);
120-
void sys_spinlock_unlock(sys_spinlock_t *lock);
121+
void sys_spinlock_lock(sys_spinlock_t *lock) TA_ACQ(*lock);
122+
void sys_spinlock_unlock(sys_spinlock_t *lock) TA_REL(*lock);
121123

122124

123125
typedef struct sys_thread_t sys_thread_t;

src/dispatch.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,8 @@ void qd_dispatch_free(qd_dispatch_t *qd)
384384
}
385385

386386

387-
QD_EXPORT void qd_dispatch_router_lock(qd_dispatch_t *qd) { sys_mutex_lock(&qd->router->lock); }
388-
QD_EXPORT void qd_dispatch_router_unlock(qd_dispatch_t *qd) { sys_mutex_unlock(&qd->router->lock); }
387+
QD_EXPORT void qd_dispatch_router_lock(qd_dispatch_t *qd) TA_ACQ(qd->router->lock) { sys_mutex_lock(&qd->router->lock); }
388+
QD_EXPORT void qd_dispatch_router_unlock(qd_dispatch_t *qd) TA_REL(qd->router->lock) { sys_mutex_unlock(&qd->router->lock); }
389389

390390
qdr_core_t* qd_dispatch_router_core(const qd_dispatch_t *qd)
391391
{

src/entity_cache.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ void qd_entity_cache_remove(const char *type, void *object) { push_event(REMOVE,
8787
// Locks the entity cache so entities can be updated safely (prevent entities from being deleted.)
8888
// Do not process any entities if return error code != 0
8989
// Must call qd_entity_refresh_end when done, regardless of error code.
90-
QD_EXPORT qd_error_t qd_entity_refresh_begin(PyObject *list) {
90+
QD_EXPORT qd_error_t qd_entity_refresh_begin(PyObject *list) TA_ACQ(event_lock) TA_NO_THREAD_SAFETY_ANALYSIS
91+
{
9192
qd_error_clear();
9293
sys_mutex_lock(&event_lock);
9394
entity_event_t *event = DEQ_HEAD(event_list);
@@ -104,7 +105,7 @@ QD_EXPORT qd_error_t qd_entity_refresh_begin(PyObject *list) {
104105
return qd_error_code();
105106
}
106107

107-
QD_EXPORT void qd_entity_refresh_end(void)
108+
QD_EXPORT void qd_entity_refresh_end(void) TA_REL(event_lock)
108109
{
109110
sys_mutex_unlock(&event_lock);
110111
}

src/log.c

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
const char *QD_LOG_STATS_TYPE = "logStats";
4848

4949
static qd_log_source_t *default_log_source=0;
50+
static sys_mutex_t log_source_lock;
5051

5152
int qd_log_max_len(void)
5253
{
@@ -177,14 +178,16 @@ void qd_log_formatted_time(const struct timeval *time, char *buf, size_t buflen)
177178
snprintf(buf, buflen, fmt, time->tv_usec);
178179
}
179180

180-
static log_sink_t* find_log_sink_lh(const char* name) {
181+
static log_sink_t *find_log_sink_lh(const char *name) TA_REQ(log_source_lock)
182+
{
181183
log_sink_t* sink = DEQ_HEAD(sink_list);
182184
DEQ_FIND(sink, strcmp(sink->name, name) == 0);
183185
return sink;
184186
}
185187

186188
// Must hold the log_source_lock
187-
static void log_sink_free_lh(log_sink_t* sink) {
189+
static void log_sink_free_lh(log_sink_t *sink) TA_REQ(log_source_lock)
190+
{
188191
if (!sink) return;
189192
assert(sink->ref_count);
190193

@@ -199,7 +202,8 @@ static void log_sink_free_lh(log_sink_t* sink) {
199202
}
200203
}
201204

202-
static log_sink_t* log_sink_lh(const char* name) {
205+
static log_sink_t *log_sink_lh(const char *name) TA_REQ(log_source_lock)
206+
{
203207
log_sink_t* sink = find_log_sink_lh(name);
204208
if (sink) {
205209
sys_atomic_inc(&sink->ref_count);
@@ -393,7 +397,8 @@ static void write_log_lh(qd_log_source_t *log_source, qd_log_entry_t *entry)
393397
}
394398

395399
/// Reset the log source to the default state
396-
static void qd_log_source_defaults(qd_log_source_t *log_source) {
400+
static void qd_log_source_defaults(qd_log_source_t *log_source) TA_REQ(log_source_lock)
401+
{
397402
log_source->mask = -1;
398403
log_source->includeTimestamp = -1;
399404
log_source->includeSource = -1;
@@ -402,7 +407,7 @@ static void qd_log_source_defaults(qd_log_source_t *log_source) {
402407
}
403408

404409
/// Caller must hold the log_source_lock
405-
static qd_log_source_t *qd_log_source_lh(qd_log_module_t module)
410+
static qd_log_source_t *qd_log_source_lh(qd_log_module_t module) TA_REQ(log_source_lock)
406411
{
407412
qd_log_source_t *log_source = log_sources[module];
408413

@@ -437,7 +442,7 @@ qd_log_source_t *qd_log_source_reset(qd_log_module_t module)
437442
return src;
438443
}
439444

440-
static void qd_log_source_free_lh(qd_log_module_t module)
445+
static void qd_log_source_free_lh(qd_log_module_t module) TA_REQ(log_source_lock)
441446
{
442447
qd_log_source_t *src = log_sources[module];
443448
if (src) {
@@ -569,7 +574,7 @@ QD_EXPORT PyObject *qd_log_recent_py(long limit) {
569574
return NULL;
570575
}
571576

572-
void qd_log_initialize(void)
577+
void qd_log_initialize(void) TA_NO_THREAD_SAFETY_ANALYSIS
573578
{
574579
DEQ_INIT(entries);
575580
DEQ_INIT(sink_list);
@@ -589,8 +594,8 @@ void qd_log_initialize(void)
589594
default_log_source->sink = log_sink_lh(SINK_STDERR);
590595
}
591596

592-
593-
void qd_log_finalize(void) {
597+
void qd_log_finalize(void) TA_NO_THREAD_SAFETY_ANALYSIS
598+
{
594599
for (int i = 0; i < NUM_LOG_SOURCES; i++)
595600
qd_log_source_free_lh(i);
596601
while (DEQ_HEAD(entries))

0 commit comments

Comments
 (0)