From 14684c8c83d90c5e478448aac1947b311b9c1d44 Mon Sep 17 00:00:00 2001 From: skomerko <168652295+skomerko@users.noreply.github.com> Date: Sat, 14 Dec 2024 20:31:44 +0100 Subject: [PATCH 1/2] WebUI: Use vanilla JS to create elements All elements are now created using createElement() method: https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement PR #21975. --------- Co-authored-by: Chocobo1 --- src/webui/www/private/rename_files.html | 4 +- src/webui/www/private/scripts/download.js | 2 +- src/webui/www/private/scripts/dynamicTable.js | 195 ++++++++---------- src/webui/www/private/scripts/piecesbar.js | 25 +-- src/webui/www/private/scripts/progressbar.js | 87 ++++---- src/webui/www/private/scripts/prop-files.js | 6 +- src/webui/www/private/scripts/search.js | 42 ++-- 7 files changed, 169 insertions(+), 192 deletions(-) diff --git a/src/webui/www/private/rename_files.html b/src/webui/www/private/rename_files.html index bf907755fd6..588296ed78d 100644 --- a/src/webui/www/private/rename_files.html +++ b/src/webui/www/private/rename_files.html @@ -46,7 +46,7 @@ // Inject checkbox into the first column of the table header const tableHeaders = $$("#bulkRenameFilesTableFixedHeaderDiv .dynamicTableHeader th"); if (tableHeaders.length > 0) { - const checkboxHeader = new Element("input"); + const checkboxHeader = document.createElement("input"); checkboxHeader.type = "checkbox"; checkboxHeader.id = "rootMultiRename_cb"; checkboxHeader.addEventListener("click", (e) => { @@ -56,7 +56,7 @@ }); const checkboxTH = tableHeaders[0]; - checkboxHeader.injectInside(checkboxTH); + checkboxTH.append(checkboxHeader); } // Register keyboard events to modal window diff --git a/src/webui/www/private/scripts/download.js b/src/webui/www/private/scripts/download.js index 491b627a631..1bbbddea5c2 100644 --- a/src/webui/www/private/scripts/download.js +++ b/src/webui/www/private/scripts/download.js @@ -48,7 +48,7 @@ window.qBittorrent.Download ??= (() => { continue; const category = data[i]; - const option = new Element("option"); + const option = document.createElement("option"); option.value = category.name; option.textContent = category.name; $("categorySelect").appendChild(option); diff --git a/src/webui/www/private/scripts/dynamicTable.js b/src/webui/www/private/scripts/dynamicTable.js index 42eb19c8dc0..4e11c881aa1 100644 --- a/src/webui/www/private/scripts/dynamicTable.js +++ b/src/webui/www/private/scripts/dynamicTable.js @@ -433,10 +433,12 @@ window.qBittorrent.DynamicTable ??= (() => { const menuId = this.dynamicTableDivId + "_headerMenu"; // reuse menu if already exists - const ul = $(menuId) ?? new Element("ul", { - id: menuId, - class: "contextMenu scrollableMenu" - }); + let ul = document.getElementById(menuId); + if (ul === null) { + ul = document.createElement("ul"); + ul.id = menuId; + ul.className = "contextMenu scrollableMenu"; + } const createLi = (columnName, text) => { const anchor = document.createElement("a"); @@ -499,7 +501,7 @@ window.qBittorrent.DynamicTable ??= (() => { ul.firstElementChild.classList.add("separator"); ul.insertBefore(autoResizeAllElement, ul.firstElementChild); ul.insertBefore(autoResizeElement, ul.firstElementChild); - ul.inject(document.body); + document.body.append(ul); this.headerContextMenu = new DynamicTableHeaderContextMenuClass({ targets: "#" + this.dynamicTableFixedHeaderDivId + " tr th", @@ -549,8 +551,8 @@ window.qBittorrent.DynamicTable ??= (() => { this.columns.push(column); this.columns[name] = column; - this.hiddenTableHeader.appendChild(new Element("th")); - this.fixedTableHeader.appendChild(new Element("th")); + this.hiddenTableHeader.append(document.createElement("th")); + this.fixedTableHeader.append(document.createElement("th")); }, loadColumnsOrder: function() { @@ -850,7 +852,7 @@ window.qBittorrent.DynamicTable ??= (() => { this.updateRow(trs[rowPos], fullUpdate); } else { // else create a new row in the table - const tr = new Element("tr"); + const tr = document.createElement("tr"); // set tabindex so element receives keydown events // more info: https://developer.mozilla.org/en-US/docs/Web/API/Element/keydown_event tr.tabIndex = -1; @@ -860,10 +862,10 @@ window.qBittorrent.DynamicTable ??= (() => { tr["rowId"] = rowId; for (let k = 0; k < this.columns.length; ++k) { - const td = new Element("td"); + const td = document.createElement("td"); if ((this.columns[k].visible === "0") || this.columns[k].force_hide) td.classList.add("invisible"); - td.injectInside(tr); + tr.append(td); } // Insert @@ -1104,11 +1106,11 @@ window.qBittorrent.DynamicTable ??= (() => { } } else { - td.adopt(new Element("img", { - "src": img_path, - "class": "stateIcon", - "title": state - })); + const img = document.createElement("img"); + img.src = img_path; + img.className = "stateIcon"; + img.title = state; + td.append(img); } }; @@ -1237,7 +1239,7 @@ window.qBittorrent.DynamicTable ??= (() => { else { if (ProgressColumnWidth < 0) ProgressColumnWidth = td.offsetWidth; - td.adopt(new window.qBittorrent.ProgressBar.ProgressBar(progressFormatted.toFloat(), { + td.append(new window.qBittorrent.ProgressBar.ProgressBar(progressFormatted.toFloat(), { "width": ProgressColumnWidth - 5 })); td.resized = false; @@ -2213,13 +2215,11 @@ window.qBittorrent.DynamicTable ??= (() => { const id = row.rowId; const value = this.getRowValue(row); - const treeImg = new Element("img", { - src: "images/L.gif", - styles: { - "margin-bottom": -2 - } - }); - const checkbox = new Element("input"); + const treeImg = document.createElement("img"); + treeImg.src = "images/L.gif"; + treeImg.style.marginBottom = "-2px"; + + const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = "cbRename" + id; checkbox.setAttribute("data-id", id); @@ -2235,7 +2235,7 @@ window.qBittorrent.DynamicTable ??= (() => { checkbox.checked = (value === 0); checkbox.state = checkbox.checked ? "checked" : "unchecked"; checkbox.indeterminate = false; - td.adopt(treeImg, checkbox); + td.replaceChildren(treeImg, checkbox); }; this.columns["checked"].staticWidth = 50; @@ -2253,32 +2253,26 @@ window.qBittorrent.DynamicTable ??= (() => { $(fileNameId).textContent = value; } else { - const span = new Element("span", { - text: value, - id: fileNameId - }); - const dirImg = new Element("img", { - src: "images/directory.svg", - styles: { - "width": 20, - "padding-right": 5, - "margin-bottom": -3, - "margin-left": (node.depth * 20) - }, - id: dirImgId - }); + const span = document.createElement("span"); + span.textContent = value; + span.id = fileNameId; + + const dirImg = document.createElement("img"); + dirImg.src = "images/directory.svg"; + dirImg.style.width = "20px"; + dirImg.style.paddingRight = "5px"; + dirImg.style.marginBottom = "-3px"; + dirImg.style.marginLeft = `${node.depth * 20}px`; + dirImg.id = dirImgId; td.replaceChildren(dirImg, span); } } else { // is file const value = this.getRowValue(row); - const span = new Element("span", { - text: value, - id: fileNameId, - styles: { - "margin-left": ((node.depth + 1) * 20) - } - }); + const span = document.createElement("span"); + span.textContent = value; + span.id = fileNameId; + span.style.marginLeft = `${(node.depth + 1) * 20}px`; td.replaceChildren(span); } }; @@ -2289,10 +2283,9 @@ window.qBittorrent.DynamicTable ??= (() => { const fileNameRenamedId = "filesTablefileRenamed" + id; const value = this.getRowValue(row); - const span = new Element("span", { - text: value, - id: fileNameRenamedId, - }); + const span = document.createElement("span"); + span.textContent = value; + span.id = fileNameRenamedId; td.replaceChildren(span); }; }, @@ -2540,13 +2533,10 @@ window.qBittorrent.DynamicTable ??= (() => { window.qBittorrent.PropFiles.updateDownloadCheckbox(id, value); } else { - const treeImg = new Element("img", { - src: "images/L.gif", - styles: { - "margin-bottom": -2 - } - }); - td.adopt(treeImg, window.qBittorrent.PropFiles.createDownloadCheckbox(id, row.full_data.fileId, value)); + const treeImg = document.createElement("img"); + treeImg.src = "images/L.gif"; + treeImg.style.marginBottom = "-2px"; + td.append(treeImg, window.qBittorrent.PropFiles.createDownloadCheckbox(id, row.full_data.fileId, value)); } }; this.columns["checked"].staticWidth = 50; @@ -2566,41 +2556,34 @@ window.qBittorrent.DynamicTable ??= (() => { $(fileNameId).textContent = value; } else { - const collapseIcon = new Element("img", { - src: "images/go-down.svg", - styles: { - "margin-left": (node.depth * 20) - }, - class: "filesTableCollapseIcon", - id: collapseIconId, - "data-id": id, - onclick: "qBittorrent.PropFiles.collapseIconClicked(this)" - }); - const span = new Element("span", { - text: value, - id: fileNameId - }); - const dirImg = new Element("img", { - src: "images/directory.svg", - styles: { - "width": 20, - "padding-right": 5, - "margin-bottom": -3 - }, - id: dirImgId - }); + const collapseIcon = document.createElement("img"); + collapseIcon.src = "images/go-down.svg"; + collapseIcon.style.marginLeft = `${node.depth * 20}px`; + collapseIcon.className = "filesTableCollapseIcon"; + collapseIcon.id = collapseIconId; + collapseIcon.setAttribute("data-id", id); + collapseIcon.addEventListener("click", function(e) { qBittorrent.PropFiles.collapseIconClicked(this); }); + + const span = document.createElement("span"); + span.textContent = value; + span.id = fileNameId; + + const dirImg = document.createElement("img"); + dirImg.src = "images/directory.svg"; + dirImg.style.width = "20px"; + dirImg.style.paddingRight = "5px"; + dirImg.style.marginBottom = "-3px"; + dirImg.id = dirImgId; + td.replaceChildren(collapseIcon, dirImg, span); } } else { const value = this.getRowValue(row); - const span = new Element("span", { - text: value, - id: fileNameId, - styles: { - "margin-left": ((node.depth + 1) * 20) - } - }); + const span = document.createElement("span"); + span.textContent = value; + span.id = fileNameId; + span.style.marginLeft = `${(node.depth + 1) * 20}px`; td.replaceChildren(span); } }; @@ -2621,7 +2604,7 @@ window.qBittorrent.DynamicTable ??= (() => { const progressBar = $("pbf_" + id); if (progressBar === null) { - td.adopt(new window.qBittorrent.ProgressBar.ProgressBar(value.toFloat(), { + td.append(new window.qBittorrent.ProgressBar.ProgressBar(value.toFloat(), { id: "pbf_" + id, width: 80 })); @@ -2640,7 +2623,7 @@ window.qBittorrent.DynamicTable ??= (() => { if (window.qBittorrent.PropFiles.isPriorityComboExists(id)) window.qBittorrent.PropFiles.updatePriorityCombo(id, value); else - td.adopt(window.qBittorrent.PropFiles.createPriorityCombo(id, row.full_data.fileId, value)); + td.append(window.qBittorrent.PropFiles.createPriorityCombo(id, row.full_data.fileId, value)); }; this.columns["priority"].staticWidth = 140; @@ -2887,12 +2870,12 @@ window.qBittorrent.DynamicTable ??= (() => { } } else { - td.adopt(new Element("img", { - "src": img_path, - "class": "stateIcon", - "height": "22px", - "width": "22px" - })); + const img = document.createElement("img"); + img.src = img_path; + img.className = "stateIcon"; + img.width = "22"; + img.height = "22"; + td.append(img); } }; }, @@ -2929,8 +2912,8 @@ window.qBittorrent.DynamicTable ??= (() => { this.columns.push(column); this.columns[name] = column; - this.hiddenTableHeader.appendChild(new Element("th")); - this.fixedTableHeader.appendChild(new Element("th")); + this.hiddenTableHeader.append(document.createElement("th")); + this.fixedTableHeader.append(document.createElement("th")); } }); @@ -3017,8 +3000,8 @@ window.qBittorrent.DynamicTable ??= (() => { this.columns.push(column); this.columns[name] = column; - this.hiddenTableHeader.appendChild(new Element("th")); - this.fixedTableHeader.appendChild(new Element("th")); + this.hiddenTableHeader.append(document.createElement("th")); + this.fixedTableHeader.append(document.createElement("th")); } }); @@ -3030,7 +3013,7 @@ window.qBittorrent.DynamicTable ??= (() => { this.columns["checked"].updateTd = function(td, row) { if ($("cbRssDlRule" + row.rowId) === null) { - const checkbox = new Element("input"); + const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = "cbRssDlRule" + row.rowId; checkbox.checked = row.full_data.checked; @@ -3101,8 +3084,8 @@ window.qBittorrent.DynamicTable ??= (() => { this.columns.push(column); this.columns[name] = column; - this.hiddenTableHeader.appendChild(new Element("th")); - this.fixedTableHeader.appendChild(new Element("th")); + this.hiddenTableHeader.append(document.createElement("th")); + this.fixedTableHeader.append(document.createElement("th")); }, selectRow: function(rowId) { this.selectedRows.push(rowId); @@ -3128,7 +3111,7 @@ window.qBittorrent.DynamicTable ??= (() => { this.columns["checked"].updateTd = function(td, row) { if ($("cbRssDlFeed" + row.rowId) === null) { - const checkbox = new Element("input"); + const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = "cbRssDlFeed" + row.rowId; checkbox.checked = row.full_data.checked; @@ -3187,8 +3170,8 @@ window.qBittorrent.DynamicTable ??= (() => { this.columns.push(column); this.columns[name] = column; - this.hiddenTableHeader.appendChild(new Element("th")); - this.fixedTableHeader.appendChild(new Element("th")); + this.hiddenTableHeader.append(document.createElement("th")); + this.fixedTableHeader.append(document.createElement("th")); }, selectRow: () => {} }); @@ -3236,8 +3219,8 @@ window.qBittorrent.DynamicTable ??= (() => { this.columns.push(column); this.columns[name] = column; - this.hiddenTableHeader.appendChild(new Element("th")); - this.fixedTableHeader.appendChild(new Element("th")); + this.hiddenTableHeader.append(document.createElement("th")); + this.fixedTableHeader.append(document.createElement("th")); }, selectRow: () => {}, updateRow: function(tr, fullUpdate) { diff --git a/src/webui/www/private/scripts/piecesbar.js b/src/webui/www/private/scripts/piecesbar.js index 2a946957a68..5261013b729 100644 --- a/src/webui/www/private/scripts/piecesbar.js +++ b/src/webui/www/private/scripts/piecesbar.js @@ -61,23 +61,20 @@ window.qBittorrent.PiecesBar ??= (() => { Object.append(vals, parameters); vals.height = Math.max(vals.height, 12); - const obj = new Element("div", { - "id": vals.id, - "class": "piecesbarWrapper", - "styles": { - "border": vals.borderSize.toString() + "px solid " + vals.borderColor, - "height": vals.height.toString() + "px", - } - }); + const obj = document.createElement("div"); + obj.id = vals.id; + obj.className = "piecesbarWrapper"; + obj.style.border = `${vals.borderSize}px solid ${vals.borderColor}`; + obj.style.height = `${vals.height}px`; obj.vals = vals; obj.vals.pieces = [pieces, []].pick(); - obj.vals.canvas = new Element("canvas", { - "id": vals.id + "_canvas", - "class": "piecesbarCanvas", - "width": (vals.width - (2 * vals.borderSize)).toString(), - "height": "1" // will stretch vertically to take up the height of the parent - }); + const canvas = document.createElement("canvas"); + canvas.id = `${vals.id}_canvas`; + canvas.className = "piecesbarCanvas"; + canvas.width = `${vals.width - (2 * vals.borderSize)}`; + canvas.height = "1"; // will stretch vertically to take up the height of the parent + obj.vals.canvas = canvas; obj.appendChild(obj.vals.canvas); obj.setPieces = setPieces; diff --git a/src/webui/www/private/scripts/progressbar.js b/src/webui/www/private/scripts/progressbar.js index b2273307ab2..5b0d9b7fa93 100644 --- a/src/webui/www/private/scripts/progressbar.js +++ b/src/webui/www/private/scripts/progressbar.js @@ -53,52 +53,51 @@ window.qBittorrent.ProgressBar ??= (() => { Object.append(vals, parameters); if (vals.height < 12) vals.height = 12; - const obj = new Element("div", { - "id": vals.id, - "class": "progressbar_wrapper", - "styles": { - "border": "1px solid var(--color-border-default)", - "box-sizing": "content-box", - "width": vals.width, - "height": vals.height, - "position": "relative", - "margin": "0 auto" - } - }); + + const obj = document.createElement("div"); + obj.id = vals.id; + obj.className = "progressbar_wrapper"; + obj.style.border = "1px solid var(--color-border-default)"; + obj.style.boxSizing = "content-box"; + obj.style.width = `${vals.width}px`; + obj.style.height = `${vals.height}px`; + obj.style.position = "relative"; + obj.style.margin = "0 auto"; obj.vals = vals; obj.vals.value = [value, 0].pick(); - obj.vals.dark = new Element("div", { - "id": vals.id + "_dark", - "class": "progressbar_dark", - "styles": { - "width": vals.width, - "height": vals.height, - "background": vals.darkbg, - "box-sizing": "content-box", - "color": vals.darkfg, - "position": "absolute", - "text-align": "center", - "left": 0, - "top": 0, - "line-height": vals.height - } - }); - obj.vals.light = new Element("div", { - "id": vals.id + "_light", - "class": "progressbar_light", - "styles": { - "width": vals.width, - "height": vals.height, - "background": vals.lightbg, - "box-sizing": "content-box", - "color": vals.lightfg, - "position": "absolute", - "text-align": "center", - "left": 0, - "top": 0, - "line-height": vals.height - } - }); + + const dark = document.createElement("div"); + dark.id = `${vals.id}_dark`; + dark.className = "progressbar_dark"; + dark.style.width = `${vals.width}px`; + dark.style.height = `${vals.height}px`; + dark.style.background = vals.darkbg; + dark.style.boxSizing = "content-box"; + dark.style.color = vals.darkfg; + dark.style.position = "absolute"; + dark.style.textAlign = "center"; + dark.style.left = "0"; + dark.style.top = "0"; + dark.style.lineHeight = `${vals.height}px`; + + obj.vals.dark = dark; + + const light = document.createElement("div"); + light.id = `${vals.id}_light`; + light.className = "progressbar_light"; + light.style.width = `${vals.width}px`; + light.style.height = `${vals.height}px`; + light.style.background = vals.lightbg; + light.style.boxSizing = "content-box"; + light.style.color = vals.lightfg; + light.style.position = "absolute"; + light.style.textAlign = "center"; + light.style.left = "0"; + light.style.top = "0"; + light.style.lineHeight = `${vals.height}px`; + + obj.vals.light = light; + obj.appendChild(obj.vals.dark); obj.appendChild(obj.vals.light); obj.getValue = ProgressBar_getValue; diff --git a/src/webui/www/private/scripts/prop-files.js b/src/webui/www/private/scripts/prop-files.js index 0edf84392b2..9cbae8c7c56 100644 --- a/src/webui/www/private/scripts/prop-files.js +++ b/src/webui/www/private/scripts/prop-files.js @@ -131,7 +131,7 @@ window.qBittorrent.PropFiles ??= (() => { }; const createDownloadCheckbox = (id, fileId, checked) => { - const checkbox = new Element("input"); + const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = "cbPrio" + id; checkbox.setAttribute("data-id", id); @@ -623,13 +623,13 @@ window.qBittorrent.PropFiles ??= (() => { // inject checkbox into table header const tableHeaders = $$("#torrentFilesTableFixedHeaderDiv .dynamicTableHeader th"); if (tableHeaders.length > 0) { - const checkbox = new Element("input"); + const checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.id = "tristate_cb"; checkbox.addEventListener("click", switchCheckboxState); const checkboxTH = tableHeaders[0]; - checkbox.injectInside(checkboxTH); + checkboxTH.append(checkbox); } // default sort by name column diff --git a/src/webui/www/private/scripts/search.js b/src/webui/www/private/scripts/search.js index a756a54b77c..e51d4ce1315 100644 --- a/src/webui/www/private/scripts/search.js +++ b/src/webui/www/private/scripts/search.js @@ -175,20 +175,18 @@ window.qBittorrent.Search ??= (() => { const createSearchTab = (searchId, pattern) => { const newTabId = `${searchTabIdPrefix}${searchId}`; - const tabElem = new Element("a", { - text: pattern, - }); - - const closeTabElem = new Element("img", { - alt: "QBT_TR(Close tab)QBT_TR[CONTEXT=SearchWidget]", - title: "QBT_TR(Close tab)QBT_TR[CONTEXT=SearchWidget]", - src: "images/application-exit.svg", - width: "10", - height: "10", - onclick: "qBittorrent.Search.closeSearchTab(this);", - }); - closeTabElem.inject(tabElem, "top"); - + const tabElem = document.createElement("a"); + tabElem.textContent = pattern; + + const closeTabElem = document.createElement("img"); + closeTabElem.alt = "QBT_TR(Close tab)QBT_TR[CONTEXT=SearchWidget]"; + closeTabElem.title = "QBT_TR(Close tab)QBT_TR[CONTEXT=SearchWidget]"; + closeTabElem.src = "images/application-exit.svg"; + closeTabElem.width = "10"; + closeTabElem.height = "10"; + closeTabElem.addEventListener("click", function(e) { qBittorrent.Search.closeSearchTab(this); }); + + tabElem.prepend(closeTabElem); tabElem.appendChild(getStatusIconElement("QBT_TR(Searching...)QBT_TR[CONTEXT=SearchJobWidget]", "images/queued.svg")); const listItem = document.createElement("li"); @@ -375,14 +373,14 @@ window.qBittorrent.Search ??= (() => { }; const getStatusIconElement = (text, image) => { - return new Element("img", { - alt: text, - title: text, - src: image, - class: "statusIcon", - width: "12", - height: "12", - }); + const statusIcon = document.createElement("img"); + statusIcon.alt = text; + statusIcon.title = text; + statusIcon.src = image; + statusIcon.className = "statusIcon"; + statusIcon.width = "12"; + statusIcon.height = "12"; + return statusIcon; }; const updateStatusIconElement = (searchId, text, image) => { From eb2eea8d345173311908031f8c6fe7ff3e74e95f Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Sun, 15 Dec 2024 14:07:36 +0300 Subject: [PATCH 2/2] Avoid race condition when update tracker entries PR #21995. --- src/base/bittorrent/sessionimpl.cpp | 12 +++++++++--- src/base/bittorrent/sessionimpl.h | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index ac14c52a519..abf56a9babc 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -6234,6 +6235,8 @@ void SessionImpl::handleTrackerAlert(const lt::tracker_alert *alert) if (!torrent) return; + [[maybe_unused]] const QMutexLocker updatedTrackerStatusesLocker {&m_updatedTrackerStatusesMutex}; + const auto prevSize = m_updatedTrackerStatuses.size(); QMap &updateInfo = m_updatedTrackerStatuses[torrent->nativeHandle()][std::string(alert->tracker_url())][alert->local_endpoint]; if (prevSize < m_updatedTrackerStatuses.size()) @@ -6330,14 +6333,17 @@ void SessionImpl::updateTrackerEntryStatuses(lt::torrent_handle torrentHandle) try { std::vector nativeTrackers = torrentHandle.trackers(); - invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers)] + + QMutexLocker updatedTrackerStatusesLocker {&m_updatedTrackerStatusesMutex}; + QHash>> updatedTrackers = m_updatedTrackerStatuses.take(torrentHandle); + updatedTrackerStatusesLocker.unlock(); + + invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers), updatedTrackers = std::move(updatedTrackers)] { TorrentImpl *torrent = m_torrents.value(torrentHandle.info_hash()); if (!torrent || torrent->isStopped()) return; - QHash>> updatedTrackers = m_updatedTrackerStatuses.take(torrentHandle); - QHash trackers; trackers.reserve(updatedTrackers.size()); for (const lt::announce_entry &announceEntry : nativeTrackers) diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index 32aeed86d28..7298afabe8c 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -799,6 +800,7 @@ namespace BitTorrent // This field holds amounts of peers reported by trackers in their responses to announces // (torrent.tracker_name.tracker_local_endpoint.protocol_version.num_peers) QHash>>> m_updatedTrackerStatuses; + QMutex m_updatedTrackerStatusesMutex; // I/O errored torrents QSet m_recentErroredTorrents;