Skip to content

Commit e024856

Browse files
authored
Refactor jobs engine into multiple files (#28)
* Refactor jobs engine into multiple files
1 parent 9d0b439 commit e024856

File tree

9 files changed

+820
-610
lines changed

9 files changed

+820
-610
lines changed

README.md

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ Signal exit when we no longer want to use it,
399399

400400
`signal_exit_when_done`
401401

402-
Use it like this
402+
Use it like this (for a more complete example see the [example](examples/examples_jobs_engine.h) )
403403

404404
```
405405
enum class JobsType
@@ -412,44 +412,48 @@ enum class JobsGroupType
412412
kJobsGroup1
413413
};
414414
...
415-
using JobsRequest = std::pair<int, std::string>;
416-
using JobsResponse = int;
417-
using JobsEng = small::jobs_engine<JobsType, JobsRequest, JobsResponse, JobsGroupType>;
418-
...
419-
JobsEng jobs(
420-
{.threads_count = 0 /*dont start any thread yet*/}, // overall config with default priorities
421-
{.threads_count = 1, .bulk_count = 1}, // default jobs group config
422-
{.group = JobsGroupType::kJobsGroup1}, // default jobs type config
423-
[](auto &j /*this*/, const auto &items) {
424-
for (auto &item : items) {
425-
... // item->
426-
}
415+
using Request = std::pair<int, std::string>;
416+
using JobsEng = small::jobs_engine<JobsType, Request, int /*response*/, JobsGroupType>;
417+
...
418+
JobsEng::JobsConfig config{
419+
.m_engine = {.m_threads_count = 0 /*dont start any thread yet*/ }, // overall config with default priorities
420+
.m_groups = {
421+
{JobsGroupType::kJobsGroup1, {.m_threads_count = 1}}}, // config by jobs group
422+
.m_types = {
423+
{JobsType::kJobsType1, {.m_group = JobsGroupType::kJobsGroup1}},
424+
{JobsType::kJobsType2, {.m_group = JobsGroupType::kJobsGroup1}},
425+
}};
426+
...
427+
// create jobs engine
428+
JobsEng jobs(config);
429+
...
430+
jobs.add_default_processing_function([](auto &j /*this jobs engine*/, const auto &jobs_items) {
431+
for (auto &item : jobs_items) {
427432
...
428-
});
429-
430-
jobs.add_jobs_group(JobsGroupType::kJobsGroup1, {.threads_count = 1});
433+
}
434+
...
435+
});
431436
...
432-
// add specific function for job1
433-
jobs.add_jobs_type(JobsType::kJobsType1, {.group = JobsGroupType::kJobsGroup1}, [](auto &j /*this*/, const auto &items, auto b /*extra param b*/) {
434-
for (auto &item : items) {
435-
... // item->
437+
// add specific function for job1 (calling the function from jobs intead of config allows to pass the engine and extra param)
438+
jobs.add_job_processing_function(JobsType::kJobsType1, [](auto &j /*this jobs engine*/, const auto &jobs_items, auto b /*extra param b*/) {
439+
for (auto &item : jobs_items) {
440+
...
436441
}
437-
438-
}, 5 /*extra param b*/);
439-
440-
// use default config and default function for job2
441-
jobs.add_jobs_type(JobsType::kJobsType2);
442+
}, 5 /*param b*/);
442443
...
443444
JobsEng::JobsID jobs_id{};
444445
std::vector<JobsEng::JobsID> jobs_ids;
445-
446-
jobs.push_back(small::EnumPriorities::kNormal, JobsType::kJobsType1, {1, "normal"}, &jobs_id);
447-
jobs.push_back(small::EnumPriorities::kHigh, JobsType::kJobsType2, {2, "high"}, &jobs_id);
448-
449-
std::vector<JobsEng::JobsItem> jobs_items = {{.type = JobsType::kJobsType1, .request = {7, "highest"}}};
450-
jobs.push_back(small::EnumPriorities::kHighest, jobs_items, &jobs_ids);
451-
452-
jobs.push_back_delay_for(std::chrono::milliseconds(300), small::EnumPriorities::kNormal, JobsType::kJobsType1, {100, "delay normal"}, &jobs_id);
446+
...
447+
// push
448+
jobs.queue().push_back(small::EnumPriorities::kNormal, JobsType::kJobsType1, {1, "normal"}, &jobs_id);
449+
...
450+
std::vector<std::shared_ptr<JobsEng::JobsItem>> jobs_items = {
451+
std::make_shared<JobsEng::JobsItem>(JobsType::kJobsType1, Request{7, "highest"}),
452+
std::make_shared<JobsEng::JobsItem>(JobsType::kJobsType1, Request{8, "highest"}),
453+
};
454+
jobs.queue().push_back(small::EnumPriorities::kHighest, jobs_items, &jobs_ids);
455+
...
456+
jobs.queue().push_back_delay_for(std::chrono::milliseconds(300), small::EnumPriorities::kNormal, JobsType::kJobsType1, {100, "delay normal"}, &jobs_id);
453457
...
454458
jobs.start_threads(3); // manual start threads
455459
...

examples/examples_jobs_engine.h

Lines changed: 71 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -27,72 +27,86 @@ namespace examples::jobs_engine {
2727
};
2828

2929
using Request = std::pair<int, std::string>;
30-
using JobsEng = small::jobs_engine<JobsType, Request, int, JobsGroupType>;
31-
32-
JobsEng jobs(
33-
{.threads_count = 0 /*dont start any thread yet*/}, // overall config with default priorities
34-
{.threads_count = 1, .bulk_count = 1}, // default jobs group config
35-
{.group = JobsGroupType::kJobsGroup1}, // default jobs type config
36-
[](auto &j /*this*/, const auto &items) {
37-
for (auto &item : items) {
38-
std::cout << "thread " << std::this_thread::get_id()
39-
<< " DEFAULT processing "
40-
<< "{"
41-
<< " type=" << (int)item->type
42-
<< " req.int=" << item->request.first << ","
43-
<< " req.str=\"" << item->request.second << "\""
44-
<< "}"
45-
<< " time " << small::toISOString(small::timeNow())
46-
<< "\n";
47-
}
48-
small::sleep(30);
49-
});
50-
51-
jobs.add_jobs_group(JobsGroupType::kJobsGroup1, {.threads_count = 1});
52-
53-
// add specific function for job1
54-
jobs.add_jobs_type(JobsType::kJobsType1, {.group = JobsGroupType::kJobsGroup1}, [](auto &j /*this*/, const auto &items, auto b /*extra param b*/) {
55-
// process item using the jobs lock (not recommended)
56-
{
57-
std::unique_lock mlock( j );
58-
for (auto &item : items) {
59-
std::cout << "thread " << std::this_thread::get_id()
60-
<< " JOB1 processing "
61-
<< "{"
62-
<< " type=" << (int)item->type
63-
<< " req.int=" << item->request.first << ","
64-
<< " req.str=\"" << item->request.second << "\""
65-
<< "}"
66-
<< " time " << small::toISOString(small::timeNow())
67-
<< "\n";
68-
}
69-
}
70-
small::sleep(30); }, 5 /*param b*/);
30+
using JobsEng = small::jobs_engine<JobsType, Request, int /*response*/, JobsGroupType>;
31+
32+
auto jobs_processing_function = [](const std::vector<std::shared_ptr<JobsEng::JobsItem>> &items) {
33+
// this functions is defined without the engine params (it is here just for the example)
34+
std::cout << "this function is defined without the engine params, called for " << (int)items[0]->type << "\n";
35+
};
7136

72-
// use default config and default function for job2
73-
jobs.add_jobs_type(JobsType::kJobsType2);
37+
JobsEng::JobsConfig config{
38+
.m_engine = {.m_threads_count = 0 /*dont start any thread yet*/,
39+
.m_config_prio = {.priorities = {{small::EnumPriorities::kHighest, 2},
40+
{small::EnumPriorities::kHigh, 2},
41+
{small::EnumPriorities::kNormal, 2},
42+
{small::EnumPriorities::kLow, 1}}}}, // overall config with default priorities
43+
.m_default_processing_function = jobs_processing_function, // default processing function, better use jobs.add_default_processing_function to set it
44+
.m_groups = {
45+
{JobsGroupType::kJobsGroup1, {.m_threads_count = 1}}}, // config by jobs group
46+
.m_types = {
47+
{JobsType::kJobsType1, {.m_group = JobsGroupType::kJobsGroup1}},
48+
{JobsType::kJobsType2, {.m_group = JobsGroupType::kJobsGroup1}},
49+
}};
50+
51+
// create jobs engine
52+
JobsEng jobs(config);
53+
54+
jobs.add_default_processing_function([](auto &j /*this jobs engine*/, const auto &jobs_items) {
55+
for (auto &item : jobs_items) {
56+
std::cout << "thread " << std::this_thread::get_id()
57+
<< " DEFAULT processing "
58+
<< "{"
59+
<< " type=" << (int)item->type
60+
<< " req.int=" << item->request.first << ","
61+
<< " req.str=\"" << item->request.second << "\""
62+
<< "}"
63+
<< " ref count " << item.use_count()
64+
<< " time " << small::toISOString(small::timeNow())
65+
<< "\n";
66+
}
67+
small::sleep(30);
68+
});
69+
70+
// add specific function for job1 (calling the function from jobs intead of config allows to pass the engine and extra param)
71+
jobs.add_job_processing_function(JobsType::kJobsType1, [](auto &j /*this jobs engine*/, const auto &jobs_items, auto b /*extra param b*/) {
72+
for (auto &item : jobs_items) {
73+
std::cout << "thread " << std::this_thread::get_id()
74+
<< " JOB1 processing "
75+
<< "{"
76+
<< " type=" << (int)item->type
77+
<< " req.int=" << item->request.first << ","
78+
<< " req.str=\"" << item->request.second << "\""
79+
<< "}"
80+
<< " ref count " << item.use_count()
81+
<< " time " << small::toISOString(small::timeNow())
82+
<< "\n";
83+
}
84+
small::sleep(30); }, 5 /*param b*/);
7485

7586
JobsEng::JobsID jobs_id{};
7687
std::vector<JobsEng::JobsID> jobs_ids;
7788

7889
// push
79-
jobs.push_back(small::EnumPriorities::kNormal, JobsType::kJobsType1, {1, "normal"}, &jobs_id);
80-
jobs.push_back(small::EnumPriorities::kHigh, JobsType::kJobsType2, {2, "high"}, &jobs_id);
90+
jobs.queue().push_back(small::EnumPriorities::kNormal, JobsType::kJobsType1, {1, "normal"}, &jobs_id);
91+
jobs.queue().push_back(small::EnumPriorities::kHigh, JobsType::kJobsType2, {2, "high"}, &jobs_id);
8192

82-
jobs.push_back(small::EnumPriorities::kNormal, JobsType::kJobsType1, std::make_pair(3, "normal"), &jobs_id);
83-
jobs.push_back(small::EnumPriorities::kHigh, {.type = JobsType::kJobsType1, .request = {4, "high"}}, &jobs_id);
84-
jobs.push_back(small::EnumPriorities::kLow, JobsType::kJobsType1, {5, "low"}, &jobs_id);
93+
jobs.queue().push_back(small::EnumPriorities::kNormal, JobsType::kJobsType1, std::make_pair(3, "normal"), &jobs_id);
94+
jobs.queue().push_back(small::EnumPriorities::kHigh, JobsType::kJobsType1, {4, "high"}, &jobs_id);
95+
jobs.queue().push_back(small::EnumPriorities::kLow, JobsType::kJobsType1, {5, "low"}, &jobs_id);
8596

8697
Request req = {6, "normal"};
87-
jobs.push_back(small::EnumPriorities::kNormal, {.type = JobsType::kJobsType1, .request = req}, nullptr);
98+
jobs.queue().push_back(small::EnumPriorities::kNormal, JobsType::kJobsType1, req, nullptr);
8899

89-
std::vector<JobsEng::JobsItem> jobs_items = {{.type = JobsType::kJobsType1, .request = {7, "highest"}}};
90-
jobs.push_back(small::EnumPriorities::kHighest, jobs_items, &jobs_ids);
91-
jobs.push_back(small::EnumPriorities::kHighest, {{.type = JobsType::kJobsType1, .request = {8, "highest"}}}, &jobs_ids);
100+
std::vector<std::shared_ptr<JobsEng::JobsItem>> jobs_items = {
101+
std::make_shared<JobsEng::JobsItem>(JobsType::kJobsType1, Request{7, "highest"}),
102+
std::make_shared<JobsEng::JobsItem>(JobsType::kJobsType1, Request{8, "highest"}),
103+
};
104+
jobs.queue().push_back(small::EnumPriorities::kHighest, jobs_items, &jobs_ids);
105+
jobs.queue().push_back(small::EnumPriorities::kHighest, {std::make_shared<JobsEng::JobsItem>(JobsType::kJobsType1, Request{9, "highest"})}, &jobs_ids);
92106

93-
jobs.push_back_delay_for(std::chrono::milliseconds(300), small::EnumPriorities::kNormal, JobsType::kJobsType1, {100, "delay normal"}, &jobs_id);
94-
jobs.push_back_delay_until(small::timeNow() + std::chrono::milliseconds(350), small::EnumPriorities::kNormal, JobsType::kJobsType1, {101, "delay normal"}, &jobs_id);
95-
jobs.push_back_delay_for(std::chrono::milliseconds(400), small::EnumPriorities::kNormal, JobsType::kJobsType1, {102, "delay normal"}, &jobs_id);
107+
jobs.queue().push_back_delay_for(std::chrono::milliseconds(300), small::EnumPriorities::kNormal, JobsType::kJobsType1, {100, "delay normal"}, &jobs_id);
108+
jobs.queue().push_back_delay_until(small::timeNow() + std::chrono::milliseconds(350), small::EnumPriorities::kNormal, JobsType::kJobsType1, {101, "delay normal"}, &jobs_id);
109+
jobs.queue().push_back_delay_for(std::chrono::milliseconds(400), small::EnumPriorities::kNormal, JobsType::kJobsType1, {102, "delay normal"}, &jobs_id);
96110

97111
jobs.start_threads(3); // manual start threads
98112

@@ -102,6 +116,8 @@ namespace examples::jobs_engine {
102116
std::cout << "wait for with timeout, ret = " << static_cast<int>(ret) << " as timeout\n";
103117
jobs.wait(); // wait here for jobs to finish due to exit flag
104118

119+
std::cout << "size = " << jobs.size() << "\n";
120+
105121
std::cout << "Jobs Engine example 1 finish\n\n";
106122

107123
return 0;

include/buffer.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ namespace small {
5353
//
5454
// class for representing a buffer
5555
//
56-
class buffer : public base_buffer
56+
class buffer : public small::bufferimpl::base_buffer
5757
{
5858
public:
5959
// buffer (allocates in chunks)

include/impl/base_buffer_impl.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
#include <type_traits>
1010
#include <vector>
1111

12-
namespace small {
12+
namespace small::bufferimpl {
1313
// class for representing a base_buffer that implements
1414
// all the needed functions and operators
1515
// it must be supplied with derived class with proper functions
@@ -457,4 +457,4 @@ namespace small {
457457
std::size_t m_buffer_length{0};
458458
};
459459

460-
} // namespace small
460+
} // namespace small::bufferimpl

include/jobs_engine_thread_pool.h renamed to include/impl/jobs_engine_thread_pool_impl.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
#include <unordered_map>
44

5-
#include "worker_thread.h"
5+
#include "../worker_thread.h"
66

7-
namespace small {
7+
namespace small::jobsimpl {
88

99
//
1010
// helper class for jobs_engine to execute group of jobs (parent caller must implement 'do_action')
@@ -188,7 +188,7 @@ namespace small {
188188
//
189189
struct JobWorkerThreadFunction
190190
{
191-
void operator()(small::worker_thread<JobGroupT> &, const std::vector<JobGroupT> &items, small::jobs_engine_thread_pool<JobGroupT, ParentCallerT> *pThis) const
191+
void operator()(small::worker_thread<JobGroupT> &, const std::vector<JobGroupT> &items, jobs_engine_thread_pool<JobGroupT, ParentCallerT> *pThis) const
192192
{
193193
pThis->thread_function(items);
194194
}
@@ -198,4 +198,4 @@ namespace small {
198198
small::worker_thread<JobGroupT> m_workers{{.threads_count = 0}, JobWorkerThreadFunction(), this};
199199
ParentCallerT &m_parent_caller; // parent jobs engine
200200
};
201-
} // namespace small
201+
} // namespace small::jobsimpl

include/impl/jobs_item_impl.h

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
#pragma once
2+
3+
#include <atomic>
4+
#include <deque>
5+
#include <functional>
6+
#include <queue>
7+
#include <unordered_map>
8+
#include <vector>
9+
10+
#include "../base_lock.h"
11+
12+
namespace small::jobsimpl {
13+
// a job can be in the following states
14+
enum class EnumJobsState : unsigned int
15+
{
16+
kNone = 0,
17+
kInProgress,
18+
kFinished,
19+
kFailed,
20+
kCancelled,
21+
kTimeout
22+
};
23+
24+
// a job item
25+
template <typename JobsTypeT, typename JobsRequestT, typename JobsResponseT>
26+
struct jobs_item
27+
{
28+
using JobsID = unsigned long long;
29+
30+
JobsID id{}; // job unique id
31+
JobsTypeT type{}; // job type
32+
std::atomic<EnumJobsState> state{EnumJobsState::kNone}; // job state
33+
std::atomic<int> progress{}; // progress 0-100 for state kInProgress
34+
JobsRequestT request{}; // request needed for processing function
35+
JobsResponseT response{}; // where the results are saved (for the finished callback if exists)
36+
37+
explicit jobs_item() = default;
38+
explicit jobs_item(const JobsID &jobs_id, const JobsTypeT &jobs_type, const JobsRequestT &jobs_request)
39+
: id(jobs_id), type(jobs_type), request(jobs_request) {}
40+
explicit jobs_item(const JobsTypeT &jobs_type, const JobsRequestT &jobs_request)
41+
: type(jobs_type), request(jobs_request) {}
42+
explicit jobs_item(const JobsID &jobs_id, const JobsTypeT &jobs_type, JobsRequestT &&jobs_request)
43+
: id(jobs_id), type(jobs_type), request(std::forward<JobsRequestT>(jobs_request)) {}
44+
explicit jobs_item(const JobsTypeT &jobs_type, JobsRequestT &&jobs_request)
45+
: type(jobs_type), request(std::forward<JobsRequestT>(jobs_request)) {}
46+
47+
jobs_item(const jobs_item &other) { operator=(other); };
48+
jobs_item(jobs_item &&other) noexcept { operator=(other); };
49+
jobs_item &operator=(const jobs_item &other)
50+
{
51+
id = other.id;
52+
type = other.type;
53+
state = other.state.load();
54+
progress = other.progress.load();
55+
request = other.request;
56+
response = other.response;
57+
return *this;
58+
}
59+
jobs_item &operator=(jobs_item &&other) noexcept
60+
{
61+
id = std::move(other.id);
62+
type = std::move(other.type);
63+
state = other.state.load();
64+
progress = other.progress.load();
65+
request = std::move(other.request);
66+
response = std::move(other.response);
67+
return *this;
68+
}
69+
70+
//
71+
// set job state (can only go from lower to upper state)
72+
//
73+
inline void set_state(const EnumJobsState &new_state)
74+
{
75+
for (;;) {
76+
EnumJobsState current_state = state.load();
77+
if (current_state >= new_state) {
78+
return;
79+
}
80+
if (state.compare_exchange_weak(current_state, new_state)) {
81+
return;
82+
}
83+
}
84+
}
85+
86+
//
87+
// set job progress (can only increase)
88+
//
89+
inline void set_progress(const int &new_progress)
90+
{
91+
for (;;) {
92+
int current_progress = progress.load();
93+
if (current_progress >= new_progress) {
94+
return;
95+
}
96+
if (progress.compare_exchange_weak(current_progress, new_progress)) {
97+
return;
98+
}
99+
}
100+
}
101+
};
102+
103+
} // namespace small::jobsimpl

0 commit comments

Comments
 (0)