task_info.h 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. //
  2. // Copyright (c) 2021-2026, Manticore Software LTD (https://manticoresearch.com)
  3. // All rights reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License. You should have
  7. // received a copy of the GPL license along with this program; if you
  8. // did not, you can find it at http://www.gnu.org/
  9. //
  10. #pragma once
  11. #include "sphinxstd.h"
  12. #include "threadutils.h"
  13. #include <optional>
  14. /*
  15. * Lowest info came from thread. It includes low details as thread name, thread ID and basic profile statistic.
  16. *
  17. * When a task engaged into thread, we also have details about the task, like connection id, proto, etc.
  18. * OR, for system task - the name of the action.
  19. *
  20. * Task may produce subtasks - like parallel processing rt chunks, and they also include details like current chunk N.
  21. * It may be any kind of different pieces of data, coming from different subtasks, and each need to be processed
  22. * different way, depends on the essence of the data itself.
  23. *
  24. * When task engaged, it fills its own TaskInfo structure with its essential data, and stores it in the
  25. * field m_pTaskInfo of thread descriptor LowThreadDesc_t. Previous value of that field stored in task's m_pNext field.
  26. * So, moving deep from task to task we finally have linked list of task infos, starting from one stored in Thread
  27. * and moving forward with m_pNext pointers.
  28. *
  29. * Each piece lives in own context (task) and owned by the task.
  30. * For displaying data (in show threads) we have 'public thread descriptor' with plain structure and fixed fields
  31. * Filling of that structure from low thread descriptor performed by GatherPublicThreadsInfo. It first copies low thread
  32. * attributes, and then walk over task info chains, collecting essential data.
  33. *
  34. * Each task info has TaskType flag, and provides function which publish essential data into public descriptor.
  35. * When unwinding task chain we look for the type and call saved function, if any.
  36. * If no 'displaying' func provided, we just skip to the next layer.
  37. *
  38. */
  39. // snapshot of current thread and tasks, used in 'show threads', etc. and contains flat rendered info.
  40. struct PublicThreadDesc_t
  41. {
  42. int m_iThreadID = -1; ///< OS thread id
  43. std::optional<int64_t> m_tmStart; ///< when did the current request start?
  44. int64_t m_tmLastJobStartTimeUS = -1;///< time where I've done something useful
  45. int64_t m_tmLastJobDoneTimeUS = -1; ///< time where I've done something useful
  46. int64_t m_tmTotalWorkedTimeUS = -1; ///< total time I've worked on useful tasks
  47. int64_t m_tmTotalWorkedCPUTimeUS = -1; ///< total time I've worked on useful tasks
  48. int64_t m_iTotalJobsDone = -1; ///< total jobs I've completed
  49. CSphString m_sThreadName;
  50. StringBuilder_c m_sChain;
  51. StringBuilder_c m_sClientName {" "};
  52. StringBuilder_c m_sDescription {" "};
  53. StringBuilder_c m_sProto {","};
  54. int m_iDescriptionLimit = -1; ///< cb flag when collecting info with columns=N, avoid copy huge descriptions then
  55. int64_t m_tmConnect = -1; ///< when did the client connect?
  56. std::unique_ptr<CSphQuery> m_pQuery; /// currently running query, if not sphinxql
  57. const char* m_szCommand = nullptr; /// simple static SYSTEM, SELECT, UPDATE, etc. Used in show threads, crash dumping
  58. int m_iConnID = -1; ///< current conn-id for this thread. For logging and tracking in mysql
  59. Proto_e m_eProto = Proto_e::UNKNOWN; /// used in show threads to format or not format query
  60. TaskState_e m_eTaskState = TaskState_e::UNKNOWN; /// show threads, crash dumping
  61. bool m_bKilled = false; /// informational about if session is killed.
  62. PublicThreadDesc_t() = default;
  63. void Swap (PublicThreadDesc_t& rhs);
  64. MOVE_BYSWAP ( PublicThreadDesc_t )
  65. };
  66. // flatten info from thread. iCols make hint for huge descriptions to avoid full copy
  67. PublicThreadDesc_t GatherPublicThreadInfo ( const Threads::LowThreadDesc_t * pSrc, int iCols );
  68. // internal helpers
  69. // we don't expect all possible taskinfos being located in this file,
  70. // Instead, for each type of info you need to call RegisterRenderer and provide pointer to function which knows how to
  71. // fill PublicThreadDesc from given type. Byte returned by registrar is then became ID of that type of info,
  72. // and has to be written in m_eType field of each instance of such info
  73. using RenderFnPtr = void ( * ) ( const void * pSrc, PublicThreadDesc_t & dDst );
  74. BYTE RegisterRenderer ( RenderFnPtr pFunc ) noexcept;
  75. // Declare static member func 'Render', and provide initial registration and storing type ID
  76. #define DECLARE_RENDER( TASKINFO ) \
  77. static BYTE Task() { \
  78. static BYTE eTask = RegisterRenderer ( TASKINFO::Render ); \
  79. return eTask; \
  80. } \
  81. TASKINFO () noexcept { \
  82. m_eType = Task(); \
  83. } \
  84. static void Render ( const void * pSrc, PublicThreadDesc_t & dDst )
  85. // Define declared stuff (has to be written in .cpp to avoid multiple definitions)
  86. #define DEFINE_RENDER( TASKINFO ) \
  87. void TASKINFO::Render ( const void * pSrc, PublicThreadDesc_t & dDst )
  88. // generic task info
  89. struct TaskInfo_t
  90. {
  91. DECLARE_RENDER ( TaskInfo_t );
  92. std::atomic<void *> m_pPrev { nullptr }; // link to previous (parent) chain. Hazard, NOT owned!
  93. BYTE m_eType;
  94. };
  95. struct RefCount_t {
  96. static void Inc ( BYTE eType );
  97. static void Dec ( BYTE eType );
  98. };
  99. struct NoRefCount_t
  100. {
  101. static void Inc ( BYTE ) {}
  102. static void Dec ( BYTE ) {}
  103. };
  104. void GatherPublicTaskInfo ( PublicThreadDesc_t& dDesc, const std::atomic<void*>& pTask );
  105. // RAII task info
  106. // Store info to TLS root, stores previous root to the chain
  107. // On dtr restores stored chain as root and retire info (uses hazard pointers)
  108. // if explicit root provided - uses it instead of TLS root.
  109. template<typename TASKINFO, typename REFCOUNT=RefCount_t>
  110. class ScopedInfo_T
  111. {
  112. protected:
  113. hazard::ScopedPtr_t<TASKINFO*> m_pInfo;
  114. public:
  115. explicit ScopedInfo_T ( TASKINFO* pInfo )
  116. : m_pInfo ( pInfo )
  117. {
  118. pInfo->m_pPrev = Threads::MyThd().m_pTaskInfo.exchange ( pInfo, std::memory_order_acq_rel );
  119. REFCOUNT::Inc ( TASKINFO::Task() );
  120. }
  121. explicit operator TASKINFO*() const noexcept { return m_pInfo.operator TASKINFO*(); };
  122. TASKINFO* operator->() const noexcept { return m_pInfo.operator->(); }
  123. ScopedInfo_T ( ScopedInfo_T&& rhs ) = delete;
  124. ScopedInfo_T& operator= ( ScopedInfo_T&& rhs) = delete;
  125. ~ScopedInfo_T ()
  126. {
  127. Threads::MyThd ().m_pTaskInfo.store ( m_pInfo->m_pPrev, std::memory_order_release );
  128. REFCOUNT::Dec ( TASKINFO::Task() );
  129. }
  130. };
  131. // make provided task info (by typed, NOT generic! ptr) displayed in the thread.
  132. // when produced RAII obj come out of scope, info will be hidden and retired.
  133. template<typename TASKINFO>
  134. ScopedInfo_T<TASKINFO> PublishTaskInfo ( TASKINFO* pPtr )
  135. {
  136. return ScopedInfo_T<TASKINFO> ( pPtr );
  137. }
  138. // wide used task infos (declare them here to be accessed from anywhere, and also as examples)
  139. // system task - command and description
  140. struct MiniTaskInfo_t : public TaskInfo_t
  141. {
  142. DECLARE_RENDER( MiniTaskInfo_t );
  143. void RenderWithoutChain ( PublicThreadDesc_t& dDst );
  144. int64_t m_tmStart = sphMicroTimer ();
  145. int64_t m_tmLastJobStartTimeUS = -1;
  146. int64_t m_tmLastJobDoneTimeUS = -1;
  147. const char * m_szCommand = nullptr; // is always mt-safe since always set static const
  148. hazard::ScopedPtr_t<const CSphString *> m_pHazardDescription;
  149. int m_iDescriptionLen = 0; // len of string in m_pHazardDescription
  150. };
  151. using ScopedMiniInfo_t = ScopedInfo_T<MiniTaskInfo_t>;
  152. using ScopedMiniInfoNoCount_t = ScopedInfo_T<MiniTaskInfo_t,NoRefCount_t>;
  153. // create and publish info about system task (what before did ThreadSystem_t)
  154. // command = 'SYSTEM', description = 'SYSTEM sDescription'
  155. ScopedMiniInfo_t PublishSystemInfo ( const char * sDescription );
  156. namespace myinfo {
  157. // descriptions in m_pHazardDescription bigger than this limit will be retired as soon as possible
  158. static const int HazardDescriptionSizeLimit = 256*1024;
  159. // thread-local m_pTaskInfo available globally from any thread
  160. // it is hazard, since implicitly assume that I own this info and so, it will not be deleted by another thread
  161. TaskInfo_t * HazardTaskInfo ();
  162. // num of tasks with given type
  163. int Count ( BYTE eType );
  164. // sum of all counters. Note, that might be quite useless, as one task may be 'decorated' by another
  165. int CountAll();
  166. // first ptr to node with type eType
  167. TaskInfo_t * GetHazardTypedNode ( BYTE eType );
  168. // bind current taskinfo content to handler
  169. Threads::Handler StickParent ( Threads::Handler fnHandler );
  170. // bind current taskinfo and add new scoped mini info for coro handler
  171. Threads::Handler OwnMini ( Threads::Handler fnHandler );
  172. // bind current taskinfo and add new scoped mini info for coro handler, without tracing N of mini
  173. // (for example, if you call it as subroutine in the same context and don't want to count it as separate task)
  174. Threads::Handler OwnMiniNoCount ( Threads::Handler fnHandler );
  175. // first ptr to node of given type
  176. template <typename TASKINFO>
  177. TASKINFO* ref()
  178. {
  179. return (TASKINFO *) GetHazardTypedNode ( TASKINFO::Task() );
  180. }
  181. // set MiniTaskInfo_t::m_sCommand
  182. void SetCommand ( const char * szCommand );
  183. void SetCommandDone ();
  184. // set MiniTaskInfo_t::m_pHazardDescription. and refresh timer
  185. // iLen used to select retire policy (lazy, or immediate retire)
  186. void SetDescription ( CSphString sDescription, int iLen );
  187. // set MiniTaskInfo_t::m_pHazardDescription and refresh timer
  188. void SetTaskInfo ( const char * sTemplate, ... );
  189. // returns non-guarded ref to MiniTaskInfo_t::m_pHazardDescription (to be used in same scope as set functions)
  190. Str_t UnsafeDescription ();
  191. // expect that we own or live shorter than client info. So, called it 'hazard' because of it.
  192. template<typename FILTER>
  193. TaskInfo_t* HazardGetNode ( FILTER fnFilter )
  194. {
  195. auto pSrc = (TaskInfo_t*)Threads::MyThd().m_pTaskInfo.load ( std::memory_order_relaxed );
  196. for ( ; pSrc; pSrc = (TaskInfo_t*)pSrc->m_pPrev.load ( std::memory_order_relaxed ) )
  197. if ( fnFilter ( pSrc ) )
  198. break;
  199. return pSrc;
  200. }
  201. MiniTaskInfo_t* HazardGetMini();
  202. } // namespace myinfo