106c3fb27SDimitry Andric //===-- sanitizer_thread_arg_retval.h ---------------------------*- C++ -*-===// 206c3fb27SDimitry Andric // 306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 606c3fb27SDimitry Andric // 706c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 806c3fb27SDimitry Andric // 906c3fb27SDimitry Andric // This file is shared between sanitizer tools. 1006c3fb27SDimitry Andric // 1106c3fb27SDimitry Andric // Tracks thread arguments and return value for leak checking. 1206c3fb27SDimitry Andric //===----------------------------------------------------------------------===// 1306c3fb27SDimitry Andric 1406c3fb27SDimitry Andric #ifndef SANITIZER_THREAD_ARG_RETVAL_H 1506c3fb27SDimitry Andric #define SANITIZER_THREAD_ARG_RETVAL_H 1606c3fb27SDimitry Andric 1706c3fb27SDimitry Andric #include "sanitizer_common.h" 1806c3fb27SDimitry Andric #include "sanitizer_dense_map.h" 1906c3fb27SDimitry Andric #include "sanitizer_list.h" 2006c3fb27SDimitry Andric #include "sanitizer_mutex.h" 2106c3fb27SDimitry Andric 2206c3fb27SDimitry Andric namespace __sanitizer { 2306c3fb27SDimitry Andric 2406c3fb27SDimitry Andric // Primary goal of the class is to keep alive arg and retval pointer for leak 2506c3fb27SDimitry Andric // checking. However it can be used to pass those pointer into wrappers used by 2606c3fb27SDimitry Andric // interceptors. The difference from ThreadRegistry/ThreadList is that this 2706c3fb27SDimitry Andric // class keeps data up to the detach or join, as exited thread still can be 2806c3fb27SDimitry Andric // joined to retrive retval. ThreadRegistry/ThreadList can discard exited 2906c3fb27SDimitry Andric // threads immediately. 3006c3fb27SDimitry Andric class SANITIZER_MUTEX ThreadArgRetval { 3106c3fb27SDimitry Andric public: 3206c3fb27SDimitry Andric struct Args { 3306c3fb27SDimitry Andric void* (*routine)(void*); 3406c3fb27SDimitry Andric void* arg_retval; // Either arg or retval. 3506c3fb27SDimitry Andric }; Lock()3606c3fb27SDimitry Andric void Lock() SANITIZER_ACQUIRE() { mtx_.Lock(); } CheckLocked()3706c3fb27SDimitry Andric void CheckLocked() const SANITIZER_CHECK_LOCKED() { mtx_.CheckLocked(); } Unlock()3806c3fb27SDimitry Andric void Unlock() SANITIZER_RELEASE() { mtx_.Unlock(); } 3906c3fb27SDimitry Andric 4006c3fb27SDimitry Andric // Wraps pthread_create or similar. We need to keep object locked, to 4106c3fb27SDimitry Andric // prevent child thread from proceeding without thread handle. 4206c3fb27SDimitry Andric template <typename CreateFn /* returns thread id on success, or 0 */> Create(bool detached,const Args & args,const CreateFn & fn)4306c3fb27SDimitry Andric void Create(bool detached, const Args& args, const CreateFn& fn) { 4406c3fb27SDimitry Andric // No need to track detached threads with no args, but we will to do as it's 4506c3fb27SDimitry Andric // not expensive and less edge-cases. 4606c3fb27SDimitry Andric __sanitizer::Lock lock(&mtx_); 4706c3fb27SDimitry Andric if (uptr thread = fn()) 4806c3fb27SDimitry Andric CreateLocked(thread, detached, args); 4906c3fb27SDimitry Andric } 5006c3fb27SDimitry Andric 5106c3fb27SDimitry Andric // Returns thread arg and routine. 5206c3fb27SDimitry Andric Args GetArgs(uptr thread) const; 5306c3fb27SDimitry Andric 5406c3fb27SDimitry Andric // Mark thread as done and stores retval or remove if detached. Should be 5506c3fb27SDimitry Andric // called by the thread. 5606c3fb27SDimitry Andric void Finish(uptr thread, void* retval); 5706c3fb27SDimitry Andric 5806c3fb27SDimitry Andric // Mark thread as detached or remove if done. 5906c3fb27SDimitry Andric template <typename DetachFn /* returns true on success */> Detach(uptr thread,const DetachFn & fn)6006c3fb27SDimitry Andric void Detach(uptr thread, const DetachFn& fn) { 6106c3fb27SDimitry Andric // Lock to prevent re-use of the thread between fn() and DetachLocked() 6206c3fb27SDimitry Andric // calls. 6306c3fb27SDimitry Andric __sanitizer::Lock lock(&mtx_); 6406c3fb27SDimitry Andric if (fn()) 6506c3fb27SDimitry Andric DetachLocked(thread); 6606c3fb27SDimitry Andric } 6706c3fb27SDimitry Andric 6806c3fb27SDimitry Andric // Joins the thread. 6906c3fb27SDimitry Andric template <typename JoinFn /* returns true on success */> Join(uptr thread,const JoinFn & fn)7006c3fb27SDimitry Andric void Join(uptr thread, const JoinFn& fn) { 7106c3fb27SDimitry Andric // Remember internal id of the thread to prevent re-use of the thread 7206c3fb27SDimitry Andric // between fn() and AfterJoin() calls. Locking JoinFn, like in 7306c3fb27SDimitry Andric // Detach(), implementation can cause deadlock. 7406c3fb27SDimitry Andric auto gen = BeforeJoin(thread); 7506c3fb27SDimitry Andric if (fn()) 7606c3fb27SDimitry Andric AfterJoin(thread, gen); 7706c3fb27SDimitry Andric } 7806c3fb27SDimitry Andric 7906c3fb27SDimitry Andric // Returns all arg and retval which are considered alive. 8006c3fb27SDimitry Andric void GetAllPtrsLocked(InternalMmapVector<uptr>* ptrs); 8106c3fb27SDimitry Andric size()8206c3fb27SDimitry Andric uptr size() const { 8306c3fb27SDimitry Andric __sanitizer::Lock lock(&mtx_); 8406c3fb27SDimitry Andric return data_.size(); 8506c3fb27SDimitry Andric } 8606c3fb27SDimitry Andric 8706c3fb27SDimitry Andric // FIXME: Add fork support. Expected users of the class are sloppy with forks 8806c3fb27SDimitry Andric // anyway. We likely should lock/unlock the object to avoid deadlocks, and 8906c3fb27SDimitry Andric // erase all but the current threads, so we can detect leaked arg or retval in 9006c3fb27SDimitry Andric // child process. 9106c3fb27SDimitry Andric 9206c3fb27SDimitry Andric // FIXME: Add cancelation support. Now if a thread was canceled, the class 9306c3fb27SDimitry Andric // will keep pointers alive forever, missing leaks caused by cancelation. 9406c3fb27SDimitry Andric 9506c3fb27SDimitry Andric private: 96*297eecfbSDimitry Andric static const u32 kInvalidGen = UINT32_MAX; 9706c3fb27SDimitry Andric struct Data { 9806c3fb27SDimitry Andric Args args; 9906c3fb27SDimitry Andric u32 gen; // Avoid collision if thread id re-used. 10006c3fb27SDimitry Andric bool detached; 10106c3fb27SDimitry Andric bool done; 10206c3fb27SDimitry Andric }; 10306c3fb27SDimitry Andric 10406c3fb27SDimitry Andric void CreateLocked(uptr thread, bool detached, const Args& args); 10506c3fb27SDimitry Andric u32 BeforeJoin(uptr thread) const; 10606c3fb27SDimitry Andric void AfterJoin(uptr thread, u32 gen); 10706c3fb27SDimitry Andric void DetachLocked(uptr thread); 10806c3fb27SDimitry Andric 10906c3fb27SDimitry Andric mutable Mutex mtx_; 11006c3fb27SDimitry Andric 11106c3fb27SDimitry Andric DenseMap<uptr, Data> data_; 11206c3fb27SDimitry Andric u32 gen_ = 0; 11306c3fb27SDimitry Andric }; 11406c3fb27SDimitry Andric 11506c3fb27SDimitry Andric } // namespace __sanitizer 11606c3fb27SDimitry Andric 11706c3fb27SDimitry Andric #endif // SANITIZER_THREAD_ARG_RETVAL_H 118