Skip to content

Commit 944fd2e

Browse files
committed
Rewrite downloader using cpr::Session
1 parent 33de213 commit 944fd2e

File tree

7 files changed

+99
-208
lines changed

7 files changed

+99
-208
lines changed

src/internal/downloader.cpp

Lines changed: 13 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,32 @@
11
// SPDX-License-Identifier: Apache-2.0
22

3+
#include <iostream>
4+
35
#include "downloader.h"
46

57
using namespace libscratchcpp;
68

79
Downloader::Downloader()
810
{
11+
m_session.SetTimeout(cpr::Timeout(5000));
912
}
1013

11-
void Downloader::startDownload(const std::string &url)
14+
bool Downloader::download(const std::string &url)
1215
{
13-
if (m_asyncResponse)
14-
cancel();
16+
m_session.SetUrl(cpr::Url(url));
17+
m_response = m_session.Get();
1518

16-
m_asyncResponse = std::make_unique<cpr::AsyncResponse>(cpr::GetAsync(cpr::Url(url)));
17-
}
19+
if (m_response.status_code != 200) {
20+
std::cerr << "download error: " << m_response.error.message << std::endl;
1821

19-
void Downloader::cancel()
20-
{
21-
if (m_asyncResponse) {
22-
if (!m_asyncResponse->IsCancelled())
23-
(void)(m_asyncResponse->Cancel());
24-
m_asyncResponse.reset();
25-
}
26-
}
22+
if (m_response.status_code != 0)
23+
std::cerr << "code: " << m_response.status_code << std::endl;
2724

28-
void Downloader::wait()
29-
{
30-
if (m_asyncResponse) {
31-
m_asyncResponse->wait();
32-
m_response = m_asyncResponse->get();
33-
}
34-
}
35-
36-
bool Downloader::isCancelled() const
37-
{
38-
if (!m_asyncResponse)
25+
std::cerr << "URL: " << url << std::endl;
3926
return false;
27+
}
4028

41-
return m_asyncResponse->IsCancelled();
29+
return true;
4230
}
4331

4432
const std::string &Downloader::text() const

src/internal/downloader.h

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,11 @@ class Downloader : public IDownloader
1515
public:
1616
Downloader();
1717

18-
void startDownload(const std::string &url) override;
19-
void cancel() override;
20-
void wait() override;
21-
22-
bool isCancelled() const override;
18+
bool download(const std::string &url) override;
2319
const std::string &text() const override;
2420

2521
private:
26-
std::unique_ptr<cpr::AsyncResponse> m_asyncResponse;
22+
cpr::Session m_session;
2723
cpr::Response m_response;
2824
};
2925

src/internal/idownloader.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@ class IDownloader
1212
public:
1313
virtual ~IDownloader() { }
1414

15-
virtual void startDownload(const std::string &url) = 0;
16-
virtual void cancel() = 0;
17-
virtual void wait() = 0;
18-
19-
virtual bool isCancelled() const = 0;
15+
virtual bool download(const std::string &url) = 0;
2016
virtual const std::string &text() const = 0;
2117
};
2218

src/internal/projectdownloader.cpp

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <iostream>
44
#include <thread>
55
#include <nlohmann/json.hpp>
6+
#include <cpr/cpr.h>
67

78
#include "projectdownloader.h"
89
#include "downloaderfactory.h"
@@ -19,6 +20,8 @@ static const std::string ASSET_SUFFIX = "/get";
1920
m_cancelMutex.lock(); \
2021
if (m_cancel) { \
2122
m_downloadedAssetCount = 0; \
23+
m_cancelMutex.unlock(); \
24+
std::cout << "Download aborted!" << std::endl; \
2225
return false; \
2326
} \
2427
m_cancelMutex.unlock()
@@ -41,17 +44,13 @@ bool ProjectDownloader::downloadJson(const std::string &projectId)
4144

4245
// Get project token
4346
std::cout << "Fetching project info of " << projectId << std::endl;
44-
m_tokenDownloader->startDownload(PROJECT_META_PREFIX + projectId);
45-
m_tokenDownloader->wait();
47+
bool ret = m_tokenDownloader->download(PROJECT_META_PREFIX + projectId);
4648

47-
if (m_tokenDownloader->text().empty()) {
49+
if (!ret) {
4850
std::cerr << "Could not fetch project info of " << projectId << std::endl;
4951
return false;
5052
}
5153

52-
if (m_tokenDownloader->isCancelled())
53-
return false;
54-
5554
CHECK_CANCEL();
5655

5756
nlohmann::json json;
@@ -76,17 +75,13 @@ bool ProjectDownloader::downloadJson(const std::string &projectId)
7675

7776
// Download project JSON
7877
std::cout << "Downloading project JSON of " << projectId << std::endl;
79-
m_jsonDownloader->startDownload(PROJECT_JSON_PREFIX + projectId + "?token=" + token);
80-
m_jsonDownloader->wait();
78+
ret = m_jsonDownloader->download(PROJECT_JSON_PREFIX + projectId + "?token=" + token);
8179

82-
if (m_jsonDownloader->text().empty()) {
80+
if (!ret) {
8381
std::cerr << "Failed to download project JSON of " << projectId << std::endl;
8482
return false;
8583
}
8684

87-
if (m_jsonDownloader->isCancelled())
88-
return false;
89-
9085
CHECK_CANCEL();
9186

9287
return true;
@@ -100,16 +95,16 @@ bool ProjectDownloader::downloadAssets(const std::vector<std::string> &assetIds)
10095

10196
auto count = assetIds.size();
10297
unsigned int threadCount = std::thread::hardware_concurrency();
103-
unsigned int times; // how many times we should download assets simultaneously (in "groups")
98+
99+
// Thread count: number of assets / 5, limited to maximum number of threads
100+
threadCount = std::max(1u, std::min(threadCount, static_cast<unsigned int>(std::ceil(count / 5.0))));
101+
104102
m_assets.clear();
103+
m_assets.reserve(count);
105104
m_downloadedAssetCount = 0;
106105

107-
// Calculate number of "groups"
108-
if (threadCount == 0) {
109-
times = count;
110-
threadCount = 1;
111-
} else
112-
times = std::ceil(count / static_cast<double>(threadCount));
106+
for (unsigned int i = 0; i < count; i++)
107+
m_assets.push_back(std::string());
113108

114109
std::cout << "Downloading " << count << " asset(s)";
115110

@@ -125,33 +120,55 @@ bool ProjectDownloader::downloadAssets(const std::vector<std::string> &assetIds)
125120
downloaders.push_back(m_downloaderFactory->create());
126121

127122
// Download assets
128-
for (unsigned int i = 0; i < times; i++) {
129-
unsigned int currentCount = std::min(threadCount, static_cast<unsigned int>(count - i * threadCount));
123+
auto f = [this, &downloaders, &assetIds, count, threadCount](unsigned int thread) {
124+
auto downloader = downloaders[thread];
125+
unsigned int n = std::ceil(count / static_cast<double>(threadCount));
126+
127+
for (unsigned int i = 0; i < n; i++) {
128+
unsigned int index = thread * n + i;
129+
130+
if (index < count) {
131+
m_cancelMutex.lock();
130132

131-
for (unsigned int j = 0; j < currentCount; j++)
132-
downloaders[j]->startDownload(ASSET_PREFIX + assetIds[i * threadCount + j] + ASSET_SUFFIX);
133+
if (m_cancel)
134+
return;
133135

134-
for (unsigned int j = 0; j < currentCount; j++) {
135-
downloaders[j]->wait();
136-
assert(m_assets.size() == i * threadCount + j);
136+
m_cancelMutex.unlock();
137137

138-
if (downloaders[j]->isCancelled())
139-
return false;
138+
bool ret = downloader->download(ASSET_PREFIX + assetIds[index] + ASSET_SUFFIX);
140139

141-
CHECK_CANCEL();
140+
if (!ret) {
141+
std::cerr << "Failed to download asset: " << assetIds[index] << std::endl;
142+
m_cancelMutex.lock();
143+
m_cancel = true;
144+
m_cancelMutex.unlock();
145+
return;
146+
}
142147

143-
m_assets.push_back(downloaders[j]->text());
144-
m_downloadedAssetCount++;
148+
m_assetsMutex.lock();
149+
m_assets[index] = downloader->text();
150+
m_downloadedAssetCount++;
151+
std::cout << "Downloaded assets: " << m_downloadedAssetCount << " of " << count << std::endl;
152+
m_assetsMutex.unlock();
153+
}
145154
}
146-
}
155+
};
156+
157+
std::vector<std::thread> threads;
158+
159+
for (unsigned int i = 0; i < threadCount; i++)
160+
threads.emplace_back(std::thread(f, i));
161+
162+
for (unsigned int i = 0; i < threadCount; i++)
163+
threads[i].join();
164+
165+
CHECK_CANCEL();
147166

148167
return true;
149168
}
150169

151170
void ProjectDownloader::cancel()
152171
{
153-
m_tokenDownloader->cancel();
154-
m_jsonDownloader->cancel();
155172
m_cancelMutex.lock();
156173
m_cancel = true;
157174
m_cancelMutex.unlock();

src/internal/projectdownloader.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class ProjectDownloader : public IProjectDownloader
3232
std::shared_ptr<IDownloader> m_tokenDownloader;
3333
std::shared_ptr<IDownloader> m_jsonDownloader;
3434
std::vector<std::string> m_assets;
35+
std::mutex m_assetsMutex;
3536
std::atomic<unsigned int> m_downloadedAssetCount = 0;
3637
bool m_cancel = false;
3738
std::mutex m_cancelMutex;

test/mocks/downloadermock.h

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,6 @@ using namespace libscratchcpp;
88
class DownloaderMock : public IDownloader
99
{
1010
public:
11-
MOCK_METHOD(void, startDownload, (const std::string &), (override));
12-
MOCK_METHOD(void, cancel, (), (override));
13-
MOCK_METHOD(void, wait, (), (override));
14-
15-
MOCK_METHOD(bool, isCancelled, (), (const, override));
11+
MOCK_METHOD(bool, download, (const std::string &), (override));
1612
MOCK_METHOD(const std::string &, text, (), (const, override));
1713
};

0 commit comments

Comments
 (0)