KiCad PCB EDA Suite
Loading...
Searching...
No Matches
git_transfer.cpp
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 along
17 * with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
22
23#include <exception>
24
25#include <wx/app.h>
26#include <wx/intl.h>
27#include <wx/log.h>
28
29#include <git/git_backend.h>
30
31
32void KIGIT::CallOnUi( std::function<void()> aFn )
33{
34 if( !wxTheApp )
35 return;
36
37 wxTheApp->CallAfter( std::move( aFn ) );
38}
39
40
41void KIGIT::CallIfAlive( const std::weak_ptr<bool>& aWatch, std::function<void()> aFn )
42{
44 [aWatch, fn = std::move( aFn )]()
45 {
46 if( aWatch.lock() )
47 fn();
48 } );
49}
50
51
52bool KIGIT::StartTransfer( const std::string& aLabel, std::function<void()> aWork )
53{
54 GIT_BACKEND* backend = GetGitBackend();
55
56 return backend && backend->OrphanRegistry().Register( aLabel, std::move( aWork ) );
57}
58
59
60KIGIT_STATUS_REPORTER::KIGIT_STATUS_REPORTER( const std::weak_ptr<bool>& aAlive,
61 std::function<void( const wxString& )> aShow ) :
63 m_alive( aAlive ),
64 m_show( std::move( aShow ) )
65{
66}
67
68
70{
71 wxString text;
72
73 {
74 std::lock_guard<std::mutex> lock( m_mutex );
76 }
77
78 if( text == m_lastShown )
79 return true;
80
82
84 [show = m_show, text]()
85 {
86 show( text );
87 } );
88
89 return true;
90}
91
92
94{
95 Cancel();
96 m_alive.reset();
97
98 if( m_completion )
99 *m_completion = {};
100}
101
102
104{
105 if( m_cancel )
106 m_cancel->store( true );
107}
108
109
110bool KIGIT_TRANSFER::Start( const std::string& aLabel,
111 const std::function<void( const KIGIT_COMMON::CANCEL_TOKEN& )>& aWork,
112 const std::function<void()>& aDone )
113{
114 // Made before the thread starts, so a cancel pressed while the task is still queued reaches
115 // this transfer rather than being discarded when it begins.
117
118 std::weak_ptr<bool> alive = m_alive;
120
121 if( m_completion )
122 *m_completion = {};
123
124 auto completion = std::make_shared<std::function<void()>>( aDone );
125 m_completion = completion;
126 m_busy = true;
127
128 bool started = false;
129
130 try
131 {
132 started = KIGIT::StartTransfer( aLabel,
133 [this, alive, token, aWork, completion]()
134 {
135 aWork( token );
136
138 [this, alive, completion]()
139 {
140 // Empty the holder on the UI thread even if its owner
141 // has gone. The worker may release its copy afterwards.
142 std::function<void()> done = std::move( *completion );
143 *completion = {};
144
145 if( alive.lock() && m_completion == completion )
146 {
147 m_completion.reset();
148 m_busy = false;
149
150 if( done )
151 done();
152 }
153 } );
154 } );
155 }
156 catch( ... )
157 {
158 // A failed thread construction must not leave its owner waiting for a completion
159 // which cannot arrive.
160 *completion = {};
161 m_completion.reset();
162 m_busy = false;
163 throw;
164 }
165
166 if( !started )
167 {
168 *completion = {};
169 m_completion.reset();
170 m_busy = false;
171 }
172
173 return started;
174}
175
176
181
182
183void KIGIT_QUERY::Run( std::shared_ptr<KIGIT_COMMON> aSession, std::function<void( KIGIT_COMMON& )> aWork,
184 std::function<void()> aDone )
185{
186 REQUEST request{ std::move( aSession ), std::move( aWork ), std::move( aDone ), ++m_generation };
187
188 if( m_transfer.IsBusy() )
189 {
190 m_pending = std::move( request );
191 m_transfer.Cancel();
192 return;
193 }
194
195 start( std::move( request ) );
196}
197
198
200{
201 ++m_generation;
202 m_pending.reset();
203 m_transfer.Cancel();
204}
205
206
208{
209 auto error = std::make_shared<std::exception_ptr>();
210
211 auto done = [this, error, generation = aRequest.generation, complete = std::move( aRequest.done )]()
212 {
213 if( m_pending )
214 {
215 REQUEST next = std::move( *m_pending );
216 m_pending.reset();
217 start( std::move( next ) );
218 return;
219 }
220
221 if( generation != m_generation )
222 return;
223
224 // The completion may destroy this query along with its window. Do not touch members
225 // after calling it. Logging is also left to the UI thread, where the target can show it.
226 if( complete )
227 complete();
228
229 if( *error )
230 {
231 try
232 {
233 std::rethrow_exception( *error );
234 }
235 catch( const std::exception& e )
236 {
237 wxLogError( _( "Could not read the Git repository: %s" ), wxString::FromUTF8( e.what() ) );
238 }
239 catch( ... )
240 {
241 wxLogError( _( "Could not read the Git repository." ) );
242 }
243 }
244 };
245
246 bool started = false;
247
248 try
249 {
250 started = m_transfer.Start(
251 "read repository",
252 [error, session = std::move( aRequest.session ),
253 work = std::move( aRequest.work )]( const KIGIT_COMMON::CANCEL_TOKEN& aCancel )
254 {
255 try
256 {
257 if( !session || aCancel->load() || session->IsShuttingDown() )
258 return;
259
260 std::unique_lock<std::mutex> lock = session->LockAction();
261
262 if( aCancel->load() || session->IsShuttingDown() || !session->GetRepo() )
263 return;
264
265 KIGIT_COMMON::OPERATION_GUARD operation( session.get(), aCancel );
266
267 if( !aCancel->load() && work )
268 work( *session );
269 }
270 catch( ... )
271 {
272 // Let the transfer post its completion even when a read throws, or this
273 // query would stay busy and never start the latest pending request.
274 *error = std::current_exception();
275 }
276 },
277 done );
278 }
279 catch( ... )
280 {
281 *error = std::current_exception();
282 }
283
284 if( !started )
285 done();
286}
KIGIT_ORPHAN_REGISTRY & OrphanRegistry()
Return the process-wide orphan thread registry owned by this backend.
Definition git_backend.h:91
The project's git session.
std::shared_ptr< std::atomic< bool > > CANCEL_TOKEN
One token per operation.
static CANCEL_TOKEN NewCancelToken()
bool Register(const std::string &aLabel, F &&aWork)
Spawn a tracked orphan thread running aWork.
uint64_t m_generation
KIGIT_TRANSFER m_transfer
void Cancel()
Suppress the running completion and discard pending work. Does not wait for a read.
std::optional< REQUEST > m_pending
void start(REQUEST aRequest)
void Run(std::shared_ptr< KIGIT_COMMON > aSession, std::function< void(KIGIT_COMMON &)> aWork, std::function< void()> aDone)
Run aWork under the session's action lock on a worker, then aDone on the UI thread.
std::function< void(const wxString &)> m_show
std::weak_ptr< bool > m_alive
KIGIT_STATUS_REPORTER(const std::weak_ptr< bool > &aAlive, std::function< void(const wxString &)> aShow)
bool updateUI() override
KIGIT_COMMON::CANCEL_TOKEN m_cancel
The token of the transfer started last.
std::shared_ptr< std::function< void()> > m_completion
Created and cleared on the UI thread.
std::shared_ptr< bool > m_alive
Dropped when this object goes, so a transfer that outlives it completes into nothing.
void Cancel()
Ask the running transfer to stop. It finishes on its own time.
bool Start(const std::string &aLabel, const std::function< void(const KIGIT_COMMON::CANCEL_TOKEN &)> &aWork, const std::function< void()> &aDone)
Run aWork on a background thread, then aDone on the UI thread.
#define _(s)
GIT_BACKEND * GetGitBackend()
bool StartTransfer(const std::string &aLabel, std::function< void()> aWork)
Run aWork on a thread the orphan registry owns.
void CallIfAlive(const std::weak_ptr< bool > &aWatch, std::function< void()> aFn)
As CallOnUi, for work that touches the window that started the transfer.
void CallOnUi(std::function< void()> aFn)
Run aFn on the UI thread.
STL namespace.
CITER next(CITER it)
Definition ptree.cpp:120
std::function< void()> done
std::function< void(KIGIT_COMMON &)> work
std::shared_ptr< KIGIT_COMMON > session