Repository: uxlfoundation/oneTBB (C++)

Issue:
flow_graph: Assertion m_private_counter >= 0 failed
# Summary
When running TBB flow_graph I sporadically encounter  `Assertion m_private_counter >= 0 failed (located in the destroy function, line in file: 141)` on destruction. 
This is a similiar to [Issue-639](https://github.com/uxlfoundation/oneTBB/issues/639), only that I encounter it in succeeding TBB versions. 

One thing that I observed is that the failure does not occur when setting `tbb::flow::node_priority_t{0}`. 
This is not required in this minimal example, but needed in our implementation as we have other nodes with `tbb::flow::node_priority_t{0}`. 

# Version
Able to reproduce with `TBB 2022.0.0`
# Environment
* OS Ubuntu 22.04.4 LTS
* Compiler: gcc 11.4.0 | clang 17

# Steps To Reproduce
```cpp
#include <iostream>
#include <cassert>
#include <tbb/flow_graph.h>

int main() {
    struct Message {
        int idx;
        [[maybe_unused]] char pad[1] = {0};
    };
    using NODE = tbb::flow::function_node<Message, tbb::flow::continue_msg>;
    int num_iterations = 10000;
    int node_activations = 0;

    tbb::flow::graph graph;
    NODE node{graph, tbb::flow::serial, [&]([[maybe_unused]] const auto& msg){ node_activations++; },
              tbb::flow::queueing{}, tbb::flow::node_priority_t{1}};

    for (int i{0}; i < num_iterations; i++) {
        node.try_put(Message{i});
    }

    graph.wait_for_all();
    assert (num_iterations == node_activations);
    return 0;
}
```


Discussion: @martinr0x, thanks for reporting the issue.
I was able to reproduce it on my side and will investigate this.
Great, thank you.



Fix (patch):
diff --git a/include/oneapi/tbb/detail/_flow_graph_impl.h b/include/oneapi/tbb/detail/_flow_graph_impl.h
index 55063b93e1..a5408adf4a 100644
--- a/include/oneapi/tbb/detail/_flow_graph_impl.h
+++ b/include/oneapi/tbb/detail/_flow_graph_impl.h
@@ -1,5 +1,5 @@
 /*
-    Copyright (c) 2005-2024 Intel Corporation
+    Copyright (c) 2005-2025 Intel Corporation
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -526,7 +526,10 @@ inline graph_task* prioritize_task(graph& g, graph_task& gt) {
     //! priority queue, and a new critical task is created to take and execute a work item with
     //! the highest known priority. The reference counting responsibility is transferred to
     //! the new task.
-    d1::task* critical_task = gt.my_allocator.new_object<priority_task_selector>(g.my_priority_queue, gt.my_allocator);
+    // A newly created small_object_allocator should be used to allocate the priority_task_selector
+    // instead of the allocator, associated with gt since gt can be allocated by another thread
+    d1::small_object_allocator allocator;
+    d1::task* critical_task = allocator.new_object<priority_task_selector>(g.my_priority_queue, allocator);
     __TBB_ASSERT( critical_task, "bad_alloc?" );
     g.my_priority_queue.push(&gt);
     using tbb::detail::d1::submit;
diff --git a/src/tbb/small_object_pool.cpp b/src/tbb/small_object_pool.cpp
index 28d11d011d..c7926e8afd 100644
--- a/src/tbb/small_object_pool.cpp
+++ b/src/tbb/small_object_pool.cpp
@@ -1,5 +1,5 @@
 /*
-    Copyright (c) 2020-2021 Intel Corporation
+    Copyright (c) 2020-2025 Intel Corporation
 
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
@@ -45,6 +45,8 @@ void* __TBB_EXPORTED_FUNC allocate(d1::small_object_pool*& allocator, std::size_
 
 void* small_object_pool_impl::allocate_impl(d1::small_object_pool*& allocator, std::size_t number_of_bytes)
 {
+    __TBB_ASSERT(allocator == nullptr || allocator == this,
+                 "An attempt was made to allocate using another thread's small memory pool");
     small_object* obj{nullptr};
 
     if (number_of_bytes <= small_object_size) {