@@ -26,6 +26,7 @@ public class Main : ISettingProvider, IPlugin, IAsyncReloadable, IPluginI18n, IC
26
26
private readonly CancellationTokenSource _cancellationTokenSource = new ( ) ;
27
27
private PeriodicTimer ? _firefoxBookmarkTimer ;
28
28
private static readonly TimeSpan FirefoxPollingInterval = TimeSpan . FromHours ( 3 ) ;
29
+ private readonly SemaphoreSlim _reloadGate = new ( 1 , 1 ) ;
29
30
30
31
public void Init ( PluginInitContext context )
31
32
{
@@ -106,15 +107,23 @@ public List<Result> Query(Query query)
106
107
107
108
public async Task ReloadDataAsync ( )
108
109
{
109
- var ( bookmarks , discoveredFiles ) = await _bookmarkLoader . LoadBookmarksAsync ( _cancellationTokenSource . Token ) ;
110
+ await _reloadGate . WaitAsync ( _cancellationTokenSource . Token ) ;
111
+ try
112
+ {
113
+ var ( bookmarks , discoveredFiles ) = await _bookmarkLoader . LoadBookmarksAsync ( _cancellationTokenSource . Token ) ;
110
114
111
- // Atomically swap the list. This prevents the Query method from seeing a partially loaded list.
112
- Volatile . Write ( ref _bookmarks , bookmarks ) ;
115
+ // Atomically swap the list. This prevents the Query method from seeing a partially loaded list.
116
+ Volatile . Write ( ref _bookmarks , bookmarks ) ;
113
117
114
- _bookmarkWatcher . UpdateWatchers ( discoveredFiles ) ;
118
+ _bookmarkWatcher . UpdateWatchers ( discoveredFiles ) ;
115
119
116
- // Fire and forget favicon processing to not block the UI
117
- _ = _faviconService . ProcessBookmarkFavicons ( _bookmarks , _cancellationTokenSource . Token ) ;
120
+ // Fire and forget favicon processing to not block the UI
121
+ _ = _faviconService . ProcessBookmarkFavicons ( _bookmarks , _cancellationTokenSource . Token ) ;
122
+ }
123
+ finally
124
+ {
125
+ _reloadGate . Release ( ) ;
126
+ }
118
127
}
119
128
120
129
private void OnSettingsPropertyChanged ( object ? sender , PropertyChangedEventArgs e )
@@ -157,60 +166,69 @@ private void StartFirefoxBookmarkTimer()
157
166
158
167
private async Task ReloadFirefoxBookmarksAsync ( )
159
168
{
160
- Context . API . LogInfo ( nameof ( Main ) , "Starting periodic reload of Firefox bookmarks." ) ;
161
-
162
- var firefoxLoaders = _bookmarkLoader . GetFirefoxBookmarkLoaders ( ) . ToList ( ) ;
163
- if ( ! firefoxLoaders . Any ( ) )
169
+ // Share the same gate to avoid conflicting with full reloads
170
+ await _reloadGate . WaitAsync ( _cancellationTokenSource . Token ) ;
171
+ try
164
172
{
165
- Context . API . LogInfo ( nameof ( Main ) , "No Firefox bookmark loaders enabled, skipping reload." ) ;
166
- return ;
167
- }
173
+ Context . API . LogInfo ( nameof ( Main ) , "Starting periodic reload of Firefox bookmarks." ) ;
168
174
169
- var tasks = firefoxLoaders . Select ( async loader =>
170
- {
171
- var loadedBookmarks = new List < Bookmark > ( ) ;
172
- try
173
- {
174
- await foreach ( var bookmark in loader . GetBookmarksAsync ( _cancellationTokenSource . Token ) )
175
- {
176
- loadedBookmarks . Add ( bookmark ) ;
177
- }
178
- return ( Loader : loader , Bookmarks : loadedBookmarks , Success : true ) ;
179
- }
180
- catch ( OperationCanceledException )
175
+ var firefoxLoaders = _bookmarkLoader . GetFirefoxBookmarkLoaders ( ) . ToList ( ) ;
176
+ if ( ! firefoxLoaders . Any ( ) )
181
177
{
182
- return ( Loader : loader , Bookmarks : new List < Bookmark > ( ) , Success : false ) ;
178
+ Context . API . LogInfo ( nameof ( Main ) , "No Firefox bookmark loaders enabled, skipping reload." ) ;
179
+ return ;
183
180
}
184
- catch ( Exception e )
181
+
182
+ var tasks = firefoxLoaders . Select ( async loader =>
185
183
{
186
- Context . API . LogException ( nameof ( Main ) , $ "Failed to load bookmarks from { loader . Name } .", e ) ;
187
- return ( Loader : loader , Bookmarks : new List < Bookmark > ( ) , Success : false ) ;
188
- }
189
- } ) ;
184
+ var loadedBookmarks = new List < Bookmark > ( ) ;
185
+ try
186
+ {
187
+ await foreach ( var bookmark in loader . GetBookmarksAsync ( _cancellationTokenSource . Token ) )
188
+ {
189
+ loadedBookmarks . Add ( bookmark ) ;
190
+ }
191
+ return ( Loader : loader , Bookmarks : loadedBookmarks , Success : true ) ;
192
+ }
193
+ catch ( OperationCanceledException )
194
+ {
195
+ return ( Loader : loader , Bookmarks : new List < Bookmark > ( ) , Success : false ) ;
196
+ }
197
+ catch ( Exception e )
198
+ {
199
+ Context . API . LogException ( nameof ( Main ) , $ "Failed to load bookmarks from { loader . Name } .", e ) ;
200
+ return ( Loader : loader , Bookmarks : new List < Bookmark > ( ) , Success : false ) ;
201
+ }
202
+ } ) ;
190
203
191
- var results = await Task . WhenAll ( tasks ) ;
192
- var successfulResults = results . Where ( r => r . Success ) . ToList ( ) ;
204
+ var results = await Task . WhenAll ( tasks ) ;
205
+ var successfulResults = results . Where ( r => r . Success ) . ToList ( ) ;
193
206
194
- if ( ! successfulResults . Any ( ) )
195
- {
196
- Context . API . LogInfo ( nameof ( Main ) , "No Firefox bookmarks successfully reloaded." ) ;
197
- return ;
198
- }
207
+ if ( ! successfulResults . Any ( ) )
208
+ {
209
+ Context . API . LogInfo ( nameof ( Main ) , "No Firefox bookmarks successfully reloaded." ) ;
210
+ return ;
211
+ }
199
212
200
- var newFirefoxBookmarks = successfulResults . SelectMany ( r => r . Bookmarks ) . ToList ( ) ;
201
- var successfulLoaderNames = successfulResults . Select ( r => r . Loader . Name ) . ToHashSet ( ) ;
213
+ var newFirefoxBookmarks = successfulResults . SelectMany ( r => r . Bookmarks ) . ToList ( ) ;
214
+ var successfulLoaderNames = successfulResults . Select ( r => r . Loader . Name ) . ToHashSet ( ) ;
202
215
203
- var currentBookmarks = Volatile . Read ( ref _bookmarks ) ;
216
+ var currentBookmarks = Volatile . Read ( ref _bookmarks ) ;
204
217
205
- var otherBookmarks = currentBookmarks . Where ( b => ! successfulLoaderNames . Any ( name => b . Source . StartsWith ( name , StringComparison . OrdinalIgnoreCase ) ) ) ;
218
+ var otherBookmarks = currentBookmarks . Where ( b => ! successfulLoaderNames . Any ( name => b . Source . StartsWith ( name , StringComparison . OrdinalIgnoreCase ) ) ) ;
206
219
207
- var newBookmarkList = otherBookmarks . Concat ( newFirefoxBookmarks ) . Distinct ( ) . ToList ( ) ;
220
+ var newBookmarkList = otherBookmarks . Concat ( newFirefoxBookmarks ) . Distinct ( ) . ToList ( ) ;
208
221
209
- Volatile . Write ( ref _bookmarks , newBookmarkList ) ;
222
+ Volatile . Write ( ref _bookmarks , newBookmarkList ) ;
210
223
211
- Context . API . LogInfo ( nameof ( Main ) , $ "Periodic reload complete. Loaded { newFirefoxBookmarks . Count } Firefox bookmarks from { successfulLoaderNames . Count } sources.") ;
224
+ Context . API . LogInfo ( nameof ( Main ) , $ "Periodic reload complete. Loaded { newFirefoxBookmarks . Count } Firefox bookmarks from { successfulLoaderNames . Count } sources.") ;
212
225
213
- _ = _faviconService . ProcessBookmarkFavicons ( newFirefoxBookmarks , _cancellationTokenSource . Token ) ;
226
+ _ = _faviconService . ProcessBookmarkFavicons ( newFirefoxBookmarks , _cancellationTokenSource . Token ) ;
227
+ }
228
+ finally
229
+ {
230
+ _reloadGate . Release ( ) ;
231
+ }
214
232
}
215
233
216
234
public Control CreateSettingPanel ( )
0 commit comments