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 | |