pytq-cxx 1.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pytq_cxx-1.0.2/MANIFEST.in +4 -0
- pytq_cxx-1.0.2/PKG-INFO +21 -0
- pytq_cxx-1.0.2/README_pytq.md +9 -0
- pytq_cxx-1.0.2/_yatq/bind.cpp +72 -0
- pytq_cxx-1.0.2/_yatq/include/yatq/internal/concepts.h +61 -0
- pytq_cxx-1.0.2/_yatq/include/yatq/internal/log4cxx_proxy.h +23 -0
- pytq_cxx-1.0.2/_yatq/include/yatq/internal/promise_utils.h +32 -0
- pytq_cxx-1.0.2/_yatq/include/yatq/thread_pool.h +156 -0
- pytq_cxx-1.0.2/_yatq/include/yatq/timer_queue.h +368 -0
- pytq_cxx-1.0.2/_yatq/include/yatq/utils/logging_utils.h +51 -0
- pytq_cxx-1.0.2/_yatq/include/yatq/utils/sched_utils.h +50 -0
- pytq_cxx-1.0.2/_yatq/include/yatq/version.h +6 -0
- pytq_cxx-1.0.2/_yatq/traits.h +19 -0
- pytq_cxx-1.0.2/pyproject.toml +19 -0
- pytq_cxx-1.0.2/pytq/__init__.py +5 -0
- pytq_cxx-1.0.2/pytq/awaitable_future.py +19 -0
- pytq_cxx-1.0.2/pytq_cxx.egg-info/PKG-INFO +21 -0
- pytq_cxx-1.0.2/pytq_cxx.egg-info/SOURCES.txt +22 -0
- pytq_cxx-1.0.2/pytq_cxx.egg-info/dependency_links.txt +1 -0
- pytq_cxx-1.0.2/pytq_cxx.egg-info/top_level.txt +2 -0
- pytq_cxx-1.0.2/setup.cfg +4 -0
- pytq_cxx-1.0.2/setup.py +60 -0
- pytq_cxx-1.0.2/setup_helpers/find_dep_headers.cmake +9 -0
- pytq_cxx-1.0.2/setup_helpers/find_dep_libs.cmake +9 -0
pytq_cxx-1.0.2/PKG-INFO
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: pytq-cxx
|
3
|
+
Version: 1.0.2
|
4
|
+
Summary: single-threaded timer demultiplexer backed by C++ implementation
|
5
|
+
Author-email: Nikita Vaganov <nikita.e.vaganov@gmail.com>
|
6
|
+
Project-URL: Homepage, https://github.com/vaganov/yatq
|
7
|
+
Project-URL: Bug Tracker, https://github.com/vaganov/yatq/issues
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
|
10
|
+
Requires-Python: >=3.7
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
|
13
|
+
# pytq-cxx
|
14
|
+
**pytq-cxx** is a _python_ wrapper for **yatq** -- a _C++_ template library. For the documentation and examples please
|
15
|
+
see [yatq homepage](https://github.com/vaganov/yatq)
|
16
|
+
|
17
|
+
**NB:** This package has nothing to do with [pytq package](https://pypi.org/project/pytq)
|
18
|
+
|
19
|
+
**NB:** Module name is, however, **pytq**
|
20
|
+
|
21
|
+
from pytq import ThreadPool, TimerQueue, AwaitableFuture
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# pytq-cxx
|
2
|
+
**pytq-cxx** is a _python_ wrapper for **yatq** -- a _C++_ template library. For the documentation and examples please
|
3
|
+
see [yatq homepage](https://github.com/vaganov/yatq)
|
4
|
+
|
5
|
+
**NB:** This package has nothing to do with [pytq package](https://pypi.org/project/pytq)
|
6
|
+
|
7
|
+
**NB:** Module name is, however, **pytq**
|
8
|
+
|
9
|
+
from pytq import ThreadPool, TimerQueue, AwaitableFuture
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#define PYBIND11_DETAILED_ERROR_MESSAGES
|
2
|
+
#include <pybind11/pybind11.h>
|
3
|
+
#include <pybind11/chrono.h>
|
4
|
+
#include <pybind11/functional.h>
|
5
|
+
|
6
|
+
#ifndef YATQ_DISABLE_FUTURES
|
7
|
+
#define BOOST_THREAD_PROVIDES_FUTURE
|
8
|
+
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
|
9
|
+
#include <boost/thread/future.hpp>
|
10
|
+
#endif
|
11
|
+
|
12
|
+
#include "yatq/thread_pool.h"
|
13
|
+
#include "yatq/timer_queue.h"
|
14
|
+
#include "yatq/version.h"
|
15
|
+
|
16
|
+
#include "traits.h"
|
17
|
+
|
18
|
+
namespace py = pybind11;
|
19
|
+
|
20
|
+
using result_type = py::object;
|
21
|
+
using Future = boost::future<result_type>;
|
22
|
+
using Executable = std::function<result_type(void)>;
|
23
|
+
using ThreadPool = yatq::ThreadPool<Executable>;
|
24
|
+
using TimerQueue = yatq::TimerQueue<ThreadPool>;
|
25
|
+
|
26
|
+
PYBIND11_MODULE(_yatq, m) {
|
27
|
+
#ifndef YATQ_DISABLE_FUTURES
|
28
|
+
auto boost_submodule = m.def_submodule("boost");
|
29
|
+
py::class_<Future>(boost_submodule, "future")
|
30
|
+
.def("get", &Future::get, py::call_guard<py::gil_scoped_release>())
|
31
|
+
.def("wait", &Future::wait, py::call_guard<py::gil_scoped_release>())
|
32
|
+
.def("is_ready", py::overload_cast<>(&Future::is_ready, py::const_));
|
33
|
+
#endif
|
34
|
+
|
35
|
+
#ifndef YATQ_DISABLE_PTHREAD
|
36
|
+
auto utils_submodule = m.def_submodule("utils");
|
37
|
+
py::enum_<yatq::utils::priority_t>(utils_submodule, "priority_t")
|
38
|
+
.value("min_priority", yatq::utils::min_priority)
|
39
|
+
.value("max_priority", yatq::utils::max_priority)
|
40
|
+
.export_values();
|
41
|
+
#endif
|
42
|
+
|
43
|
+
py::class_<ThreadPool>(m, "ThreadPool")
|
44
|
+
.def(py::init<>())
|
45
|
+
.def("start", &ThreadPool::start, py::arg("num_threads"))
|
46
|
+
.def("stop", &ThreadPool::stop)
|
47
|
+
.def("execute", &ThreadPool::execute, py::arg("job"));
|
48
|
+
|
49
|
+
py::class_<TimerQueue::TimerHandle>(m, "TimerHandle")
|
50
|
+
.def_readwrite("uid", &TimerQueue::TimerHandle::uid)
|
51
|
+
.def_readwrite("deadline", &TimerQueue::TimerHandle::deadline)
|
52
|
+
#ifndef YATQ_DISABLE_FUTURES
|
53
|
+
.def_readonly("result", &TimerQueue::TimerHandle::result)
|
54
|
+
#endif
|
55
|
+
;
|
56
|
+
|
57
|
+
py::class_<TimerQueue>(m, "TimerQueue")
|
58
|
+
.def(py::init<ThreadPool*>(), py::arg("executor"))
|
59
|
+
.def("start", py::overload_cast<>(&TimerQueue::start))
|
60
|
+
#ifndef YATQ_DISABLE_PTHREAD
|
61
|
+
.def("start", py::overload_cast<int, yatq::utils::priority_t>(&TimerQueue::start), py::arg("sched_policy"), py::arg("priority") = yatq::utils::max_priority)
|
62
|
+
.def("start", py::overload_cast<int, int>(&TimerQueue::start), py::arg("sched_policy"), py::arg("priority"))
|
63
|
+
#endif
|
64
|
+
.def("stop", &TimerQueue::stop)
|
65
|
+
.def("enqueue", &TimerQueue::enqueue, py::arg("deadline"), py::arg("job"))
|
66
|
+
.def("cancel", &TimerQueue::cancel, py::arg("uid"))
|
67
|
+
.def("clear", &TimerQueue::clear)
|
68
|
+
.def("purge", &TimerQueue::purge)
|
69
|
+
.def("in_queue", &TimerQueue::in_queue, py::arg("uid"));
|
70
|
+
|
71
|
+
m.attr("__version__") = YATQ_VERSION;
|
72
|
+
}
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#ifndef _YATQ_INTERNAL_CONCEPTS_H
|
2
|
+
#define _YATQ_INTERNAL_CONCEPTS_H
|
3
|
+
|
4
|
+
#include <chrono>
|
5
|
+
#include <concepts>
|
6
|
+
|
7
|
+
namespace yatq::internal {
|
8
|
+
|
9
|
+
// template<typename Clock>
|
10
|
+
// concept ClockGeneric = std::chrono::is_clock<Clock>;
|
11
|
+
|
12
|
+
// 'std::chrono::is_clock' seems to be not yet implemented for all architectures
|
13
|
+
// ok, we only these from 'Clock'
|
14
|
+
template<typename Clock>
|
15
|
+
concept ClockGeneric = requires (Clock::time_point t1, Clock::time_point t2) {
|
16
|
+
typename Clock::duration;
|
17
|
+
typename Clock::time_point;
|
18
|
+
{ Clock::now() } -> std::convertible_to<typename Clock::time_point>;
|
19
|
+
{ t1 > t2 } -> std::convertible_to<bool>;
|
20
|
+
{ t1 <= t2 } -> std::convertible_to<bool>;
|
21
|
+
#ifndef YATQ_DISABLE_LOGGING
|
22
|
+
t1.time_since_epoch().count();
|
23
|
+
#endif
|
24
|
+
};
|
25
|
+
|
26
|
+
template<typename Executable>
|
27
|
+
concept ExecutableGeneric = requires {
|
28
|
+
typename Executable::result_type;
|
29
|
+
std::invocable<Executable>;
|
30
|
+
std::movable<Executable>;
|
31
|
+
};
|
32
|
+
|
33
|
+
#ifndef YATQ_DISABLE_FUTURES
|
34
|
+
template<typename Future, typename result_type>
|
35
|
+
concept ChainableFutureGeneric = requires(Future future, void (*then) (Future)) {
|
36
|
+
std::movable<Future>;
|
37
|
+
{ future.get() } -> std::convertible_to<result_type>;
|
38
|
+
future.then(then);
|
39
|
+
};
|
40
|
+
#endif
|
41
|
+
|
42
|
+
template<typename Executor>
|
43
|
+
concept ExecutorGeneric =
|
44
|
+
#ifndef YATQ_DISABLE_FUTURES
|
45
|
+
ChainableFutureGeneric<typename Executor::Future, typename Executor::Executable::result_type> &&
|
46
|
+
#endif
|
47
|
+
requires(Executor executor, Executor::Executable job) {
|
48
|
+
typename Executor::Executable;
|
49
|
+
typename Executor::Executable::result_type;
|
50
|
+
std::movable<typename Executor::Executable>;
|
51
|
+
#ifndef YATQ_DISABLE_FUTURES
|
52
|
+
typename Executor::Future;
|
53
|
+
{ executor.execute(job) } -> std::convertible_to<typename Executor::Future>;
|
54
|
+
#else
|
55
|
+
executor.execute(job);
|
56
|
+
#endif
|
57
|
+
};
|
58
|
+
|
59
|
+
}
|
60
|
+
|
61
|
+
#endif
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#ifndef _YATQ_INTERNAL_LOG4CXX_PROXY_H
|
2
|
+
#define _YATQ_INTERNAL_LOG4CXX_PROXY_H
|
3
|
+
|
4
|
+
#ifndef YATQ_DISABLE_LOGGING
|
5
|
+
|
6
|
+
#include <log4cxx/logger.h>
|
7
|
+
#include <log4cxx/mdc.h>
|
8
|
+
|
9
|
+
#define SET_THREAD_TAG(tag) log4cxx::MDC::put("threadTag", tag)
|
10
|
+
|
11
|
+
#else
|
12
|
+
|
13
|
+
#define LOG4CXX_TRACE(...)
|
14
|
+
#define LOG4CXX_DEBUG(...)
|
15
|
+
#define LOG4CXX_INFO(...)
|
16
|
+
#define LOG4CXX_WARN(...)
|
17
|
+
#define LOG4CXX_ERROR(...)
|
18
|
+
|
19
|
+
#define SET_THREAD_TAG(...)
|
20
|
+
|
21
|
+
#endif
|
22
|
+
|
23
|
+
#endif
|
@@ -0,0 +1,32 @@
|
|
1
|
+
#ifndef _YATQ_INTERNAL_PROMISE_UTILS_H
|
2
|
+
#define _YATQ_INTERNAL_PROMISE_UTILS_H
|
3
|
+
|
4
|
+
#include <type_traits>
|
5
|
+
|
6
|
+
namespace yatq::internal {
|
7
|
+
|
8
|
+
template<typename result_type, typename Job, typename Promise>
|
9
|
+
std::enable_if_t<!std::is_void_v<result_type>> run_and_set_value(Job job, Promise promise) {
|
10
|
+
promise.set_value(job());
|
11
|
+
}
|
12
|
+
|
13
|
+
template<typename result_type, typename Job, typename Promise>
|
14
|
+
std::enable_if_t<std::is_void_v<result_type>> run_and_set_value(Job job, Promise promise) {
|
15
|
+
job();
|
16
|
+
promise.set_value();
|
17
|
+
}
|
18
|
+
|
19
|
+
template<typename result_type, typename Future, typename Promise>
|
20
|
+
std::enable_if_t<!std::is_void_v<result_type>> get_and_set_value(Future future, Promise promise) {
|
21
|
+
promise.set_value(future.get());
|
22
|
+
}
|
23
|
+
|
24
|
+
template<typename result_type, typename Future, typename Promise>
|
25
|
+
std::enable_if_t<std::is_void_v<result_type>> get_and_set_value(Future future, Promise promise) {
|
26
|
+
future.get();
|
27
|
+
promise.set_value();
|
28
|
+
}
|
29
|
+
|
30
|
+
}
|
31
|
+
|
32
|
+
#endif
|
@@ -0,0 +1,156 @@
|
|
1
|
+
#ifndef _YATQ_THREAD_POOL_H
|
2
|
+
#define _YATQ_THREAD_POOL_H
|
3
|
+
|
4
|
+
#include <condition_variable>
|
5
|
+
#include <cstdlib>
|
6
|
+
#include <deque>
|
7
|
+
#include <functional>
|
8
|
+
#include <mutex>
|
9
|
+
#include <string>
|
10
|
+
#include <thread>
|
11
|
+
#include <vector>
|
12
|
+
|
13
|
+
#ifndef YATQ_DISABLE_FUTURES
|
14
|
+
#define BOOST_THREAD_PROVIDES_FUTURE
|
15
|
+
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
|
16
|
+
#include <boost/thread/future.hpp>
|
17
|
+
#endif
|
18
|
+
|
19
|
+
#include "yatq/internal/concepts.h"
|
20
|
+
#include "yatq/internal/promise_utils.h"
|
21
|
+
#include "yatq/internal/log4cxx_proxy.h"
|
22
|
+
|
23
|
+
namespace yatq {
|
24
|
+
|
25
|
+
using internal::ExecutableGeneric;
|
26
|
+
|
27
|
+
template<ExecutableGeneric _Executable = std::function<void(void)>>
|
28
|
+
class ThreadPool {
|
29
|
+
public:
|
30
|
+
using Executable = _Executable;
|
31
|
+
using result_type = Executable::result_type;
|
32
|
+
#ifndef YATQ_DISABLE_FUTURES
|
33
|
+
using Future = boost::future<result_type>;
|
34
|
+
#endif
|
35
|
+
|
36
|
+
private:
|
37
|
+
#ifndef YATQ_DISABLE_FUTURES
|
38
|
+
using Promise = boost::promise<result_type>;
|
39
|
+
#endif
|
40
|
+
|
41
|
+
typedef struct {
|
42
|
+
Executable job;
|
43
|
+
#ifndef YATQ_DISABLE_FUTURES
|
44
|
+
Promise promise;
|
45
|
+
#endif
|
46
|
+
} QueueEntry;
|
47
|
+
|
48
|
+
bool _running;
|
49
|
+
std::mutex _lock;
|
50
|
+
std::condition_variable _cond;
|
51
|
+
std::deque<QueueEntry> _queue;
|
52
|
+
std::vector<std::thread> _pool;
|
53
|
+
|
54
|
+
public:
|
55
|
+
/**
|
56
|
+
* create thread pool
|
57
|
+
*/
|
58
|
+
ThreadPool(): _running(false) {}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* start thread pool
|
62
|
+
* @param num_threads number of threads
|
63
|
+
*/
|
64
|
+
void start(std::size_t num_threads) {
|
65
|
+
if (!_running) {
|
66
|
+
_running = true;
|
67
|
+
for (int i = 0; i < num_threads; ++i) {
|
68
|
+
std::string thread_tag = "pool thread #" + std::to_string(i);
|
69
|
+
auto thread = std::thread(&ThreadPool::thread_routine, this, thread_tag);
|
70
|
+
_pool.push_back(std::move(thread));
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
/**
|
76
|
+
* stop thread pool and join all the threads
|
77
|
+
*/
|
78
|
+
void stop() {
|
79
|
+
if (_running) {
|
80
|
+
_running = false;
|
81
|
+
_cond.notify_all();
|
82
|
+
for (auto&& thread: _pool) {
|
83
|
+
if (thread.joinable()) {
|
84
|
+
thread.join();
|
85
|
+
}
|
86
|
+
}
|
87
|
+
_pool.clear();
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
/**
|
92
|
+
* execute job in a thread
|
93
|
+
* @param job job to execute
|
94
|
+
* @return future object. use it to obtain job result
|
95
|
+
*/
|
96
|
+
#ifndef YATQ_DISABLE_FUTURES
|
97
|
+
Future
|
98
|
+
#else
|
99
|
+
void
|
100
|
+
#endif
|
101
|
+
execute(Executable job) {
|
102
|
+
#ifndef YATQ_DISABLE_FUTURES
|
103
|
+
Promise promise;
|
104
|
+
auto future = promise.get_future();
|
105
|
+
#endif
|
106
|
+
{
|
107
|
+
std::lock_guard<std::mutex> guard(_lock);
|
108
|
+
_queue.push_back({
|
109
|
+
std::move(job)
|
110
|
+
#ifndef YATQ_DISABLE_FUTURES
|
111
|
+
, std::move(promise)
|
112
|
+
#endif
|
113
|
+
});
|
114
|
+
}
|
115
|
+
_cond.notify_one();
|
116
|
+
#ifndef YATQ_DISABLE_FUTURES
|
117
|
+
return std::move(future);
|
118
|
+
#endif
|
119
|
+
}
|
120
|
+
|
121
|
+
private:
|
122
|
+
void thread_routine(const std::string& thread_tag) {
|
123
|
+
#ifndef YATQ_DISABLE_LOGGING
|
124
|
+
static auto logger = log4cxx::Logger::getLogger("yatq.thread_pool");
|
125
|
+
#endif
|
126
|
+
|
127
|
+
SET_THREAD_TAG(thread_tag);
|
128
|
+
LOG4CXX_INFO(logger, "Start");
|
129
|
+
|
130
|
+
while (_running) {
|
131
|
+
QueueEntry queue_entry;
|
132
|
+
{
|
133
|
+
std::unique_lock<std::mutex> guard(_lock);
|
134
|
+
_cond.wait(guard, [this] () { return !_queue.empty() || !_running; });
|
135
|
+
if (!_running) {
|
136
|
+
break;
|
137
|
+
}
|
138
|
+
queue_entry = std::move(_queue.front());
|
139
|
+
_queue.pop_front();
|
140
|
+
}
|
141
|
+
LOG4CXX_TRACE(logger, "Start job");
|
142
|
+
#ifndef YATQ_DISABLE_FUTURES
|
143
|
+
internal::run_and_set_value<result_type>(std::move(queue_entry.job), std::move(queue_entry.promise));
|
144
|
+
#else
|
145
|
+
queue_entry.job();
|
146
|
+
#endif
|
147
|
+
LOG4CXX_TRACE(logger, "Job complete");
|
148
|
+
}
|
149
|
+
|
150
|
+
LOG4CXX_INFO(logger, "Stop");
|
151
|
+
}
|
152
|
+
};
|
153
|
+
|
154
|
+
}
|
155
|
+
|
156
|
+
#endif
|
@@ -0,0 +1,368 @@
|
|
1
|
+
#ifndef _YATQ_TIMER_QUEUE_H
|
2
|
+
#define _YATQ_TIMER_QUEUE_H
|
3
|
+
|
4
|
+
#include <algorithm>
|
5
|
+
#include <chrono>
|
6
|
+
#include <condition_variable>
|
7
|
+
#include <mutex>
|
8
|
+
#include <thread>
|
9
|
+
#include <unordered_map>
|
10
|
+
#include <vector>
|
11
|
+
|
12
|
+
#ifndef YATQ_DISABLE_FUTURES
|
13
|
+
#define BOOST_THREAD_PROVIDES_FUTURE
|
14
|
+
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
|
15
|
+
#include <boost/thread/future.hpp>
|
16
|
+
#endif
|
17
|
+
|
18
|
+
#include "yatq/internal/concepts.h"
|
19
|
+
#include "yatq/internal/promise_utils.h"
|
20
|
+
#include "yatq/internal/log4cxx_proxy.h"
|
21
|
+
#include "yatq/utils/logging_utils.h"
|
22
|
+
#ifndef YATQ_DISABLE_PTHREAD
|
23
|
+
#include "yatq/utils/sched_utils.h"
|
24
|
+
#endif
|
25
|
+
#include "yatq/thread_pool.h"
|
26
|
+
|
27
|
+
namespace yatq {
|
28
|
+
|
29
|
+
using internal::ClockGeneric;
|
30
|
+
using internal::ExecutorGeneric;
|
31
|
+
|
32
|
+
template<ExecutorGeneric _Executor = ThreadPool<>, ClockGeneric _Clock = std::chrono::system_clock>
|
33
|
+
class TimerQueue {
|
34
|
+
public:
|
35
|
+
using Clock = _Clock;
|
36
|
+
using Executor = _Executor;
|
37
|
+
using Executable = Executor::Executable;
|
38
|
+
using result_type = Executable::result_type;
|
39
|
+
#ifndef YATQ_DISABLE_FUTURES
|
40
|
+
using Future = boost::future<result_type>; // NB: doesn't have to match 'Executor::Future'
|
41
|
+
#endif
|
42
|
+
|
43
|
+
using uid_t = unsigned int;
|
44
|
+
|
45
|
+
typedef struct {
|
46
|
+
/**
|
47
|
+
* opaque timer uid. use it to cancel the timer or to check whether it is still in queue
|
48
|
+
*/
|
49
|
+
uid_t uid;
|
50
|
+
/**
|
51
|
+
* scheduled execution timepoint. added for the sake of convenience
|
52
|
+
*/
|
53
|
+
Clock::time_point deadline;
|
54
|
+
#ifndef YATQ_DISABLE_FUTURES
|
55
|
+
/**
|
56
|
+
* future object. use it to obtain job result
|
57
|
+
*/
|
58
|
+
Future result;
|
59
|
+
#endif
|
60
|
+
} TimerHandle;
|
61
|
+
|
62
|
+
private:
|
63
|
+
#ifndef YATQ_DISABLE_FUTURES
|
64
|
+
using Promise = boost::promise<result_type>;
|
65
|
+
#endif
|
66
|
+
|
67
|
+
typedef struct {
|
68
|
+
Executable job;
|
69
|
+
#ifndef YATQ_DISABLE_FUTURES
|
70
|
+
Promise promise;
|
71
|
+
#endif
|
72
|
+
} MapEntry;
|
73
|
+
|
74
|
+
typedef struct {
|
75
|
+
uid_t uid;
|
76
|
+
Clock::time_point deadline;
|
77
|
+
} HeapEntry;
|
78
|
+
|
79
|
+
bool _running;
|
80
|
+
uid_t _next_uid;
|
81
|
+
mutable std::mutex _lock;
|
82
|
+
std::condition_variable _cond;
|
83
|
+
std::unordered_map<uid_t, MapEntry> _jobs;
|
84
|
+
std::vector<HeapEntry> _heap;
|
85
|
+
Executor* const _executor;
|
86
|
+
std::thread _thread;
|
87
|
+
|
88
|
+
public:
|
89
|
+
/**
|
90
|
+
* create timer queue
|
91
|
+
* @param executor raw pointer to the job executor; cannot be \a nullptr. ownership not taken
|
92
|
+
*/
|
93
|
+
explicit TimerQueue(Executor* executor): _running(false), _next_uid(0), _executor(executor) {}
|
94
|
+
|
95
|
+
/**
|
96
|
+
* start timer queue thread with default scheduling parameters
|
97
|
+
*/
|
98
|
+
void start() {
|
99
|
+
if (!_running) {
|
100
|
+
_running = true;
|
101
|
+
_thread = std::thread(&TimerQueue::demux, this);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
#ifndef YATQ_DISABLE_PTHREAD
|
106
|
+
/**
|
107
|
+
* start timer queue thread with specified scheduling policy and priority
|
108
|
+
* @param sched_policy \a SCHED_OTHER | \a SCHED_RR | \a SCHED_FIFO
|
109
|
+
* @param priority \a yatq::utils::max_priority | \a yatq::utils::min_priority
|
110
|
+
*/
|
111
|
+
void start(int sched_policy, utils::priority_t priority = utils::max_priority) {
|
112
|
+
start();
|
113
|
+
utils::set_sched_params(_thread.native_handle(), sched_policy, priority, "timer_queue");
|
114
|
+
}
|
115
|
+
|
116
|
+
/**
|
117
|
+
* start timer queue thread with specified scheduling policy and priority
|
118
|
+
* @param sched_policy \a SCHED_OTHER | \a SCHED_RR | \a SCHED_FIFO
|
119
|
+
* @param priority explicit priority
|
120
|
+
*/
|
121
|
+
void start(int sched_policy, int priority) {
|
122
|
+
start();
|
123
|
+
utils::set_sched_params(_thread.native_handle(), sched_policy, priority, "timer_queue");
|
124
|
+
}
|
125
|
+
#endif
|
126
|
+
|
127
|
+
/**
|
128
|
+
* stop timer queue thread
|
129
|
+
*/
|
130
|
+
void stop() {
|
131
|
+
if (_running) {
|
132
|
+
_running = false;
|
133
|
+
_cond.notify_one();
|
134
|
+
if (_thread.joinable()) {
|
135
|
+
_thread.join();
|
136
|
+
}
|
137
|
+
}
|
138
|
+
}
|
139
|
+
|
140
|
+
/**
|
141
|
+
* add timed job to the queue
|
142
|
+
* @param deadline scheduled execution timepoint
|
143
|
+
* @param job job to execute
|
144
|
+
* @return timer handle to obtain result or cancel
|
145
|
+
*/
|
146
|
+
TimerHandle enqueue(const Clock::time_point& deadline, Executable job) {
|
147
|
+
#ifndef YATQ_DISABLE_LOGGING
|
148
|
+
static auto logger = log4cxx::Logger::getLogger("yatq.timer_queue");
|
149
|
+
#endif
|
150
|
+
|
151
|
+
#ifndef YATQ_DISABLE_FUTURES
|
152
|
+
Promise promise;
|
153
|
+
auto future = promise.get_future();
|
154
|
+
#endif
|
155
|
+
uid_t uid;
|
156
|
+
bool is_first;
|
157
|
+
{
|
158
|
+
std::lock_guard<std::mutex> guard(_lock);
|
159
|
+
uid = _next_uid++;
|
160
|
+
MapEntry map_entry {
|
161
|
+
std::move(job)
|
162
|
+
#ifndef YATQ_DISABLE_FUTURES
|
163
|
+
, std::move(promise)
|
164
|
+
#endif
|
165
|
+
};
|
166
|
+
_jobs.insert(std::make_pair(uid, std::move(map_entry)));
|
167
|
+
_heap.push_back(HeapEntry {uid, deadline});
|
168
|
+
std::push_heap(_heap.begin(), _heap.end(), TimerQueue::heap_cmp);
|
169
|
+
is_first = (_heap[0].uid == uid);
|
170
|
+
}
|
171
|
+
if (is_first) {
|
172
|
+
_cond.notify_one();
|
173
|
+
}
|
174
|
+
LOG4CXX_DEBUG(logger, "New timer uid=" + std::to_string(uid));
|
175
|
+
return {
|
176
|
+
uid
|
177
|
+
, deadline
|
178
|
+
#ifndef YATQ_DISABLE_FUTURES
|
179
|
+
, std::move(future)
|
180
|
+
#endif
|
181
|
+
};
|
182
|
+
}
|
183
|
+
|
184
|
+
/**
|
185
|
+
* cancel timed job
|
186
|
+
* @param uid timer uid
|
187
|
+
* @return \a true if timer was present in the queue; \a false otherwise
|
188
|
+
*/
|
189
|
+
bool cancel(uid_t uid) {
|
190
|
+
#ifndef YATQ_DISABLE_LOGGING
|
191
|
+
static auto logger = log4cxx::Logger::getLogger("yatq.timer_queue");
|
192
|
+
#endif
|
193
|
+
|
194
|
+
bool was_removed;
|
195
|
+
bool was_first;
|
196
|
+
{
|
197
|
+
std::lock_guard<std::mutex> guard(_lock);
|
198
|
+
auto i = _jobs.find(uid);
|
199
|
+
if (i != _jobs.end()) {
|
200
|
+
LOG4CXX_DEBUG(logger, "Canceling timer uid=" + std::to_string(uid));
|
201
|
+
_jobs.erase(i);
|
202
|
+
was_removed = true;
|
203
|
+
was_first = (_heap[0].uid == uid);
|
204
|
+
}
|
205
|
+
else {
|
206
|
+
was_removed = false;
|
207
|
+
}
|
208
|
+
}
|
209
|
+
if (was_removed && was_first) {
|
210
|
+
_cond.notify_one();
|
211
|
+
}
|
212
|
+
return was_removed;
|
213
|
+
}
|
214
|
+
|
215
|
+
/**
|
216
|
+
* delete all jobs from the queue
|
217
|
+
*/
|
218
|
+
void clear() {
|
219
|
+
#ifndef YATQ_DISABLE_LOGGING
|
220
|
+
static auto logger = log4cxx::Logger::getLogger("yatq.timer_queue");
|
221
|
+
#endif
|
222
|
+
|
223
|
+
std::size_t total_jobs;
|
224
|
+
std::size_t total_timers;
|
225
|
+
{
|
226
|
+
std::lock_guard<std::mutex> guard(_lock);
|
227
|
+
total_jobs = _jobs.size();
|
228
|
+
_jobs.clear();
|
229
|
+
total_timers = _heap.size();
|
230
|
+
_heap.clear();
|
231
|
+
}
|
232
|
+
if (total_jobs > 0) {
|
233
|
+
_cond.notify_one();
|
234
|
+
}
|
235
|
+
auto canceled_timers = total_timers - total_jobs;
|
236
|
+
LOG4CXX_DEBUG(logger, "Cleared " + std::to_string(total_jobs) + " timers and " + std::to_string(canceled_timers) + " canceled timers");
|
237
|
+
}
|
238
|
+
|
239
|
+
/**
|
240
|
+
* delete all canceled timers from the queue
|
241
|
+
*/
|
242
|
+
void purge() {
|
243
|
+
#ifndef YATQ_DISABLE_LOGGING
|
244
|
+
static auto logger = log4cxx::Logger::getLogger("yatq.timer_queue");
|
245
|
+
#endif
|
246
|
+
|
247
|
+
std::size_t canceled_timers;
|
248
|
+
{
|
249
|
+
std::lock_guard<std::mutex> guard(_lock);
|
250
|
+
auto total_jobs = _jobs.size();
|
251
|
+
auto total_timers = _heap.size();
|
252
|
+
canceled_timers = total_timers - total_jobs;
|
253
|
+
if (total_timers > total_jobs) {
|
254
|
+
std::vector<HeapEntry> heap(total_jobs);
|
255
|
+
auto i = heap.begin();
|
256
|
+
for (auto&& heap_entry: _heap) {
|
257
|
+
if (_jobs.contains(heap_entry.uid)) {
|
258
|
+
*i++ = std::move(heap_entry);
|
259
|
+
}
|
260
|
+
else {
|
261
|
+
LOG4CXX_DEBUG(logger, "Timer uid=" + std::to_string(heap_entry.uid) + " has been canceled");
|
262
|
+
}
|
263
|
+
}
|
264
|
+
std::make_heap(heap.begin(), heap.end(), TimerQueue::heap_cmp);
|
265
|
+
_heap.swap(heap);
|
266
|
+
}
|
267
|
+
}
|
268
|
+
// NB: 'demux()' never waits on a canceled timer => no need to notify
|
269
|
+
LOG4CXX_DEBUG(logger, "Purged " + std::to_string(canceled_timers) + " canceled timers");
|
270
|
+
}
|
271
|
+
|
272
|
+
/**
|
273
|
+
* check whether a job is still in the queue
|
274
|
+
* @param uid timer uid
|
275
|
+
*/
|
276
|
+
bool in_queue(uid_t uid) const {
|
277
|
+
std::lock_guard<std::mutex> guard(_lock);
|
278
|
+
return _jobs.contains(uid);
|
279
|
+
}
|
280
|
+
|
281
|
+
private:
|
282
|
+
static bool heap_cmp(const HeapEntry& lhs, const HeapEntry& rhs) {
|
283
|
+
return lhs.deadline > rhs.deadline; // NB: '>'
|
284
|
+
}
|
285
|
+
|
286
|
+
void demux() {
|
287
|
+
#ifndef YATQ_DISABLE_LOGGING
|
288
|
+
static auto logger = log4cxx::Logger::getLogger("yatq.timer_queue");
|
289
|
+
#endif
|
290
|
+
|
291
|
+
SET_THREAD_TAG("timer_queue");
|
292
|
+
LOG4CXX_INFO(logger, "Start");
|
293
|
+
|
294
|
+
std::unique_lock<std::mutex> guard(_lock);
|
295
|
+
while (_running) {
|
296
|
+
bool deadline_expired = false;
|
297
|
+
while (!_heap.empty()) {
|
298
|
+
auto current_uid = _heap[0].uid;
|
299
|
+
auto i = _jobs.find(current_uid);
|
300
|
+
if (i == _jobs.end()) {
|
301
|
+
LOG4CXX_DEBUG(logger, "Timer uid=" + std::to_string(current_uid) + " has been canceled");
|
302
|
+
std::pop_heap(_heap.begin(), _heap.end(), TimerQueue::heap_cmp);
|
303
|
+
_heap.pop_back();
|
304
|
+
deadline_expired = false;
|
305
|
+
continue;
|
306
|
+
}
|
307
|
+
if (!deadline_expired) {
|
308
|
+
auto now = Clock::now(); // NB: system call => context switch
|
309
|
+
deadline_expired = (_heap[0].deadline <= now);
|
310
|
+
}
|
311
|
+
if (deadline_expired) {
|
312
|
+
LOG4CXX_DEBUG(logger, "Executing timer uid=" + std::to_string(current_uid));
|
313
|
+
auto node = _jobs.extract(i);
|
314
|
+
std::pop_heap(_heap.begin(), _heap.end(), TimerQueue::heap_cmp);
|
315
|
+
_heap.pop_back();
|
316
|
+
auto map_entry = std::move(node.mapped());
|
317
|
+
|
318
|
+
guard.unlock();
|
319
|
+
#ifndef YATQ_DISABLE_FUTURES
|
320
|
+
auto future =
|
321
|
+
#endif
|
322
|
+
_executor->execute(std::move(map_entry.job));
|
323
|
+
guard.lock();
|
324
|
+
|
325
|
+
#ifndef YATQ_DISABLE_FUTURES
|
326
|
+
future.then( // future chaining -- this is why we use 'boost::future' instead of 'std::future'
|
327
|
+
boost::launch::sync, // FIXME: does not match the concept
|
328
|
+
[promise = std::move(map_entry.promise)]
|
329
|
+
(Executor::Future future) mutable
|
330
|
+
{ internal::get_and_set_value<result_type>(std::move(future), std::move(promise)); }
|
331
|
+
);
|
332
|
+
#endif
|
333
|
+
deadline_expired = false;
|
334
|
+
}
|
335
|
+
else {
|
336
|
+
// NB: without this explicit cast duration type may be deduced incorrectly
|
337
|
+
// on Linux this leads to waiting for a random time point
|
338
|
+
std::chrono::time_point<Clock, typename Clock::duration> deadline = _heap[0].deadline;
|
339
|
+
LOG4CXX_TRACE(logger, "Wait until " + utils::time_point_to_string(deadline));
|
340
|
+
bool notified = _cond.wait_until(
|
341
|
+
guard,
|
342
|
+
deadline,
|
343
|
+
[this, current_uid] () {
|
344
|
+
return !_jobs.contains(current_uid) || (_heap[0].uid != current_uid) || !_running;
|
345
|
+
}
|
346
|
+
);
|
347
|
+
LOG4CXX_TRACE(logger, "Wake-up");
|
348
|
+
if (!_running) {
|
349
|
+
LOG4CXX_WARN(logger, "Stopping timer queue with unprocessed timers");
|
350
|
+
return;
|
351
|
+
}
|
352
|
+
if (!notified) { // => timeout
|
353
|
+
deadline_expired = true;
|
354
|
+
}
|
355
|
+
}
|
356
|
+
}
|
357
|
+
LOG4CXX_TRACE(logger, "Wait");
|
358
|
+
_cond.wait(guard, [this] () { return !_heap.empty() || !_running; });
|
359
|
+
LOG4CXX_TRACE(logger, "Wake-up");
|
360
|
+
}
|
361
|
+
|
362
|
+
LOG4CXX_INFO(logger, "Stop");
|
363
|
+
}
|
364
|
+
};
|
365
|
+
|
366
|
+
}
|
367
|
+
|
368
|
+
#endif
|
@@ -0,0 +1,51 @@
|
|
1
|
+
#ifndef _YATQ_UTILS_LOGGING_UTILS_H
|
2
|
+
#define _YATQ_UTILS_LOGGING_UTILS_H
|
3
|
+
|
4
|
+
#include <chrono>
|
5
|
+
#include <iomanip>
|
6
|
+
#include <locale>
|
7
|
+
#include <sstream>
|
8
|
+
|
9
|
+
#include <sched.h>
|
10
|
+
|
11
|
+
namespace yatq::utils {
|
12
|
+
|
13
|
+
template<typename time_point>
|
14
|
+
std::string time_point_to_string(const time_point& t) {
|
15
|
+
return std::to_string(t.time_since_epoch().count());
|
16
|
+
}
|
17
|
+
|
18
|
+
template<>
|
19
|
+
inline std::string time_point_to_string(const std::chrono::system_clock::time_point& time_point) {
|
20
|
+
std::stringstream stream;
|
21
|
+
|
22
|
+
std::time_t timestamp = std::chrono::system_clock::to_time_t(time_point);
|
23
|
+
std::tm* tm_info_p = std::localtime(×tamp); // static initialization, no need for memory management
|
24
|
+
stream << std::put_time(tm_info_p, "%F %T");
|
25
|
+
|
26
|
+
// locale-aware decimal point
|
27
|
+
static const char decimal_point = std::use_facet<std::numpunct<char>>(std::locale("")).decimal_point();
|
28
|
+
|
29
|
+
auto time_point_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(time_point);
|
30
|
+
auto milliseconds = time_point_ms.time_since_epoch().count() % 1000;
|
31
|
+
stream << decimal_point << std::setfill('0') << std::setw(3) << milliseconds;
|
32
|
+
|
33
|
+
return stream.str();
|
34
|
+
}
|
35
|
+
|
36
|
+
inline std::string sched_policy_to_string(int sched_policy) {
|
37
|
+
switch (sched_policy) {
|
38
|
+
case SCHED_OTHER:
|
39
|
+
return "SCHED_OTHER";
|
40
|
+
case SCHED_FIFO:
|
41
|
+
return "SCHED_FIFO";
|
42
|
+
case SCHED_RR:
|
43
|
+
return "SCHED_RR";
|
44
|
+
default:
|
45
|
+
return std::to_string(sched_policy);
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
}
|
50
|
+
|
51
|
+
#endif
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#ifndef _YATQ_UTILS_SCHED_UTILS_H
|
2
|
+
#define _YATQ_UTILS_SCHED_UTILS_H
|
3
|
+
|
4
|
+
#include <cerrno>
|
5
|
+
#include <string>
|
6
|
+
|
7
|
+
#include <pthread.h>
|
8
|
+
|
9
|
+
#include "yatq/internal/log4cxx_proxy.h"
|
10
|
+
#include "yatq/utils/logging_utils.h"
|
11
|
+
|
12
|
+
namespace yatq::utils {
|
13
|
+
|
14
|
+
typedef enum {min_priority = 0, max_priority = -1} priority_t;
|
15
|
+
|
16
|
+
inline bool set_sched_params(pthread_t handle, int sched_policy, int priority, const std::string& thread_tag = "unspecified") {
|
17
|
+
#ifndef YATQ_DISABLE_LOGGING
|
18
|
+
static auto logger = log4cxx::Logger::getLogger("yatq.utils.sched");
|
19
|
+
#endif
|
20
|
+
|
21
|
+
int prev_sched_policy; // Linux implementation crashes on 'nullptr'
|
22
|
+
sched_param sched;
|
23
|
+
pthread_getschedparam(handle, &prev_sched_policy, &sched);
|
24
|
+
sched.sched_priority = priority;
|
25
|
+
if (pthread_setschedparam(handle, sched_policy, &sched) == 0) {
|
26
|
+
LOG4CXX_INFO(logger, "Set sched params thread=" + thread_tag + " policy=" + sched_policy_to_string(sched_policy) + " priority=" + std::to_string(priority));
|
27
|
+
return true;
|
28
|
+
}
|
29
|
+
else {
|
30
|
+
LOG4CXX_WARN(logger, "Failed to set sched params thread=" + thread_tag + ": " + std::string(std::strerror(errno)));
|
31
|
+
return false;
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
inline bool set_sched_params(pthread_t handle, int sched_policy, priority_t priority_tag, const std::string& thread_tag = "unspecified") {
|
36
|
+
int priority;
|
37
|
+
switch (priority_tag) {
|
38
|
+
case min_priority:
|
39
|
+
priority = sched_get_priority_min(sched_policy);
|
40
|
+
break;
|
41
|
+
case max_priority:
|
42
|
+
priority = sched_get_priority_max(sched_policy);
|
43
|
+
break;
|
44
|
+
}
|
45
|
+
return set_sched_params(handle, sched_policy, priority, thread_tag);
|
46
|
+
}
|
47
|
+
|
48
|
+
}
|
49
|
+
|
50
|
+
#endif
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#ifndef _YATQ_BINDINGS_PYTHON_YATQ_TRAITS_H
|
2
|
+
#define _YATQ_BINDINGS_PYTHON_YATQ_TRAITS_H
|
3
|
+
|
4
|
+
#define PYBIND11_DETAILED_ERROR_MESSAGES
|
5
|
+
#include <pybind11/pybind11.h>
|
6
|
+
|
7
|
+
#define BOOST_THREAD_PROVIDES_FUTURE
|
8
|
+
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
|
9
|
+
#include <boost/thread/future.hpp>
|
10
|
+
|
11
|
+
namespace pybind11::detail {
|
12
|
+
|
13
|
+
// only needed for 'result_type = void'
|
14
|
+
template <>
|
15
|
+
struct is_move_constructible<boost::future<void>>: std::true_type {};
|
16
|
+
|
17
|
+
}
|
18
|
+
|
19
|
+
#endif
|
@@ -0,0 +1,19 @@
|
|
1
|
+
[build-system]
|
2
|
+
requires = ["setuptools >= 65.0.0", "pybind11 >= 2.13.6", "wheel >= 0.44.0"]
|
3
|
+
build-backend = "setuptools.build_meta"
|
4
|
+
|
5
|
+
[project]
|
6
|
+
name = "pytq-cxx"
|
7
|
+
version = "1.0.2"
|
8
|
+
authors = [{name="Nikita Vaganov", email="nikita.e.vaganov@gmail.com"}]
|
9
|
+
description = "single-threaded timer demultiplexer backed by C++ implementation"
|
10
|
+
readme = "README_pytq.md"
|
11
|
+
classifiers = [
|
12
|
+
"Programming Language :: Python :: 3",
|
13
|
+
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
|
14
|
+
]
|
15
|
+
requires-python = ">=3.7"
|
16
|
+
|
17
|
+
[project.urls]
|
18
|
+
"Homepage" = "https://github.com/vaganov/yatq"
|
19
|
+
"Bug Tracker" = "https://github.com/vaganov/yatq/issues"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import asyncio
|
2
|
+
|
3
|
+
|
4
|
+
__all__ = ['AwaitableFuture']
|
5
|
+
|
6
|
+
|
7
|
+
class AwaitableFuture:
|
8
|
+
def __init__(self, future):
|
9
|
+
self._future = future
|
10
|
+
|
11
|
+
def __await__(self):
|
12
|
+
while not self._future.is_ready():
|
13
|
+
yield
|
14
|
+
return self._future.get() # NB: return, not yield
|
15
|
+
|
16
|
+
async def get(self):
|
17
|
+
while not self._future.is_ready():
|
18
|
+
await asyncio.sleep(0)
|
19
|
+
return self._future.get()
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: pytq-cxx
|
3
|
+
Version: 1.0.2
|
4
|
+
Summary: single-threaded timer demultiplexer backed by C++ implementation
|
5
|
+
Author-email: Nikita Vaganov <nikita.e.vaganov@gmail.com>
|
6
|
+
Project-URL: Homepage, https://github.com/vaganov/yatq
|
7
|
+
Project-URL: Bug Tracker, https://github.com/vaganov/yatq/issues
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)
|
10
|
+
Requires-Python: >=3.7
|
11
|
+
Description-Content-Type: text/markdown
|
12
|
+
|
13
|
+
# pytq-cxx
|
14
|
+
**pytq-cxx** is a _python_ wrapper for **yatq** -- a _C++_ template library. For the documentation and examples please
|
15
|
+
see [yatq homepage](https://github.com/vaganov/yatq)
|
16
|
+
|
17
|
+
**NB:** This package has nothing to do with [pytq package](https://pypi.org/project/pytq)
|
18
|
+
|
19
|
+
**NB:** Module name is, however, **pytq**
|
20
|
+
|
21
|
+
from pytq import ThreadPool, TimerQueue, AwaitableFuture
|
@@ -0,0 +1,22 @@
|
|
1
|
+
MANIFEST.in
|
2
|
+
README_pytq.md
|
3
|
+
pyproject.toml
|
4
|
+
setup.py
|
5
|
+
_yatq/bind.cpp
|
6
|
+
_yatq/traits.h
|
7
|
+
_yatq/include/yatq/thread_pool.h
|
8
|
+
_yatq/include/yatq/timer_queue.h
|
9
|
+
_yatq/include/yatq/version.h
|
10
|
+
_yatq/include/yatq/internal/concepts.h
|
11
|
+
_yatq/include/yatq/internal/log4cxx_proxy.h
|
12
|
+
_yatq/include/yatq/internal/promise_utils.h
|
13
|
+
_yatq/include/yatq/utils/logging_utils.h
|
14
|
+
_yatq/include/yatq/utils/sched_utils.h
|
15
|
+
pytq/__init__.py
|
16
|
+
pytq/awaitable_future.py
|
17
|
+
pytq_cxx.egg-info/PKG-INFO
|
18
|
+
pytq_cxx.egg-info/SOURCES.txt
|
19
|
+
pytq_cxx.egg-info/dependency_links.txt
|
20
|
+
pytq_cxx.egg-info/top_level.txt
|
21
|
+
setup_helpers/find_dep_headers.cmake
|
22
|
+
setup_helpers/find_dep_libs.cmake
|
@@ -0,0 +1 @@
|
|
1
|
+
|
pytq_cxx-1.0.2/setup.cfg
ADDED
pytq_cxx-1.0.2/setup.py
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
import subprocess
|
3
|
+
import sys
|
4
|
+
|
5
|
+
from setuptools import setup
|
6
|
+
from pybind11.setup_helpers import Pybind11Extension, build_ext
|
7
|
+
|
8
|
+
|
9
|
+
def ugly_helper(): # https://github.com/vaganov/yatq/issues/2
|
10
|
+
dirpath = str(Path(__file__).parent / 'setup_helpers')
|
11
|
+
|
12
|
+
args = ['cmake', '-P', 'find_dep_headers.cmake']
|
13
|
+
result = subprocess.run(args, cwd=dirpath, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
14
|
+
if result.returncode != 0:
|
15
|
+
print(result.stderr.decode(), end='', file=sys.stderr, flush=True)
|
16
|
+
raise RuntimeError(f"{' '.join(args)} returned {result.returncode}")
|
17
|
+
|
18
|
+
_inc_dirs = set(line.strip() for line in result.stderr.decode().split()) # sic! 'stderr'
|
19
|
+
inc_dirs = list(_inc_dirs)
|
20
|
+
|
21
|
+
args = ['cmake', '-P', 'find_dep_libs.cmake']
|
22
|
+
result = subprocess.run(args, cwd=dirpath, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
23
|
+
if result.returncode != 0:
|
24
|
+
print(result.stderr.decode(), end='', file=sys.stderr, flush=True)
|
25
|
+
raise RuntimeError(f"{' '.join(args)} returned {result.returncode}")
|
26
|
+
|
27
|
+
lib_prefix = 'lib'
|
28
|
+
lib_prefix_len = len(lib_prefix)
|
29
|
+
|
30
|
+
_lib_dirs = set()
|
31
|
+
libs = []
|
32
|
+
for line in result.stderr.decode().split(): # sic! 'stderr'
|
33
|
+
path = Path(line.strip())
|
34
|
+
_lib_dirs.add(str(path.parent))
|
35
|
+
libname = path.stem
|
36
|
+
if libname.startswith(lib_prefix):
|
37
|
+
libname = libname[lib_prefix_len:]
|
38
|
+
libs.append(libname)
|
39
|
+
lib_dirs = list(_lib_dirs)
|
40
|
+
|
41
|
+
return {'inc_dirs': inc_dirs, 'lib_dirs': lib_dirs, 'libs': libs}
|
42
|
+
|
43
|
+
|
44
|
+
HELPER_FLAGS = ugly_helper()
|
45
|
+
|
46
|
+
|
47
|
+
setup(
|
48
|
+
packages=['pytq'],
|
49
|
+
ext_modules=[
|
50
|
+
Pybind11Extension(
|
51
|
+
'_yatq',
|
52
|
+
['_yatq/bind.cpp'],
|
53
|
+
include_dirs=HELPER_FLAGS['inc_dirs'] + ['_yatq/include'], # https://github.com/vaganov/yatq/issues/4
|
54
|
+
library_dirs=HELPER_FLAGS['lib_dirs'],
|
55
|
+
libraries=HELPER_FLAGS['libs'],
|
56
|
+
cxx_std=20,
|
57
|
+
),
|
58
|
+
],
|
59
|
+
cmdclass=dict(build_ext=build_ext),
|
60
|
+
)
|
@@ -0,0 +1,9 @@
|
|
1
|
+
#! cmake -P
|
2
|
+
cmake_minimum_required(VERSION 3.22)
|
3
|
+
|
4
|
+
set(INCLUDE_PATHS /usr/include /usr/local/include)
|
5
|
+
|
6
|
+
find_path(boost_include_dir boost PATHS ${INCLUDE_PATHS} REQUIRED)
|
7
|
+
message("${boost_include_dir}")
|
8
|
+
find_path(log4cxx_include_dir log4cxx PATHS ${INCLUDE_PATHS} REQUIRED)
|
9
|
+
message("${log4cxx_include_dir}")
|
@@ -0,0 +1,9 @@
|
|
1
|
+
#! cmake -P
|
2
|
+
cmake_minimum_required(VERSION 3.22)
|
3
|
+
|
4
|
+
set(LIB_PATHS /lib /lib/x86_64-linux-gnu /usr/lib /usr/local/lib)
|
5
|
+
|
6
|
+
find_library(boost_thread_lib NAMES boost_thread boost_thread-mt PATHS ${LIB_PATHS} REQUIRED)
|
7
|
+
message("${boost_thread_lib}")
|
8
|
+
find_library(log4cxx_lib log4cxx PATHS ${LIB_PATHS} REQUIRED)
|
9
|
+
message("${log4cxx_lib}")
|