KiCad PCB EDA Suite
Loading...
Searching...
No Matches
db_worker_thread.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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * 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#ifndef KICAD_DB_WORKER_THREAD_H
21#define KICAD_DB_WORKER_THREAD_H
22
23#include <chrono>
24#include <cstddef>
25#include <functional>
26#include <memory>
27#include <optional>
28#include <string>
29#include <thread>
30#include <vector>
31
33
34struct DB_WORKER_CORE;
35struct DB_WORKER_STATE;
36
37
44{
45public:
46 static constexpr size_t BATCH_ROWS = 512;
47 static constexpr size_t MAX_QUEUED_BATCHES = 4;
48
55 bool Emit( std::vector<DB_VALUE>&& aRow );
56
61 bool Flush();
62
64 bool Cancelled() const;
65
66private:
67 friend struct DB_WORKER_CORE;
68
69 explicit DB_JOB_CONTEXT( DB_WORKER_CORE& aCore ) : m_core( aCore ) {}
70
71 bool pushBatch();
72
74 std::vector<std::vector<DB_VALUE>> m_pending;
75};
76
77
93{
94public:
95 static constexpr size_t MAX_ABANDONED = 2;
96
106 DB_WORKER_THREAD( std::string aSourceKey, std::function<bool()> aThreadInit = nullptr,
107 std::function<void()> aThreadExit = nullptr, bool aPumpMessages = false,
108 std::function<void()> aOnAbandon = nullptr );
109
112
122
125
138 template <typename R>
139 std::optional<R> Run( std::function<R( DB_JOB_CONTEXT& )> aJob, std::chrono::milliseconds aBound,
140 const DB_ROW_SINK* aSink )
141 {
142 auto result = std::make_shared<std::optional<R>>();
143
144 auto wrapped = [job = std::move( aJob ), result]( DB_JOB_CONTEXT& aContext )
145 {
146 *result = job( aContext );
147 };
148
149 if( !runJob( std::move( wrapped ), aBound, aSink, false ) )
150 return std::nullopt;
151
152 return std::move( *result );
153 }
154
162 bool RunCleanup( std::function<void()> aCleanup, std::chrono::milliseconds aBound );
163
165 bool IsHealthy() const;
166
168 bool HasLiveThread() const;
169
170 OUTCOME LastOutcome() const { return *m_outcome; }
171
173 static bool OnWorkerThread();
174
176 void SetShutdownBound( std::chrono::milliseconds aBound ) { m_shutdownBound = aBound; }
177
179 static std::string SourceKeyFor( const std::string& aPath );
180
182 static std::string SourceKeyFor( const std::string& aPath, bool aWindowsPath );
183
189 static std::string NormalizePath( const std::string& aPath, bool aWindowsPath );
190
192 static std::chrono::milliseconds CallerBound( int aTimeoutSeconds );
193
194private:
195 bool runJob( std::function<void( DB_JOB_CONTEXT& )> aJob, std::chrono::milliseconds aBound,
196 const DB_ROW_SINK* aSink, bool aCleanup );
197
198 std::string m_key;
200
201 std::chrono::milliseconds m_shutdownBound{ std::chrono::seconds( 30 ) };
202 std::shared_ptr<OUTCOME> m_outcome = std::make_shared<OUTCOME>( OUTCOME::OK );
203
204 // Everything a caller still waiting in admission touches lives here, so destroying the worker cannot free it
205 std::shared_ptr<DB_WORKER_STATE> m_state;
206};
207
208#endif // KICAD_DB_WORKER_THREAD_H
What a job running on a DB_WORKER_THREAD uses to hand rows to the caller.
static constexpr size_t BATCH_ROWS
static constexpr size_t MAX_QUEUED_BATCHES
bool Flush()
Delivers the rows still held back and waits until the caller's sink has taken every one.
DB_JOB_CONTEXT(DB_WORKER_CORE &aCore)
bool Cancelled() const
bool Emit(std::vector< DB_VALUE > &&aRow)
Queues a row for the caller's sink.
friend struct DB_WORKER_CORE
DB_WORKER_CORE & m_core
std::vector< std::vector< DB_VALUE > > m_pending
bool HasLiveThread() const
~DB_WORKER_THREAD()
Waits up to the shutdown bound for a running job, then abandons it.
static constexpr size_t MAX_ABANDONED
std::chrono::milliseconds m_shutdownBound
bool runJob(std::function< void(DB_JOB_CONTEXT &)> aJob, std::chrono::milliseconds aBound, const DB_ROW_SINK *aSink, bool aCleanup)
OUTCOME
Why the last Run or RunCleanup returned without a result.
@ UNAVAILABLE
the worker thread could not start
@ REFUSED
two abandoned jobs hold the source, or no slot freed within the bound
@ TOO_LATE
a slot freed with under half the bound left, so nothing was started
@ TIMED_OUT
no progress within the bound, so the job was abandoned
OUTCOME LastOutcome() const
static std::chrono::milliseconds CallerBound(int aTimeoutSeconds)
std::shared_ptr< OUTCOME > m_outcome
void SetShutdownBound(std::chrono::milliseconds aBound)
Sets how long the destructor waits for a running job before abandoning it.
bool RunCleanup(std::function< void()> aCleanup, std::chrono::milliseconds aBound)
Runs aCleanup on a worker thread for work that must not happen on the caller, such as closing a conne...
DB_WORKER_THREAD(const DB_WORKER_THREAD &)=delete
static std::string SourceKeyFor(const std::string &aPath)
std::shared_ptr< DB_WORKER_STATE > m_state
std::optional< R > Run(std::function< R(DB_JOB_CONTEXT &)> aJob, std::chrono::milliseconds aBound, const DB_ROW_SINK *aSink)
Runs aJob on the worker thread and waits for it.
DB_WORKER_THREAD & operator=(const DB_WORKER_THREAD &)=delete
static std::string NormalizePath(const std::string &aPath, bool aWindowsPath)
Rewrites the spellings of one file that Windows treats alike into one form, without touching the file...
DB_WORKER_THREAD(std::string aSourceKey, std::function< bool()> aThreadInit=nullptr, std::function< void()> aThreadExit=nullptr, bool aPumpMessages=false, std::function< void()> aOnAbandon=nullptr)
static bool OnWorkerThread()
std::function< void(std::vector< DB_VALUE > &&)> DB_ROW_SINK
Receives one row at a time, in the order of the labels the read produced.
State shared by the owner and the worker thread; the thread keeps it alive after the owner is gone.
The part of a worker that outlives its owner while a caller is still inside Run.
wxString result
Test unit parsing edge cases and error handling.