You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

210 lines
8.7 KiB

  1. /*
  2. Copyright 2005-2014 Intel Corporation. All Rights Reserved.
  3. This file is part of Threading Building Blocks.
  4. Threading Building Blocks is free software; you can redistribute it
  5. and/or modify it under the terms of the GNU General Public License
  6. version 2 as published by the Free Software Foundation.
  7. Threading Building Blocks is distributed in the hope that it will be
  8. useful, but WITHOUT ANY WARRANTY; without even the implied warranty
  9. of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU General Public License for more details.
  11. You should have received a copy of the GNU General Public License
  12. along with Threading Building Blocks; if not, write to the Free Software
  13. Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
  14. As a special exception, you may use this file as part of a free software
  15. library without restriction. Specifically, if other files instantiate
  16. templates or use macros or inline functions from this file, or you compile
  17. this file and link it with other files to produce an executable, this
  18. file does not by itself cause the resulting executable to be covered by
  19. the GNU General Public License. This exception does not however
  20. invalidate any other reasons why the executable file might be covered by
  21. the GNU General Public License.
  22. */
  23. #ifndef __TBB__aggregator_H
  24. #define __TBB__aggregator_H
  25. #if !TBB_PREVIEW_AGGREGATOR
  26. #error Set TBB_PREVIEW_AGGREGATOR before including aggregator.h
  27. #endif
  28. #include "atomic.h"
  29. #include "tbb_profiling.h"
  30. namespace tbb {
  31. namespace interface6 {
  32. using namespace tbb::internal;
  33. class aggregator_operation {
  34. template<typename handler_type> friend class aggregator_ext;
  35. uintptr_t status;
  36. aggregator_operation* my_next;
  37. public:
  38. enum aggregator_operation_status { agg_waiting=0, agg_finished };
  39. aggregator_operation() : status(agg_waiting), my_next(NULL) {}
  40. /// Call start before handling this operation
  41. void start() { call_itt_notify(acquired, &status); }
  42. /// Call finish when done handling this operation
  43. /** The operation will be released to its originating thread, and possibly deleted. */
  44. void finish() { itt_store_word_with_release(status, uintptr_t(agg_finished)); }
  45. aggregator_operation* next() { return itt_hide_load_word(my_next);}
  46. void set_next(aggregator_operation* n) { itt_hide_store_word(my_next, n); }
  47. };
  48. namespace internal {
  49. class basic_operation_base : public aggregator_operation {
  50. friend class basic_handler;
  51. virtual void apply_body() = 0;
  52. public:
  53. basic_operation_base() : aggregator_operation() {}
  54. virtual ~basic_operation_base() {}
  55. };
  56. template<typename Body>
  57. class basic_operation : public basic_operation_base, no_assign {
  58. const Body& my_body;
  59. /*override*/ void apply_body() { my_body(); }
  60. public:
  61. basic_operation(const Body& b) : basic_operation_base(), my_body(b) {}
  62. };
  63. class basic_handler {
  64. public:
  65. basic_handler() {}
  66. void operator()(aggregator_operation* op_list) const {
  67. while (op_list) {
  68. // ITT note: &(op_list->status) tag is used to cover accesses to the operation data.
  69. // The executing thread "acquires" the tag (see start()) and then performs
  70. // the associated operation w/o triggering a race condition diagnostics.
  71. // A thread that created the operation is waiting for its status (see execute_impl()),
  72. // so when this thread is done with the operation, it will "release" the tag
  73. // and update the status (see finish()) to give control back to the waiting thread.
  74. basic_operation_base& request = static_cast<basic_operation_base&>(*op_list);
  75. // IMPORTANT: need to advance op_list to op_list->next() before calling request.finish()
  76. op_list = op_list->next();
  77. request.start();
  78. request.apply_body();
  79. request.finish();
  80. }
  81. }
  82. };
  83. } // namespace internal
  84. //! Aggregator base class and expert interface
  85. /** An aggregator for collecting operations coming from multiple sources and executing
  86. them serially on a single thread. */
  87. template <typename handler_type>
  88. class aggregator_ext : tbb::internal::no_copy {
  89. public:
  90. aggregator_ext(const handler_type& h) : handler_busy(0), handle_operations(h) { mailbox = NULL; }
  91. //! EXPERT INTERFACE: Enter a user-made operation into the aggregator's mailbox.
  92. /** Details of user-made operations must be handled by user-provided handler */
  93. void process(aggregator_operation *op) { execute_impl(*op); }
  94. protected:
  95. /** Place operation in mailbox, then either handle mailbox or wait for the operation
  96. to be completed by a different thread. */
  97. void execute_impl(aggregator_operation& op) {
  98. aggregator_operation* res;
  99. // ITT note: &(op.status) tag is used to cover accesses to this operation. This
  100. // thread has created the operation, and now releases it so that the handler
  101. // thread may handle the associated operation w/o triggering a race condition;
  102. // thus this tag will be acquired just before the operation is handled in the
  103. // handle_operations functor.
  104. call_itt_notify(releasing, &(op.status));
  105. // insert the operation in the queue
  106. do {
  107. // ITT may flag the following line as a race; it is a false positive:
  108. // This is an atomic read; we don't provide itt_hide_load_word for atomics
  109. op.my_next = res = mailbox; // NOT A RACE
  110. } while (mailbox.compare_and_swap(&op, res) != res);
  111. if (!res) { // first in the list; handle the operations
  112. // ITT note: &mailbox tag covers access to the handler_busy flag, which this
  113. // waiting handler thread will try to set before entering handle_operations.
  114. call_itt_notify(acquired, &mailbox);
  115. start_handle_operations();
  116. __TBB_ASSERT(op.status, NULL);
  117. }
  118. else { // not first; wait for op to be ready
  119. call_itt_notify(prepare, &(op.status));
  120. spin_wait_while_eq(op.status, uintptr_t(aggregator_operation::agg_waiting));
  121. itt_load_word_with_acquire(op.status);
  122. }
  123. }
  124. private:
  125. //! An atomically updated list (aka mailbox) of aggregator_operations
  126. atomic<aggregator_operation *> mailbox;
  127. //! Controls thread access to handle_operations
  128. /** Behaves as boolean flag where 0=false, 1=true */
  129. uintptr_t handler_busy;
  130. handler_type handle_operations;
  131. //! Trigger the handling of operations when the handler is free
  132. void start_handle_operations() {
  133. aggregator_operation *pending_operations;
  134. // ITT note: &handler_busy tag covers access to mailbox as it is passed
  135. // between active and waiting handlers. Below, the waiting handler waits until
  136. // the active handler releases, and the waiting handler acquires &handler_busy as
  137. // it becomes the active_handler. The release point is at the end of this
  138. // function, when all operations in mailbox have been handled by the
  139. // owner of this aggregator.
  140. call_itt_notify(prepare, &handler_busy);
  141. // get handler_busy: only one thread can possibly spin here at a time
  142. spin_wait_until_eq(handler_busy, uintptr_t(0));
  143. call_itt_notify(acquired, &handler_busy);
  144. // acquire fence not necessary here due to causality rule and surrounding atomics
  145. __TBB_store_with_release(handler_busy, uintptr_t(1));
  146. // ITT note: &mailbox tag covers access to the handler_busy flag itself.
  147. // Capturing the state of the mailbox signifies that handler_busy has been
  148. // set and a new active handler will now process that list's operations.
  149. call_itt_notify(releasing, &mailbox);
  150. // grab pending_operations
  151. pending_operations = mailbox.fetch_and_store(NULL);
  152. // handle all the operations
  153. handle_operations(pending_operations);
  154. // release the handler
  155. itt_store_word_with_release(handler_busy, uintptr_t(0));
  156. }
  157. };
  158. //! Basic aggregator interface
  159. class aggregator : private aggregator_ext<internal::basic_handler> {
  160. public:
  161. aggregator() : aggregator_ext<internal::basic_handler>(internal::basic_handler()) {}
  162. //! BASIC INTERFACE: Enter a function for exclusvie execution by the aggregator.
  163. /** The calling thread stores the function object in a basic_operation and
  164. places the operation in the aggregator's mailbox */
  165. template<typename Body>
  166. void execute(const Body& b) {
  167. internal::basic_operation<Body> op(b);
  168. this->execute_impl(op);
  169. }
  170. };
  171. } // namespace interface6
  172. using interface6::aggregator;
  173. using interface6::aggregator_ext;
  174. using interface6::aggregator_operation;
  175. } // namespace tbb
  176. #endif // __TBB__aggregator_H