It is possible to use clang -Wthread-safety to do some basic coroutine checks: http://lists.llvm.org/pipermail/cfe-dev/2017-June/054372.html https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
This will basically check that you don't call accidentally a coroutine function from a non-coroutine, as this may crash at run time if the coroutine function yields. I had to modify clang to support annotations on typedef and function pointers, and check some function assignments/arguments. The end result is quire far from ready for upstream review, but could serve as basis for more checks or work. (https://github.com/elmarco/clang qemu-ta branch) Signed-off-by: Marc-André Lureau <marcandre.lur...@redhat.com> --- include/qemu/coroutine.h | 31 ++++++++++++++++++++++++++++++- util/coroutine-sigaltstack.c | 2 ++ util/coroutine-ucontext.c | 2 ++ util/coroutine-win32.c | 2 ++ util/qemu-coroutine.c | 2 ++ 5 files changed, 38 insertions(+), 1 deletion(-) diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index a4509bd977..35ff394f51 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -28,6 +28,34 @@ * These functions are re-entrant and may be used outside the global mutex. */ +/* clang thread-safety attributes, used for static analysis of the CFG */ +#if defined(__clang__) && (!defined(SWIG)) +#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x)) +#else +#define THREAD_ANNOTATION_ATTRIBUTE__(x) +#endif + +#define TAA_ROLE \ + THREAD_ANNOTATION_ATTRIBUTE__(capability("role")) + +#define TAA_REQUIRES(...) \ + THREAD_ANNOTATION_ATTRIBUTE__(requires_capability(__VA_ARGS__)) + +#define TAA_ACQUIRE(R) \ + THREAD_ANNOTATION_ATTRIBUTE__(acquire_capability(R)) + +#define TAA_RELEASE(R) \ + THREAD_ANNOTATION_ATTRIBUTE__(release_capability(R)) + +#define TAA_NO_ANALYSYS \ + THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis) + +typedef int TAA_ROLE coroutine_role; +extern coroutine_role _coroutine_fn; + +static inline void co_role_acquire(coroutine_role R) TAA_ACQUIRE(R) TAA_NO_ANALYSYS {} +static inline void co_role_release(coroutine_role R) TAA_RELEASE(R) TAA_NO_ANALYSYS {} + /** * Mark a function that executes in coroutine context * @@ -42,7 +70,8 @@ * .... * } */ -#define coroutine_fn + +#define coroutine_fn TAA_REQUIRES(_coroutine_fn) typedef struct Coroutine Coroutine; diff --git a/util/coroutine-sigaltstack.c b/util/coroutine-sigaltstack.c index f6fc49a0e5..05d1a378d1 100644 --- a/util/coroutine-sigaltstack.c +++ b/util/coroutine-sigaltstack.c @@ -98,7 +98,9 @@ static void coroutine_bootstrap(CoroutineSigAltStack *self, Coroutine *co) } while (true) { + co_role_acquire(_coroutine_fn); co->entry(co->entry_arg); + co_role_release(_coroutine_fn); qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); } } diff --git a/util/coroutine-ucontext.c b/util/coroutine-ucontext.c index 6621f3f692..010fbaedf1 100644 --- a/util/coroutine-ucontext.c +++ b/util/coroutine-ucontext.c @@ -76,7 +76,9 @@ static void coroutine_trampoline(int i0, int i1) } while (true) { + co_role_acquire(_coroutine_fn); co->entry(co->entry_arg); + co_role_release(_coroutine_fn); qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); } } diff --git a/util/coroutine-win32.c b/util/coroutine-win32.c index de6bd4fd3e..75a3bed543 100644 --- a/util/coroutine-win32.c +++ b/util/coroutine-win32.c @@ -64,7 +64,9 @@ static void CALLBACK coroutine_trampoline(void *co_) Coroutine *co = co_; while (true) { + co_role_acquire(_coroutine_fn); co->entry(co->entry_arg); + co_role_release(_coroutine_fn); qemu_coroutine_switch(co, co->caller, COROUTINE_TERMINATE); } } diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c index d6095c1d5a..efa0f20e69 100644 --- a/util/qemu-coroutine.c +++ b/util/qemu-coroutine.c @@ -25,6 +25,8 @@ enum { POOL_BATCH_SIZE = 64, }; +coroutine_role _coroutine_fn; + /** Free list to speed up creation */ static QSLIST_HEAD(, Coroutine) release_pool = QSLIST_HEAD_INITIALIZER(pool); static unsigned int release_pool_size; -- 2.13.1.395.gf7b71de06