Home | History | Annotate | Line # | Download | only in sanitizer_common
      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   struct Data {
     97     Args args;
     98     u32 gen;  // Avoid collision if thread id re-used.
     99     bool detached;
    100     bool done;
    101   };
    102 
    103   void CreateLocked(uptr thread, bool detached, const Args& args);
    104   u32 BeforeJoin(uptr thread) const;
    105   void AfterJoin(uptr thread, u32 gen);
    106   void DetachLocked(uptr thread);
    107 
    108   mutable Mutex mtx_;
    109 
    110   DenseMap<uptr, Data> data_;
    111   u32 gen_ = 0;
    112 };
    113 
    114 }  // namespace __sanitizer
    115 
    116 #endif  // SANITIZER_THREAD_ARG_RETVAL_H
    117