| 1 | //===-- sanitizer_thread_arg_retval.h ---------------------------*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file is shared between sanitizer tools. |
| 10 | // |
| 11 | // Tracks thread arguments and return value for leak checking. |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
| 14 | #ifndef SANITIZER_THREAD_ARG_RETVAL_H |
| 15 | #define SANITIZER_THREAD_ARG_RETVAL_H |
| 16 | |
| 17 | #include "sanitizer_common.h" |
| 18 | #include "sanitizer_dense_map.h" |
| 19 | #include "sanitizer_list.h" |
| 20 | #include "sanitizer_mutex.h" |
| 21 | |
| 22 | namespace __sanitizer { |
| 23 | |
| 24 | // Primary goal of the class is to keep alive arg and retval pointer for leak |
| 25 | // checking. However it can be used to pass those pointer into wrappers used by |
| 26 | // interceptors. The difference from ThreadRegistry/ThreadList is that this |
| 27 | // class keeps data up to the detach or join, as exited thread still can be |
| 28 | // joined to retrive retval. ThreadRegistry/ThreadList can discard exited |
| 29 | // threads immediately. |
| 30 | class SANITIZER_MUTEX ThreadArgRetval { |
| 31 | public: |
| 32 | struct Args { |
| 33 | void* (*routine)(void*); |
| 34 | void* arg_retval; // Either arg or retval. |
| 35 | }; |
| 36 | void Lock() SANITIZER_ACQUIRE() { mtx_.Lock(); } |
| 37 | void CheckLocked() const SANITIZER_CHECK_LOCKED() { mtx_.CheckLocked(); } |
| 38 | void Unlock() SANITIZER_RELEASE() { mtx_.Unlock(); } |
| 39 | |
| 40 | // Wraps pthread_create or similar. We need to keep object locked, to |
| 41 | // prevent child thread from proceeding without thread handle. |
| 42 | template <typename CreateFn /* returns thread id on success, or 0 */> |
| 43 | void Create(bool detached, const Args& args, const CreateFn& fn) { |
| 44 | // No need to track detached threads with no args, but we will to do as it's |
| 45 | // not expensive and less edge-cases. |
| 46 | __sanitizer::Lock lock(&mtx_); |
| 47 | if (uptr thread = fn()) |
| 48 | CreateLocked(thread, detached, args); |
| 49 | } |
| 50 | |
| 51 | // Returns thread arg and routine. |
| 52 | Args GetArgs(uptr thread) const; |
| 53 | |
| 54 | // Mark thread as done and stores retval or remove if detached. Should be |
| 55 | // called by the thread. |
| 56 | void Finish(uptr thread, void* retval); |
| 57 | |
| 58 | // Mark thread as detached or remove if done. |
| 59 | template <typename DetachFn /* returns true on success */> |
| 60 | void Detach(uptr thread, const DetachFn& fn) { |
| 61 | // Lock to prevent re-use of the thread between fn() and DetachLocked() |
| 62 | // calls. |
| 63 | __sanitizer::Lock lock(&mtx_); |
| 64 | if (fn()) |
| 65 | DetachLocked(thread); |
| 66 | } |
| 67 | |
| 68 | // Joins the thread. |
| 69 | template <typename JoinFn /* returns true on success */> |
| 70 | void Join(uptr thread, const JoinFn& fn) { |
| 71 | // Remember internal id of the thread to prevent re-use of the thread |
| 72 | // between fn() and AfterJoin() calls. Locking JoinFn, like in |
| 73 | // Detach(), implementation can cause deadlock. |
| 74 | auto gen = BeforeJoin(thread); |
| 75 | if (fn()) |
| 76 | AfterJoin(thread, gen); |
| 77 | } |
| 78 | |
| 79 | // Returns all arg and retval which are considered alive. |
| 80 | void GetAllPtrsLocked(InternalMmapVector<uptr>* ptrs); |
| 81 | |
| 82 | uptr size() const { |
| 83 | __sanitizer::Lock lock(&mtx_); |
| 84 | return data_.size(); |
| 85 | } |
| 86 | |
| 87 | // FIXME: Add fork support. Expected users of the class are sloppy with forks |
| 88 | // anyway. We likely should lock/unlock the object to avoid deadlocks, and |
| 89 | // erase all but the current threads, so we can detect leaked arg or retval in |
| 90 | // child process. |
| 91 | |
| 92 | // FIXME: Add cancelation support. Now if a thread was canceled, the class |
| 93 | // will keep pointers alive forever, missing leaks caused by cancelation. |
| 94 | |
| 95 | private: |
| 96 | static const u32 kInvalidGen = UINT32_MAX; |
| 97 | struct Data { |
| 98 | Args args; |
| 99 | u32 gen; // Avoid collision if thread id re-used. |
| 100 | bool detached; |
| 101 | bool done; |
| 102 | }; |
| 103 | |
| 104 | void CreateLocked(uptr thread, bool detached, const Args& args); |
| 105 | u32 BeforeJoin(uptr thread) const; |
| 106 | void AfterJoin(uptr thread, u32 gen); |
| 107 | void DetachLocked(uptr thread); |
| 108 | |
| 109 | mutable Mutex mtx_; |
| 110 | |
| 111 | DenseMap<uptr, Data> data_; |
| 112 | u32 gen_ = 0; |
| 113 | }; |
| 114 | |
| 115 | } // namespace __sanitizer |
| 116 | |
| 117 | #endif // SANITIZER_THREAD_ARG_RETVAL_H |
| 118 | |