kmp_cancel.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. //===----------------------------------------------------------------------===//
  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. #include "kmp.h"
  9. #include "kmp_i18n.h"
  10. #include "kmp_io.h"
  11. #include "kmp_str.h"
  12. #if OMPT_SUPPORT
  13. #include "ompt-specific.h"
  14. #endif
  15. /*!
  16. @ingroup CANCELLATION
  17. @param loc_ref location of the original task directive
  18. @param gtid Global thread ID of encountering thread
  19. @param cncl_kind Cancellation kind (parallel, for, sections, taskgroup)
  20. @return returns true if the cancellation request has been activated and the
  21. execution thread needs to proceed to the end of the canceled region.
  22. Request cancellation of the binding OpenMP region.
  23. */
  24. kmp_int32 __kmpc_cancel(ident_t *loc_ref, kmp_int32 gtid, kmp_int32 cncl_kind) {
  25. kmp_info_t *this_thr = __kmp_threads[gtid];
  26. KC_TRACE(10, ("__kmpc_cancel: T#%d request %d OMP_CANCELLATION=%d\n", gtid,
  27. cncl_kind, __kmp_omp_cancellation));
  28. KMP_DEBUG_ASSERT(cncl_kind != cancel_noreq);
  29. KMP_DEBUG_ASSERT(cncl_kind == cancel_parallel || cncl_kind == cancel_loop ||
  30. cncl_kind == cancel_sections ||
  31. cncl_kind == cancel_taskgroup);
  32. KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid);
  33. if (__kmp_omp_cancellation) {
  34. switch (cncl_kind) {
  35. case cancel_parallel:
  36. case cancel_loop:
  37. case cancel_sections:
  38. // cancellation requests for parallel and worksharing constructs
  39. // are handled through the team structure
  40. {
  41. kmp_team_t *this_team = this_thr->th.th_team;
  42. KMP_DEBUG_ASSERT(this_team);
  43. kmp_int32 old = cancel_noreq;
  44. this_team->t.t_cancel_request.compare_exchange_strong(old, cncl_kind);
  45. if (old == cancel_noreq || old == cncl_kind) {
  46. // we do not have a cancellation request in this team or we do have
  47. // one that matches the current request -> cancel
  48. #if OMPT_SUPPORT && OMPT_OPTIONAL
  49. if (ompt_enabled.ompt_callback_cancel) {
  50. ompt_data_t *task_data;
  51. __ompt_get_task_info_internal(0, NULL, &task_data, NULL, NULL,
  52. NULL);
  53. ompt_cancel_flag_t type = ompt_cancel_parallel;
  54. if (cncl_kind == cancel_parallel)
  55. type = ompt_cancel_parallel;
  56. else if (cncl_kind == cancel_loop)
  57. type = ompt_cancel_loop;
  58. else if (cncl_kind == cancel_sections)
  59. type = ompt_cancel_sections;
  60. ompt_callbacks.ompt_callback(ompt_callback_cancel)(
  61. task_data, type | ompt_cancel_activated,
  62. OMPT_GET_RETURN_ADDRESS(0));
  63. }
  64. #endif // OMPT_SUPPORT && OMPT_OPTIONAL
  65. return 1 /* true */;
  66. }
  67. break;
  68. }
  69. case cancel_taskgroup:
  70. // cancellation requests for a task group
  71. // are handled through the taskgroup structure
  72. {
  73. kmp_taskdata_t *task;
  74. kmp_taskgroup_t *taskgroup;
  75. task = this_thr->th.th_current_task;
  76. KMP_DEBUG_ASSERT(task);
  77. taskgroup = task->td_taskgroup;
  78. if (taskgroup) {
  79. kmp_int32 old = cancel_noreq;
  80. taskgroup->cancel_request.compare_exchange_strong(old, cncl_kind);
  81. if (old == cancel_noreq || old == cncl_kind) {
  82. // we do not have a cancellation request in this taskgroup or we do
  83. // have one that matches the current request -> cancel
  84. #if OMPT_SUPPORT && OMPT_OPTIONAL
  85. if (ompt_enabled.ompt_callback_cancel) {
  86. ompt_data_t *task_data;
  87. __ompt_get_task_info_internal(0, NULL, &task_data, NULL, NULL,
  88. NULL);
  89. ompt_callbacks.ompt_callback(ompt_callback_cancel)(
  90. task_data, ompt_cancel_taskgroup | ompt_cancel_activated,
  91. OMPT_GET_RETURN_ADDRESS(0));
  92. }
  93. #endif
  94. return 1 /* true */;
  95. }
  96. } else {
  97. // TODO: what needs to happen here?
  98. // the specification disallows cancellation w/o taskgroups
  99. // so we might do anything here, let's abort for now
  100. KMP_ASSERT(0 /* false */);
  101. }
  102. }
  103. break;
  104. default:
  105. KMP_ASSERT(0 /* false */);
  106. }
  107. }
  108. // ICV OMP_CANCELLATION=false, so we ignored this cancel request
  109. KMP_DEBUG_ASSERT(!__kmp_omp_cancellation);
  110. return 0 /* false */;
  111. }
  112. /*!
  113. @ingroup CANCELLATION
  114. @param loc_ref location of the original task directive
  115. @param gtid Global thread ID of encountering thread
  116. @param cncl_kind Cancellation kind (parallel, for, sections, taskgroup)
  117. @return returns true if a matching cancellation request has been flagged in the
  118. RTL and the encountering thread has to cancel..
  119. Cancellation point for the encountering thread.
  120. */
  121. kmp_int32 __kmpc_cancellationpoint(ident_t *loc_ref, kmp_int32 gtid,
  122. kmp_int32 cncl_kind) {
  123. kmp_info_t *this_thr = __kmp_threads[gtid];
  124. KC_TRACE(10,
  125. ("__kmpc_cancellationpoint: T#%d request %d OMP_CANCELLATION=%d\n",
  126. gtid, cncl_kind, __kmp_omp_cancellation));
  127. KMP_DEBUG_ASSERT(cncl_kind != cancel_noreq);
  128. KMP_DEBUG_ASSERT(cncl_kind == cancel_parallel || cncl_kind == cancel_loop ||
  129. cncl_kind == cancel_sections ||
  130. cncl_kind == cancel_taskgroup);
  131. KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid);
  132. if (__kmp_omp_cancellation) {
  133. switch (cncl_kind) {
  134. case cancel_parallel:
  135. case cancel_loop:
  136. case cancel_sections:
  137. // cancellation requests for parallel and worksharing constructs
  138. // are handled through the team structure
  139. {
  140. kmp_team_t *this_team = this_thr->th.th_team;
  141. KMP_DEBUG_ASSERT(this_team);
  142. if (this_team->t.t_cancel_request) {
  143. if (cncl_kind == this_team->t.t_cancel_request) {
  144. // the request in the team structure matches the type of
  145. // cancellation point so we can cancel
  146. #if OMPT_SUPPORT && OMPT_OPTIONAL
  147. if (ompt_enabled.ompt_callback_cancel) {
  148. ompt_data_t *task_data;
  149. __ompt_get_task_info_internal(0, NULL, &task_data, NULL, NULL,
  150. NULL);
  151. ompt_cancel_flag_t type = ompt_cancel_parallel;
  152. if (cncl_kind == cancel_parallel)
  153. type = ompt_cancel_parallel;
  154. else if (cncl_kind == cancel_loop)
  155. type = ompt_cancel_loop;
  156. else if (cncl_kind == cancel_sections)
  157. type = ompt_cancel_sections;
  158. ompt_callbacks.ompt_callback(ompt_callback_cancel)(
  159. task_data, type | ompt_cancel_detected,
  160. OMPT_GET_RETURN_ADDRESS(0));
  161. }
  162. #endif
  163. return 1 /* true */;
  164. }
  165. KMP_ASSERT(0 /* false */);
  166. } else {
  167. // we do not have a cancellation request pending, so we just
  168. // ignore this cancellation point
  169. return 0;
  170. }
  171. break;
  172. }
  173. case cancel_taskgroup:
  174. // cancellation requests for a task group
  175. // are handled through the taskgroup structure
  176. {
  177. kmp_taskdata_t *task;
  178. kmp_taskgroup_t *taskgroup;
  179. task = this_thr->th.th_current_task;
  180. KMP_DEBUG_ASSERT(task);
  181. taskgroup = task->td_taskgroup;
  182. if (taskgroup) {
  183. // return the current status of cancellation for the taskgroup
  184. #if OMPT_SUPPORT && OMPT_OPTIONAL
  185. if (ompt_enabled.ompt_callback_cancel &&
  186. !!taskgroup->cancel_request) {
  187. ompt_data_t *task_data;
  188. __ompt_get_task_info_internal(0, NULL, &task_data, NULL, NULL,
  189. NULL);
  190. ompt_callbacks.ompt_callback(ompt_callback_cancel)(
  191. task_data, ompt_cancel_taskgroup | ompt_cancel_detected,
  192. OMPT_GET_RETURN_ADDRESS(0));
  193. }
  194. #endif
  195. return !!taskgroup->cancel_request;
  196. } else {
  197. // if a cancellation point is encountered by a task that does not
  198. // belong to a taskgroup, it is OK to ignore it
  199. return 0 /* false */;
  200. }
  201. }
  202. default:
  203. KMP_ASSERT(0 /* false */);
  204. }
  205. }
  206. // ICV OMP_CANCELLATION=false, so we ignore the cancellation point
  207. KMP_DEBUG_ASSERT(!__kmp_omp_cancellation);
  208. return 0 /* false */;
  209. }
  210. /*!
  211. @ingroup CANCELLATION
  212. @param loc_ref location of the original task directive
  213. @param gtid Global thread ID of encountering thread
  214. @return returns true if a matching cancellation request has been flagged in the
  215. RTL and the encountering thread has to cancel..
  216. Barrier with cancellation point to send threads from the barrier to the
  217. end of the parallel region. Needs a special code pattern as documented
  218. in the design document for the cancellation feature.
  219. */
  220. kmp_int32 __kmpc_cancel_barrier(ident_t *loc, kmp_int32 gtid) {
  221. int ret = 0 /* false */;
  222. kmp_info_t *this_thr = __kmp_threads[gtid];
  223. kmp_team_t *this_team = this_thr->th.th_team;
  224. KMP_DEBUG_ASSERT(__kmp_get_gtid() == gtid);
  225. // call into the standard barrier
  226. __kmpc_barrier(loc, gtid);
  227. // if cancellation is active, check cancellation flag
  228. if (__kmp_omp_cancellation) {
  229. // depending on which construct to cancel, check the flag and
  230. // reset the flag
  231. switch (KMP_ATOMIC_LD_RLX(&(this_team->t.t_cancel_request))) {
  232. case cancel_parallel:
  233. ret = 1;
  234. // ensure that threads have checked the flag, when
  235. // leaving the above barrier
  236. __kmpc_barrier(loc, gtid);
  237. this_team->t.t_cancel_request = cancel_noreq;
  238. // the next barrier is the fork/join barrier, which
  239. // synchronizes the threads leaving here
  240. break;
  241. case cancel_loop:
  242. case cancel_sections:
  243. ret = 1;
  244. // ensure that threads have checked the flag, when
  245. // leaving the above barrier
  246. __kmpc_barrier(loc, gtid);
  247. this_team->t.t_cancel_request = cancel_noreq;
  248. // synchronize the threads again to make sure we do not have any run-away
  249. // threads that cause a race on the cancellation flag
  250. __kmpc_barrier(loc, gtid);
  251. break;
  252. case cancel_taskgroup:
  253. // this case should not occur
  254. KMP_ASSERT(0 /* false */);
  255. break;
  256. case cancel_noreq:
  257. // do nothing
  258. break;
  259. default:
  260. KMP_ASSERT(0 /* false */);
  261. }
  262. }
  263. return ret;
  264. }
  265. /*!
  266. @ingroup CANCELLATION
  267. @param loc_ref location of the original task directive
  268. @param gtid Global thread ID of encountering thread
  269. @return returns true if a matching cancellation request has been flagged in the
  270. RTL and the encountering thread has to cancel..
  271. Query function to query the current status of cancellation requests.
  272. Can be used to implement the following pattern:
  273. if (kmp_get_cancellation_status(kmp_cancel_parallel)) {
  274. perform_cleanup();
  275. #pragma omp cancellation point parallel
  276. }
  277. */
  278. int __kmp_get_cancellation_status(int cancel_kind) {
  279. if (__kmp_omp_cancellation) {
  280. kmp_info_t *this_thr = __kmp_entry_thread();
  281. switch (cancel_kind) {
  282. case cancel_parallel:
  283. case cancel_loop:
  284. case cancel_sections: {
  285. kmp_team_t *this_team = this_thr->th.th_team;
  286. return this_team->t.t_cancel_request == cancel_kind;
  287. }
  288. case cancel_taskgroup: {
  289. kmp_taskdata_t *task;
  290. kmp_taskgroup_t *taskgroup;
  291. task = this_thr->th.th_current_task;
  292. taskgroup = task->td_taskgroup;
  293. return taskgroup && taskgroup->cancel_request;
  294. }
  295. }
  296. }
  297. return 0 /* false */;
  298. }