KiCad PCB EDA Suite
Loading...
Searching...
No Matches
priority_thread_pool_task.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#include <atomic>
23#include <exception>
24
25#include <progress_reporter.h>
26#include <thread_pool.h>
27
28
32template <typename ContainerT>
34{
35public:
36 using ItemT = typename ContainerT::value_type;
37
39 m_reporter( nullptr ),
40 m_highestPriority( BS::pr::high ),
41 m_reporterInterval( std::chrono::milliseconds( 250 ) )
42 {
43 }
44
45 // No copying (m_abort is atomic)
48
49 void SetReporter( PROGRESS_REPORTER* aReporter ) { m_reporter = aReporter; }
50
66 size_t Execute( ContainerT& aItems )
67 {
69 std::vector<std::future<size_t>> returns;
70
71 // Clear the abort flag in case this object is reused after a failure.
72 m_abort.store( false );
73
74 // Compute priority keys paired to item indices
75 using IndexedPriority = std::pair<size_t, int>;
76 std::vector<IndexedPriority> indexedKeys( aItems.size() );
77 for( size_t i = 0; i < aItems.size(); ++i )
78 {
79 indexedKeys[i] = { i, computePriorityKey( aItems[i] ) };
80 }
81
82 // Sort by descending priority key
83 std::sort( indexedKeys.begin(), indexedKeys.end(),
84 []( const IndexedPriority& a, const IndexedPriority& b )
85 {
86 return a.second > b.second;
87 } );
88
89 // Dispatch largest first
90 const size_t numItems = aItems.size();
91 for( size_t priorityRank = 0; priorityRank < numItems; ++priorityRank )
92 {
93 const size_t itemIndex = indexedKeys[priorityRank].first;
94 ItemT& item = aItems[itemIndex];
95
96 // Earlier ranking -> higher key -> should be higher priority
97 const size_t priority = ( ( numItems - priorityRank - 1 ) * m_highestPriority ) / numItems;
98
99 returns.emplace_back( tp.submit_task(
100 [this, &item]
101 {
102 // Abandon this item if an earlier task has failed.
103 if( m_abort.load() )
104 return size_t( 0 );
105
106 try
107 {
108 return task( item );
109 }
110 catch( ... )
111 {
112 // Let tasks that have not started yet skip their item.
113 m_abort.store( true );
114 throw;
115 }
116 },
117 priority ) );
118 }
119
120 size_t totalProcessed = 0;
121
122 // Hold onto the first exception (if any) thrown by the tasks and rethrow at the end.
123 std::exception_ptr firstException;
124
125 for( std::future<size_t>& ret : returns )
126 {
127 std::future_status status = ret.wait_for( m_reporterInterval );
128
129 while( status != std::future_status::ready )
130 {
131 if( m_reporter )
133
134 status = ret.wait_for( m_reporterInterval );
135 }
136
137 try
138 {
139 totalProcessed += ret.get();
140 }
141 catch( ... )
142 {
143 if( !firstException )
144 firstException = std::current_exception();
145 }
146 }
147
148 // Assert
149
150 if( firstException )
151 std::rethrow_exception( firstException );
152
153 return totalProcessed;
154 }
155
156protected:
158
159private:
174 virtual int computePriorityKey( const ItemT& aItem ) const = 0;
175
179 virtual size_t task( ItemT& item ) = 0;
180
181 BS::priority_t m_highestPriority;
182 std::chrono::milliseconds m_reporterInterval;
183
187 std::atomic<bool> m_abort{ false };
188};
typename ContainerT::value_type ItemT
std::chrono::milliseconds m_reporterInterval
virtual size_t task(ItemT &item)=0
Process one item in the thread pool.
PRIORITY_THREAD_POOL_TASK(const PRIORITY_THREAD_POOL_TASK &)=delete
size_t Execute(ContainerT &aItems)
Call this to execute the task on all items in aItems, using the thread pool and dispatching the tasks...
std::atomic< bool > m_abort
Set when a task throws, so tasks that have not started yet skip their item.
void SetReporter(PROGRESS_REPORTER *aReporter)
PRIORITY_THREAD_POOL_TASK & operator=(const PRIORITY_THREAD_POOL_TASK &)=delete
virtual int computePriorityKey(const ItemT &aItem) const =0
Implement this to compute a priority key for an item.
A progress reporter interface for use in multi-threaded environments.
virtual bool KeepRefreshing(bool aWait=false)=0
Update the UI (if any).
Definition singleton.h:27
STL namespace.
thread_pool & GetKiCadThreadPool()
Get a reference to the current thread pool.
static thread_pool * tp
BS::priority_thread_pool thread_pool
Definition thread_pool.h:27