gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/util/thread_util.h (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #ifndef GVISOR_TEST_UTIL_THREAD_UTIL_H_
    16  #define GVISOR_TEST_UTIL_THREAD_UTIL_H_
    17  
    18  #include <pthread.h>
    19  #ifdef __linux__
    20  #include <sys/syscall.h>
    21  #endif
    22  #include <unistd.h>
    23  
    24  #include <functional>
    25  #include <utility>
    26  
    27  #include "test/util/logging.h"
    28  
    29  namespace gvisor {
    30  namespace testing {
    31  
    32  // ScopedThread is a minimal wrapper around pthreads.
    33  //
    34  // This is used in lieu of more complex mechanisms because it provides very
    35  // predictable behavior (no messing with timers, etc.) The thread will
    36  // automatically joined when it is destructed (goes out of scope), but can be
    37  // joined manually as well.
    38  class ScopedThread {
    39   public:
    40    // Constructs a thread that executes f exactly once.
    41    explicit ScopedThread(std::function<void*()> f) : f_(std::move(f)) {
    42      CreateThread();
    43    }
    44  
    45    explicit ScopedThread(const std::function<void()>& f) {
    46      f_ = [=] {
    47        f();
    48        return nullptr;
    49      };
    50      CreateThread();
    51    }
    52  
    53    ScopedThread(const ScopedThread& other) = delete;
    54    ScopedThread& operator=(const ScopedThread& other) = delete;
    55  
    56    // Joins the thread.
    57    ~ScopedThread() { Join(); }
    58  
    59    // Waits until this thread has finished executing. Join is idempotent and may
    60    // be called multiple times, however Join itself is not thread-safe.
    61    void* Join() {
    62      if (!joined_) {
    63        TEST_PCHECK(pthread_join(pt_, &retval_) == 0);
    64        joined_ = true;
    65      }
    66      return retval_;
    67    }
    68  
    69   private:
    70    void CreateThread() {
    71      TEST_PCHECK_MSG(pthread_create(
    72                          &pt_, /* attr = */ nullptr,
    73                          +[](void* arg) -> void* {
    74                            return static_cast<ScopedThread*>(arg)->f_();
    75                          },
    76                          this) == 0,
    77                      "thread creation failed");
    78    }
    79  
    80    std::function<void*()> f_;
    81    pthread_t pt_;
    82    bool joined_ = false;
    83    void* retval_ = nullptr;
    84  };
    85  
    86  #ifdef __linux__
    87  inline pid_t gettid() { return syscall(SYS_gettid); }
    88  #endif
    89  
    90  }  // namespace testing
    91  }  // namespace gvisor
    92  
    93  #endif  // GVISOR_TEST_UTIL_THREAD_UTIL_H_