Skip to content

Commit 3f8b95a

Browse files
committed
Add Clang Thread Safety Annotations
1 parent acd49e4 commit 3f8b95a

File tree

14 files changed

+167
-55
lines changed

14 files changed

+167
-55
lines changed

CMakeLists.txt

Lines changed: 6 additions & 5 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
-Wno-gnu-statement-expression-from-macro-expansion)
6565

6666
# Set default build type. Must use FORCE because project() sets default to ""
@@ -139,9 +139,10 @@ if(NOT DEFINED VERSION)
139139
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD
140140
OUTPUT_VARIABLE BRANCH_NAME
141141
OUTPUT_STRIP_TRAILING_WHITESPACE)
142-
message(STATUS "BRANCH_NAME is ${BRANCH_NAME}")
143-
142+
message(STATUS "BRANCH_NAME is ${BRANCH_NAME}")
143+
144144
if (BRANCH_NAME STREQUAL "HEAD")
145+
145146
set(QPID_DISPATCH_VERSION "0.0.0")
146147
else(BRANCH_NAME STREQUAL "HEAD")
147148
# If the BRANCH_NAME is not HEAD (means you are on a branch, get the commit sha of the latest commit and
@@ -172,7 +173,7 @@ if(NOT DEFINED VERSION)
172173
# Git executable was not available, we will not be able to determine the version, just set it to "0.0.0"
173174
set(QPID_DISPATCH_VERSION "0.0.0")
174175
endif(Git_FOUND)
175-
176+
176177
else(NOT DEFINED VERSION)
177178
message(STATUS "VERSION is already provided")
178179
# What if VERSION is defined but someone passed in an empty value for VERSION? Deal with that case here.
@@ -185,7 +186,7 @@ else(NOT DEFINED VERSION)
185186
else()
186187
set(QPID_DISPATCH_VERSION ${VERSION})
187188
endif()
188-
endif(VERSION STREQUAL "")
189+
endif(VERSION STREQUAL "")
189190
endif(NOT DEFINED VERSION)
190191

191192
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
@@ -398,8 +398,8 @@ void qd_dispatch_free(qd_dispatch_t *qd)
398398
}
399399

400400

401-
QD_EXPORT void qd_dispatch_router_lock(qd_dispatch_t *qd) { sys_mutex_lock(&qd->router->lock); }
402-
QD_EXPORT void qd_dispatch_router_unlock(qd_dispatch_t *qd) { sys_mutex_unlock(&qd->router->lock); }
401+
QD_EXPORT void qd_dispatch_router_lock(qd_dispatch_t *qd) TA_ACQ(qd->router->lock) { sys_mutex_lock(&qd->router->lock); }
402+
QD_EXPORT void qd_dispatch_router_unlock(qd_dispatch_t *qd) TA_REL(qd->router->lock) { sys_mutex_unlock(&qd->router->lock); }
403403

404404
qdr_core_t* qd_dispatch_router_core(const qd_dispatch_t *qd)
405405
{

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
@@ -48,6 +48,7 @@
4848
const char *QD_LOG_STATS_TYPE = "logStats";
4949

5050
static qd_log_source_t *default_log_source=0;
51+
static sys_mutex_t log_source_lock;
5152

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

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

187189
// Must hold the log_source_lock
188-
static void log_sink_free_lh(log_sink_t* sink) {
190+
static void log_sink_free_lh(log_sink_t *sink) TA_REQ(log_source_lock)
191+
{
189192
if (!sink) return;
190193
assert(sink->ref_count);
191194

@@ -200,7 +203,8 @@ static void log_sink_free_lh(log_sink_t* sink) {
200203
}
201204
}
202205

203-
static log_sink_t* log_sink_lh(const char* name) {
206+
static log_sink_t *log_sink_lh(const char *name) TA_REQ(log_source_lock)
207+
{
204208
log_sink_t* sink = find_log_sink_lh(name);
205209
if (sink) {
206210
sys_atomic_inc(&sink->ref_count);
@@ -394,7 +398,8 @@ static void write_log_lh(qd_log_source_t *log_source, qd_log_entry_t *entry)
394398
}
395399

396400
/// Reset the log source to the default state
397-
static void qd_log_source_defaults(qd_log_source_t *log_source) {
401+
static void qd_log_source_defaults(qd_log_source_t *log_source) TA_REQ(log_source_lock)
402+
{
398403
log_source->mask = -1;
399404
log_source->includeTimestamp = -1;
400405
log_source->includeSource = -1;
@@ -403,7 +408,7 @@ static void qd_log_source_defaults(qd_log_source_t *log_source) {
403408
}
404409

405410
/// Caller must hold the log_source_lock
406-
static qd_log_source_t *qd_log_source_lh(qd_log_module_t module)
411+
static qd_log_source_t *qd_log_source_lh(qd_log_module_t module) TA_REQ(log_source_lock)
407412
{
408413
qd_log_source_t *log_source = log_sources[module];
409414

@@ -438,7 +443,7 @@ qd_log_source_t *qd_log_source_reset(qd_log_module_t module)
438443
return src;
439444
}
440445

441-
static void qd_log_source_free_lh(qd_log_module_t module)
446+
static void qd_log_source_free_lh(qd_log_module_t module) TA_REQ(log_source_lock)
442447
{
443448
qd_log_source_t *src = log_sources[module];
444449
if (src) {
@@ -570,7 +575,7 @@ QD_EXPORT PyObject *qd_log_recent_py(long limit) {
570575
return NULL;
571576
}
572577

573-
void qd_log_initialize(void)
578+
void qd_log_initialize(void) TA_NO_THREAD_SAFETY_ANALYSIS
574579
{
575580
DEQ_INIT(entries);
576581
DEQ_INIT(sink_list);
@@ -590,8 +595,8 @@ void qd_log_initialize(void)
590595
default_log_source->sink = log_sink_lh(SINK_STDERR);
591596
}
592597

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

0 commit comments

Comments
 (0)