| 138 | //------------------------------------------------------------------------ |
| 139 | |
| 140 | task_group_context::~task_group_context () { |
| 141 | if ( __TBB_load_relaxed(my_kind) == binding_completed ) { |
| 142 | if ( governor::is_set(my_owner) ) { |
| 143 | // Local update of the context list |
| 144 | uintptr_t local_count_snapshot = my_owner->my_context_state_propagation_epoch; |
| 145 | my_owner->my_local_ctx_list_update.store<relaxed>(1); |
| 146 | // Prevent load of nonlocal update flag from being hoisted before the |
| 147 | // store to local update flag. |
| 148 | atomic_fence(); |
| 149 | if ( my_owner->my_nonlocal_ctx_list_update.load<relaxed>() ) { |
| 150 | spin_mutex::scoped_lock lock(my_owner->my_context_list_mutex); |
| 151 | my_node.my_prev->my_next = my_node.my_next; |
| 152 | my_node.my_next->my_prev = my_node.my_prev; |
| 153 | my_owner->my_local_ctx_list_update.store<relaxed>(0); |
| 154 | } |
| 155 | else { |
| 156 | my_node.my_prev->my_next = my_node.my_next; |
| 157 | my_node.my_next->my_prev = my_node.my_prev; |
| 158 | // Release fence is necessary so that update of our neighbors in |
| 159 | // the context list was committed when possible concurrent destroyer |
| 160 | // proceeds after local update flag is reset by the following store. |
| 161 | my_owner->my_local_ctx_list_update.store<release>(0); |
| 162 | if ( local_count_snapshot != the_context_state_propagation_epoch ) { |
| 163 | // Another thread was propagating cancellation request when we removed |
| 164 | // ourselves from the list. We must ensure that it is not accessing us |
| 165 | // when this destructor finishes. We'll be able to acquire the lock |
| 166 | // below only after the other thread finishes with us. |
| 167 | spin_mutex::scoped_lock lock(my_owner->my_context_list_mutex); |
| 168 | } |
| 169 | } |
| 170 | } |
| 171 | else { |
| 172 | // Nonlocal update of the context list |
| 173 | // Synchronizes with generic_scheduler::cleanup_local_context_list() |
| 174 | // TODO: evaluate and perhaps relax, or add some lock instead |
| 175 | if ( internal::as_atomic(my_kind).fetch_and_store(dying) == detached ) { |
| 176 | my_node.my_prev->my_next = my_node.my_next; |
| 177 | my_node.my_next->my_prev = my_node.my_prev; |
| 178 | } |
| 179 | else { |
| 180 | //TODO: evaluate and perhaps relax |
| 181 | my_owner->my_nonlocal_ctx_list_update.fetch_and_increment<full_fence>(); |
| 182 | //TODO: evaluate and perhaps remove |
| 183 | spin_wait_until_eq( my_owner->my_local_ctx_list_update, 0u ); |
| 184 | my_owner->my_context_list_mutex.lock(); |
| 185 | my_node.my_prev->my_next = my_node.my_next; |
| 186 | my_node.my_next->my_prev = my_node.my_prev; |
| 187 | my_owner->my_context_list_mutex.unlock(); |
| 188 | //TODO: evaluate and perhaps relax |
| 189 | my_owner->my_nonlocal_ctx_list_update.fetch_and_decrement<full_fence>(); |
| 190 | } |
| 191 | } |
| 192 | } |
| 193 | #if __TBB_FP_CONTEXT |
| 194 | internal::punned_cast<cpu_ctl_env*>(&my_cpu_ctl_env)->~cpu_ctl_env(); |
| 195 | #endif |
| 196 | poison_value(my_version_and_traits); |
| 197 | if ( my_exception ) |
no test coverage detected