xref: /freebsd/contrib/llvm-project/compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.h (revision 297eecfb02bb25902531dbb5c3b9a88caf8adf29)
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