// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. #ifndef DORIS_BE_SRC_UTIL_THREAD_H #define DORIS_BE_SRC_UTIL_THREAD_H #include #include #include #include "common/status.h" #include "gutil/ref_counted.h" #include "http/web_page_handler.h" #include "util/countdown_latch.h" namespace doris { class Thread : public RefCountedThreadSafe { public: enum CreateFlags { NO_FLAGS = 0, NO_STACK_WATCHDOG = 1 }; template static Status create_with_flags(const std::string& category, const std::string& name, const F& f, uint64_t flags, scoped_refptr* holder) { return start_thread(category, name, f, flags, holder); } template static Status create(const std::string& category, const std::string& name, const F& f, scoped_refptr* holder) { return start_thread(category, name, f, NO_FLAGS, holder); } template static Status create(const std::string& category, const std::string& name, const F& f, const A1& a1, scoped_refptr* holder) { return start_thread(category, name, std::bind(f, a1), NO_FLAGS, holder); } template static Status create(const std::string& category, const std::string& name, const F& f, const A1& a1, const A2& a2, scoped_refptr* holder) { return start_thread(category, name, std::bind(f, a1, a2), NO_FLAGS, holder); } template static Status create(const std::string& category, const std::string& name, const F& f, const A1& a1, const A2& a2, const A3& a3, scoped_refptr* holder) { return start_thread(category, name, std::bind(f, a1, a2, a3), NO_FLAGS, holder); } template static Status create(const std::string& category, const std::string& name, const F& f, const A1& a1, const A2& a2, const A3& a3, const A4& a4, scoped_refptr* holder) { return start_thread(category, name, std::bind(f, a1, a2, a3, a4), NO_FLAGS, holder); } template static Status create(const std::string& category, const std::string& name, const F& f, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, scoped_refptr* holder) { return start_thread(category, name, std::bind(f, a1, a2, a3, a4, a5), NO_FLAGS, holder); } template static Status create(const std::string& category, const std::string& name, const F& f, const A1& a1, const A2& a2, const A3& a3, const A4& a4, const A5& a5, const A6& a6, scoped_refptr* holder) { return start_thread(category, name, std::bind(f, a1, a2, a3, a4, a5, a6), NO_FLAGS, holder); } ~Thread(); // Blocks until this thread finishes execution. Once this method returns, the thread // will be unregistered with the ThreadMgr and will not appear in the debug UI. void join(); // The thread ID assigned to this thread by the operating system. If the thread // has not yet started running, returns INVALID_TID. // // NOTE: this may block for a short amount of time if the thread has just been // started. int64_t tid() const; // Returns the thread's pthread ID. pthread_t pthread_id() const; const std::string& name() const; const std::string& category() const; std::string to_string() const; // The current thread of execution, or nullptr if the current thread isn't a doris::Thread. // This call is signal-safe. static Thread* current_thread(); // Returns a unique, stable identifier for this thread. Note that this is a static // method and thus can be used on any thread, including the main thread of the // process. // // In general, this should be used when a value is required that is unique to // a thread and must work on any thread including the main process thread. // // NOTE: this is _not_ the TID, but rather a unique value assigned by the // thread implementation. So, this value should not be presented to the user // in log messages, etc. static int64_t unique_thread_id(); // Returns the system thread ID (tid on Linux) for the current thread. Note // that this is a static method and thus can be used from any thread, // including the main thread of the process. This is in contrast to // Thread::tid(), which only works on doris::Threads. // // Thread::tid() will return the same value, but the value is cached in the // Thread object, so will be faster to call. // // Thread::unique_thread_id() (or Thread::tid()) should be preferred for // performance sensitive code, however it is only guaranteed to return a // unique and stable thread ID, not necessarily the system thread ID. static int64_t current_thread_id(); private: friend class ThreadJoiner; enum { INVALID_TID = -1, PARENT_WAITING_TID = -2, }; // User function to be executed by this thread. typedef std::function ThreadFunctor; Thread(const std::string& category, const std::string& name, ThreadFunctor functor) : _thread(0), _tid(INVALID_TID), _functor(std::move(functor)), _category(std::move(category)), _name(std::move(name)), _done(1), _joinable(false) {} // Library-specific thread ID. pthread_t _thread; // OS-specific thread ID. Once the constructor finishes start_thread(), // guaranteed to be set either to a non-negative integer, or to INVALID_TID. // // The tid_ member goes through the following states: // 1. INVALID_TID: the thread has not been started, or has already exited. // 2. PARENT_WAITING_TID: the parent has started the thread, but the // thread has not yet begun running. Therefore the TID is not yet known // but it will be set once the thread starts. // 3. : the thread is running. int64_t _tid; const ThreadFunctor _functor; const std::string _category; const std::string _name; // Joiners wait on this latch to be notified if the thread is done. // // Note that Joiners must additionally pthread_join(), otherwise certain // resources that callers expect to be destroyed (like TLS) may still be // alive when a Joiner finishes. CountDownLatch _done; bool _joinable; // Thread local pointer to the current thread of execution. Will be nullptr if the current // thread is not a Thread. static __thread Thread* _tls; // Wait for the running thread to publish its tid. int64_t wait_for_tid() const; // Starts the thread running supervise_thread(), and returns once that thread has // initialised and its TID has been read. Waits for notification from the started // thread that initialisation is complete before returning. On success, stores a // reference to the thread in holder. static Status start_thread(const std::string& category, const std::string& name, const ThreadFunctor& functor, uint64_t flags, scoped_refptr* holder); // Wrapper for the user-supplied function. Invoked from the new thread, // with the Thread as its only argument. Executes _functor, but before // doing so registers with the global ThreadMgr and reads the thread's // system ID. After _functor terminates, unregisters with the ThreadMgr. // Always returns nullptr. // // supervise_thread() notifies start_thread() when thread initialisation is // completed via the _tid, which is set to the new thread's system ID. // By that point in time supervise_thread() has also taken a reference to // the Thread object, allowing it to safely refer to it even after the // caller drops its reference. // // Additionally, start_thread() notifies supervise_thread() when the actual // Thread object has been assigned (supervise_thread() is spinning during // this time). Without this, the new thread may reference the actual // Thread object before it has been assigned by start_thread(). See // KUDU-11 for more details. static void* supervise_thread(void* arg); // Invoked when the user-supplied function finishes or in the case of an // abrupt exit (i.e. pthread_exit()). Cleans up after supervise_thread(). static void finish_thread(void* arg); static void init_threadmgr(); }; // Utility to join on a thread, printing warning messages if it // takes too long. For example: // // ThreadJoiner(&my_thread, "processing thread") // .warn_after_ms(1000) // .warn_every_ms(5000) // .Join(); // // TODO: would be nice to offer a way to use ptrace() or signals to // dump the stack trace of the thread we're trying to join on if it // gets stuck. But, after looking for 20 minutes or so, it seems // pretty complicated to get right. class ThreadJoiner { public: explicit ThreadJoiner(Thread* thread); // Start emitting warnings after this many milliseconds. // // Default: 1000 ms. ThreadJoiner& warn_after_ms(int ms); // After the warnings after started, emit another warning at the // given interval. // // Default: 1000 ms. ThreadJoiner& warn_every_ms(int ms); // If the thread has not stopped after this number of milliseconds, give up // joining on it and return Status::Aborted. // // -1 (the default) means to wait forever trying to join. ThreadJoiner& give_up_after_ms(int ms); // Join the thread, subject to the above parameters. If the thread joining // fails for any reason, returns RuntimeError. If it times out, returns // Aborted. Status join(); private: enum { kDefaultWarnAfterMs = 1000, kDefaultWarnEveryMs = 1000, kDefaultGiveUpAfterMs = -1 // forever }; Thread* _thread; int _warn_after_ms; int _warn_every_ms; int _give_up_after_ms; DISALLOW_COPY_AND_ASSIGN(ThreadJoiner); }; // Registers /threadz with the debug webserver. void register_thread_display_page(WebPageHandler* web_page_handler); } //namespace doris #endif //DORIS_BE_SRC_UTIL_THREAD_H