perf: add batch variant of tr_torrentStat() (#8100)

* refactor: add [Torrent updateTorrents]

refactor: use updateTorrents in Controller

* refactor: add a batch variant of tr_torrentStat()

* refactor: use batch variant of tr_torrentStat() in GTK details dialog

* refactor: use batch variant of tr_torrentStat() in gtr_confirm_remove()

* refactor: use batch variant of tr_torrentStat() in updateTorrents

* refactor: add Session::find_torrents()

* refactor: remove the raw ptr variant of updateTorrents()

* refactor: remove tr_sessionLock()

* fixup! refactor: add [Torrent updateTorrents]

remove duplicate method declaration

* fix: readability-avoid-const-params-in-decls

* fix: iwyu in transmission.h

* chore: remove an #include that was added in a draft that did not get used

* refactor: use nullptr instead of NULL
This commit is contained in:
Charles Kerr
2026-01-12 16:23:06 -06:00
committed by GitHub
parent a89ca4f2c9
commit 4a05c06ce0
11 changed files with 126 additions and 61 deletions

View File

@@ -2367,20 +2367,16 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
BOOL anyCompleted = NO;
BOOL anyActive = NO;
[Torrent updateTorrents:self.fTorrents];
for (Torrent* torrent in self.fTorrents)
{
// avoid having to wait for the same lock multiple times in the same operation
auto const lock = tr_sessionLock(self.sessionHandle);
for (Torrent* torrent in self.fTorrents)
{
[torrent update];
//pull the upload and download speeds - most consistent by using current stats
dlRate += torrent.downloadRate;
ulRate += torrent.uploadRate;
//pull the upload and download speeds - most consistent by using current stats
dlRate += torrent.downloadRate;
ulRate += torrent.uploadRate;
anyCompleted |= torrent.finishedSeeding;
anyActive |= torrent.active && !torrent.stalled && !torrent.error;
}
anyCompleted |= torrent.finishedSeeding;
anyActive |= torrent.active && !torrent.stalled && !torrent.error;
}
PowerManager.shared.shouldPreventSleep = anyActive && [self.fDefaults boolForKey:@"SleepPrevent"];
@@ -3817,9 +3813,10 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
for (Torrent* torrent in self.fTorrents)
{
torrent.queuePosition = i++;
[torrent update];
}
[Torrent updateTorrents:self.fTorrents];
//do the drag animation here so that the dragged torrents are the ones that are animated as moving, and not the torrents around them
[self.fTableView beginUpdates];
@@ -5527,10 +5524,7 @@ void onTorrentCompletenessChanged(tr_torrent* tor, tr_completeness status, bool
- (void)rpcUpdateQueue
{
for (Torrent* torrent in self.fTorrents)
{
[torrent update];
}
[Torrent updateTorrents:self.fTorrents];
NSSortDescriptor* descriptor = [NSSortDescriptor sortDescriptorWithKey:@"queuePosition" ascending:YES];
NSArray* descriptors = @[ descriptor ];

View File

@@ -35,6 +35,10 @@ extern NSString* const kTorrentDidChangeGroupNotification;
- (void)getAmountFinished:(float*)tab size:(int)size;
@property(nonatomic) NSIndexSet* previousFinishedPieces;
// Updates one or more torrents by refreshing their libtransmission stats.
// Prefer using this batch method when updating many torrents at once.
+ (void)updateTorrents:(NSArray<Torrent*>*)torrents;
- (void)update;
- (void)startTransferIgnoringQueue:(BOOL)ignoreQueue;

View File

@@ -242,19 +242,59 @@ bool trashDataFile(char const* filename, void* /*user_data*/, tr_error* error)
- (void)update
{
//get previous stalled value before update
BOOL const wasTransmitting = self.fStat != NULL && self.transmitting;
[Torrent updateTorrents:@[ self ]];
}
self.fStat = tr_torrentStat(self.fHandle);
//make sure the "active" filter is updated when transmitting changes
if (wasTransmitting != self.transmitting)
+ (void)updateTorrents:(NSArray<Torrent*>*)torrents
{
if (torrents == nil || torrents.count == 0)
{
//posting asynchronously with coalescing to prevent stack overflow on lots of torrents changing state at the same time
[NSNotificationQueue.defaultQueue enqueueNotification:[NSNotification notificationWithName:@"UpdateTorrentsState" object:nil]
postingStyle:NSPostASAP
coalesceMask:NSNotificationCoalescingOnName
forModes:nil];
return;
}
std::vector<Torrent*> torrent_objects;
torrent_objects.reserve(torrents.count);
std::vector<tr_torrent*> torrent_handles;
torrent_handles.reserve(torrents.count);
std::vector<BOOL> was_transmitting;
was_transmitting.reserve(torrents.count);
for (Torrent* torrent in torrents)
{
if (torrent == nil || torrent.fHandle == nullptr)
{
continue;
}
torrent_objects.emplace_back(torrent);
torrent_handles.emplace_back(torrent.fHandle);
was_transmitting.emplace_back(torrent.fStat != nullptr && torrent.transmitting);
}
if (torrent_handles.empty())
{
return;
}
auto const stats = tr_torrentStat(torrent_handles.data(), torrent_handles.size());
// Assign stats and post notifications.
for (size_t i = 0, n = torrent_objects.size(); i < n; ++i)
{
Torrent* const torrent = torrent_objects[i];
torrent.fStat = stats[i];
//make sure the "active" filter is updated when transmitting changes
if (was_transmitting[i] != torrent.transmitting)
{
//posting asynchronously with coalescing to prevent stack overflow on lots of torrents changing state at the same time
[NSNotificationQueue.defaultQueue enqueueNotification:[NSNotification notificationWithName:@"UpdateTorrentsState" object:nil]
postingStyle:NSPostASAP
coalesceMask:NSNotificationCoalescingOnName
forModes:nil];
}
}
}