lock {m_mutex};
113 |
114 | m_condition.wait(lock, [this]{return !this->empty() || m_stopDequeuing;});
115 |
116 | if (m_stopDequeuing) return {};
117 |
118 | auto job = std::move(this->front());
119 | this->pop();
120 |
121 | return job;
122 | }
123 |
124 | /**
125 | * Force stopping dequeuing
126 | */
127 | void stop() noexcept
128 | {
129 | m_stopDequeuing = true;
130 |
131 | m_condition.notify_one();
132 | }
133 |
134 |
135 | private:
136 |
137 | bool m_stopDequeuing = false;
138 |
139 | std::mutex m_mutex {};//neither copyable, nor movable
140 | std::condition_variable m_condition {};//neither copyable, nor movable
141 |
142 | };
143 |
144 | }
145 |
146 |
147 | #endif /* AOT_JOBQUEUE_H_ */
148 |
--------------------------------------------------------------------------------
/src/AOT/java/AOThread.java:
--------------------------------------------------------------------------------
1 | /**
2 | * @author: Damir Ljubic
3 | * @email: damirlj@yahoo.com
4 | * All rights reserved!
5 | */
6 | package ;
7 |
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.Optional;
11 | import java.util.concurrent.Callable;
12 | import java.util.concurrent.CompletableFuture;
13 | import java.util.concurrent.ExecutorService;
14 | import java.util.concurrent.Executors;
15 | import java.util.concurrent.TimeUnit;
16 |
17 | public final class AOThread {
18 |
19 | // Single-thread execution context
20 | private final ExecutorService executionContext = Executors.newSingleThreadExecutor();
21 | private final Looper looper = new Looper();
22 |
23 | /** Start the AOT: Attach the Looper with the execution context */
24 | public void start() {
25 | executionContext.submit(looper.getLooper());
26 | }
27 |
28 | @FunctionalInterface
29 | private interface IStopThread {
30 | void stop(@NotNull ExecutorService executor);
31 | }
32 |
33 | /** Stop thread which drains the message queue */
34 | private void stop(@NotNull IStopThread callback) throws InterruptedException {
35 | looper.stop(); // set the exit flag
36 | callback.stop(executionContext);
37 | }
38 |
39 | /**
40 | * Try to stop the background AOT gracefully, by waiting on the last task completion
41 | *
42 | * @param timeout The timeout to wait for
43 | * @param unit The time unit for the timeout to be expressed with
44 | */
45 | public void stop(long timeout, TimeUnit unit) throws InterruptedException {
46 | stop(
47 | executor -> {
48 | executor.shutdown(); // initiate shutdown
49 | try {
50 | if (!executor.awaitTermination(timeout, unit)) {
51 | executor.shutdownNow();
52 | }
53 | } catch (InterruptedException e) {
54 | executor.shutdownNow();
55 | Thread.currentThread().interrupt();
56 | }
57 | });
58 | }
59 |
60 | /**
61 | * Stop the AOT background thread immediately, without waiting on the task completion
62 | *
63 | * @note There is no guarantees that the last running task will be successfully interrupted
64 | */
65 | public void stopNow() throws InterruptedException {
66 | stop(ExecutorService::shutdownNow);
67 | }
68 |
69 | private void submit(@NotNull Runnable task) throws InterruptedException {
70 | looper.submit(task);
71 | }
72 |
73 | /**
74 | * Submit the task to the Looper (job-queue).
75 | * The client will be synchronized on the result of the execution.
76 | *
77 | * @param job The task to be enqueued
78 | * @return The future object - for synchronizing on the execution outcome
79 | * @param The result type
80 | */
81 | @NotNull
82 | public Optional> enqueueWithResult(@NotNull Callable job) {
83 | CompletableFuture f = new CompletableFuture<>();
84 |
85 | // Wrap the callable into parameterless runnable task
86 | Runnable task =
87 | () -> {
88 | try {
89 | R result = job.call();
90 | f.complete(result); // signal the result to client
91 | } catch (Exception e) {
92 | f.completeExceptionally(e); // or signal the exception to client
93 | }
94 | };
95 |
96 | try {
97 | submit(task);
98 | } catch (InterruptedException e) {
99 | Thread.currentThread().interrupt();
100 | return Optional.empty();
101 | }
102 |
103 | return Optional.of(f);
104 | }
105 |
106 | @FunctionalInterface
107 | public interface IJob {
108 | void execute() throws Exception;
109 | }
110 |
111 | /**
112 | * Submit the job to the Looper.
113 | * Fire-and-forget: client will not wait on the outcome of execution, if any.
114 | *
115 | * @param job The task to be executed
116 | */
117 | public void enqueue(@NotNull IJob job) {
118 | Runnable task = ()->{
119 | try {
120 | job.execute();
121 | } catch(Exception e) {
122 | e.printStackTrace(); // your own logging
123 | }
124 | };
125 |
126 | try {
127 | submit(task);
128 | } catch (InterruptedException e) {
129 | Thread.currentThread().interrupt();
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/AOT/java/Looper.java:
--------------------------------------------------------------------------------
1 | /**
2 | * @author: Damir Ljubic
3 | * @email: damirlj@yahoo.com
4 | * All rights reserved!
5 | */
6 | package ;
7 |
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.concurrent.BlockingQueue;
11 | import java.util.concurrent.LinkedBlockingQueue;
12 |
13 | public final class Looper {
14 | // Thread-safe tasks queue
15 | private final BlockingQueue queue = new LinkedBlockingQueue<>();
16 |
17 | private static final Runnable STOPPING_TASK = () -> {};
18 |
19 | /** For stopping the Looper */
20 | public void stop() throws InterruptedException {
21 | queue.put(STOPPING_TASK); // to unblock the queue and terminate gracefully
22 | }
23 |
24 | /** Looper drains the queue, and executes the tasks in order of reception (FIFO) */
25 | private final Runnable looper =
26 | () -> {
27 | for (; ;) {
28 | try {
29 | Runnable task = queue.take(); // blocking call
30 | if (task == STOPPING_TASK) break;
31 | task.run();
32 | } catch (InterruptedException e) {
33 | Thread.currentThread().interrupt(); // set the interrupt flag to "true"
34 | }
35 | }
36 | System.out.println(" Looper"); // your own logging
37 | };
38 |
39 | @NotNull
40 | public Runnable getLooper() {
41 | return looper;
42 | }
43 |
44 | public void submit(@NotNull Runnable task) throws InterruptedException {
45 | queue.put(task);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/AOT/java/TestAOT.java:
--------------------------------------------------------------------------------
1 | /**
2 | * @author: Damir Ljubic
3 | * @email: damirlj@yahoo.com
4 | * All rights reserved!
5 | */
6 | package ;
7 |
8 | import androidx.annotation.NonNull;
9 |
10 | import org.jetbrains.annotations.NotNull;
11 | import org.junit.Test;
12 |
13 | import java.util.ArrayList;
14 | import java.util.List;
15 | import java.util.Optional;
16 | import java.util.concurrent.Callable;
17 | import java.util.concurrent.CompletableFuture;
18 | import java.util.concurrent.ExecutionException;
19 | import java.util.concurrent.Future;
20 | import java.util.concurrent.TimeUnit;
21 | import java.util.function.Consumer;
22 |
23 | public class TestAOT {
24 |
25 | @NonNull
26 | private List> submitTasks_withResults(
27 | @NotNull List> callables, @NotNull AOThread aot) {
28 | List> results = new ArrayList<>(callables.size());
29 | for (Callable callable : callables) {
30 | aot.enqueueWithResult(callable).map(results::add);
31 | }
32 | return results;
33 | }
34 |
35 | private void fetchResultOrException(
36 | @NonNull Future result, @NotNull Consumer callback) {
37 | try {
38 | callback.accept(result.get()); // blocking call: wait on result being set
39 | } catch (ExecutionException | InterruptedException e) { // or exception being thrown
40 | e.printStackTrace();
41 | }
42 | }
43 |
44 | private void syncOnResults(@NotNull List> results, @NotNull Consumer callback) {
45 | for (Future result : results) {
46 | fetchResultOrException(result, callback);
47 | }
48 | }
49 |
50 | @NotNull
51 | private Callable createCallable(long timeout) {
52 | return () -> {
53 | Thread.sleep(timeout); // simulate some work
54 | return String.format( // it's always hosted by the same thread - execution context
55 | "(thread: %s): Sleeping for %d[ms]", Thread.currentThread().getName(), timeout);
56 | };
57 | }
58 |
59 | @Test
60 | public void test_AOTWaitingOnResults() throws InterruptedException {
61 | final AOThread aot = new AOThread();
62 | aot.start();
63 |
64 | Thread client =
65 | new Thread(
66 | () -> {
67 | final List> callables =
68 | List.of(createCallable(200L), createCallable(100L), createCallable(300L));
69 | final List> results = submitTasks_withResults(callables, aot);
70 | // Client: synchronized on the result
71 | syncOnResults(results, System.out::println);
72 | },
73 | "t_client");
74 |
75 | client.start();
76 | client.join();
77 |
78 | aot.stopNow();
79 | }
80 |
81 | @NotNull
82 | private AOThread.IJob createJob(long timeout) {
83 | return () -> {
84 | Thread.sleep(timeout); // simulate some work
85 | System.out.printf(
86 | "Executing within thread: \"%s\", after %d[ms]\n",
87 | Thread.currentThread().getName(), timeout);
88 | };
89 | }
90 |
91 | @Test
92 | public void test_AOTWithoutWaitingOnResults() throws InterruptedException {
93 |
94 | final AOThread aot = new AOThread();
95 | aot.start();
96 |
97 | Thread client =
98 | new Thread(
99 | () -> {
100 | final List callables = List.of(createJob(200L), createJob(100L));
101 | for (AOThread.IJob callable : callables) {
102 | aot.enqueue(callable);
103 | try {
104 | Thread.sleep(1000);
105 | } catch (InterruptedException e) {
106 | e.printStackTrace();
107 | }
108 | }
109 | },
110 | "t_client");
111 |
112 | client.start();
113 | client.join();
114 |
115 | aot.stop(1, TimeUnit.SECONDS);
116 | }
117 |
118 | @Test
119 | public void test_bothOfThem() throws InterruptedException {
120 | final AOThread aot = new AOThread();
121 | aot.start();
122 |
123 | Thread client =
124 | new Thread(
125 | () -> {
126 | // Submit, without waiting
127 | aot.enqueue(createJob(100L));
128 | aot.enqueue(() -> {}); // this will not brake the looper
129 |
130 | // Wait on result, or exception being set
131 | Optional> opFut =
132 | aot.enqueueWithResult(createCallable(200L));
133 | opFut.ifPresent(
134 | fut ->
135 | fetchResultOrException(
136 | fut,
137 | res ->
138 | System.out.printf(
139 | "(Thread: \"%s\") Receiving result: %s\n",
140 | Thread.currentThread().getName(), res)));
141 | },
142 | "t_client");
143 |
144 | client.start();
145 | client.join();
146 |
147 | aot.stopNow();
148 | }
149 | }
150 |
--------------------------------------------------------------------------------
/src/Event/Event.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2023. All rights reserved!
4 | //
5 |
6 | #include "Event.h"
7 |
8 | Event::Event(bool autoReset) noexcept
9 | : m_autoReset(autoReset)
10 | {}
11 |
12 | Event::~Event() = default;
13 |
14 | namespace
15 | {
16 | inline void updateWaitingThreads(Event::waiting_threads_t& waitingThreads)
17 | {
18 | waitingThreads.erase(
19 | std::remove(waitingThreads.begin(), waitingThreads.end(), std::this_thread::get_id()),
20 | waitingThreads.end());
21 | }
22 | } // namespace
23 |
24 | Event::event_wait_t Event::wait_for(std::chrono::milliseconds timeout)
25 | {
26 | std::unique_lock lock{m_lock};
27 |
28 | event_wait_t outcome = event_wait_t::signaled;
29 |
30 | if (not m_predicate)
31 | {
32 | m_waitingThreads.push_back(std::this_thread::get_id());
33 |
34 | const bool signaled = m_event.wait_for(lock, timeout, [this] { return m_predicate; });
35 | outcome = signaled ? event_wait_t::signaled : event_wait_t::timeout;
36 |
37 | updateWaitingThreads(m_waitingThreads);
38 | }
39 |
40 | // Auto reset
41 | if (m_autoReset && m_waitingThreads.empty()) m_predicate = false;
42 |
43 | return outcome;
44 | }
45 |
46 | void Event::wait()
47 | {
48 | std::unique_lock lock{m_lock};
49 |
50 | if (!m_predicate)
51 | {
52 | m_waitingThreads.push_back(std::this_thread::get_id());
53 |
54 | m_event.wait(lock, [this] { return m_predicate; });
55 |
56 | updateWaitingThreads(m_waitingThreads);
57 | }
58 |
59 | // Auto reset
60 | if (m_autoReset && m_waitingThreads.empty()) m_predicate = false;
61 | }
62 |
63 | void Event::notify()
64 | {
65 | setEvent(&std::condition_variable::notify_one);
66 | }
67 |
68 | void Event::broadcast()
69 | {
70 | setEvent(&std::condition_variable::notify_all);
71 | }
72 |
73 | [[maybe_unused]] void Event::reset()
74 | {
75 | std::lock_guard lock{m_lock};
76 | m_predicate = false;
77 | }
--------------------------------------------------------------------------------
/src/Event/Event.h:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2023. All rights reserved!
4 | //
5 |
6 | #ifndef EVENT_H
7 | #define EVENT_H
8 |
9 |
10 | // std library
11 | #include
12 | #include
13 | #include
14 | #include // std::invoke
15 | #include
16 | #include
17 |
18 |
19 | namespace utils
20 | {
21 |
22 | /**
23 | * Implementation of the event synchronization primitive.
24 | * Will synchronize threads, one producer: signaling the event to single, or
25 | * all consumer threads waiting on the same condition
26 | */
27 | class Event final
28 | {
29 | public:
30 |
31 | using event_wait_t = enum class EEvent : std::uint8_t { timeout = 0, signaled };
32 | using waiting_threads_t = std::vector;
33 |
34 | /**
35 | * C-tor
36 | *
37 | * @param autoReset In case that is set to true, will reset the event after being signaled
38 | * at consumer point, so that it can wait - block on the same event on the next recall
39 | */
40 | explicit Event(bool autoReset) noexcept;
41 | ~Event();
42 |
43 | // Copy functions forbidden
44 |
45 | Event(const Event&) = delete;
46 | Event& operator=(const Event&) = delete;
47 |
48 | // Move operations forbidden
49 |
50 | Event(Event&&) = delete;
51 | Event& operator=(Event&&) = delete;
52 |
53 | /**
54 | * Wait on the event being signaled, or timeout expired
55 | *
56 | * @param timeout Timeout in milliseconds to wait
57 | * @return Indication of the operation outcome {@link Event#event_wait_t}
58 | */
59 | event_wait_t wait_for(std::chrono::milliseconds timeout);
60 |
61 | /**
62 | * Wait infinitely on event being signaled
63 | */
64 | void wait();
65 |
66 | /**
67 | * Notify - wake up the single thread
68 | */
69 | void notify();
70 |
71 | /**
72 | * Notify - wake up the all waiting threads
73 | *
74 | * @note If the event is auto reset - this will notify only the first
75 | * woken thread
76 | */
77 | void broadcast();
78 |
79 | /**
80 | * Manually reset event - in case of the auto reset is false.
81 | */
82 | [[maybe_unused]] void reset();
83 |
84 | private:
85 |
86 | using notify_f = void (std::condition_variable::*)(void);
87 | void setEvent(notify_f notifier)
88 | {
89 | std::lock_guard lock{m_lock};
90 | m_predicate = true;
91 |
92 | // There is at least one consumer-thread - waiting on the event to be signaled
93 | if (not m_waitingThreads.empty())
94 | {
95 | std::invoke(notifier, m_event);
96 | }
97 | }
98 |
99 | private:
100 | std::condition_variable m_event; // not copyable nor movable
101 | std::mutex m_lock; // not copyable nor movable
102 |
103 | const bool m_autoReset;
104 | bool m_predicate = false;
105 |
106 | waiting_threads_t m_waitingThreads;
107 |
108 | };
109 | } // namespace utils
110 |
111 | #endif // EVENT_H
112 |
--------------------------------------------------------------------------------
/src/Event/Event20.hpp:
--------------------------------------------------------------------------------
1 | #ifndef EVENT20_HPP
2 | #define EVENT20_HPP
3 |
4 | // std library
5 | #include
6 | #include
7 | #include
8 |
9 | // Application
10 | #include "Monitor.hpp"
11 |
12 | namespace utils
13 | {
14 | class Event final
15 | {
16 | using threads_ids_type = std::vector;
17 |
18 | public:
19 | explicit Event(bool autoReset) noexcept
20 | : autoReset_{autoReset}
21 | {}
22 |
23 | ~Event() = default;
24 |
25 | // Copy functions forbidden
26 |
27 | Event(const Event&) = delete;
28 | Event& operator=(const Event&) = delete;
29 |
30 | // Move operations forbidden
31 |
32 | Event(Event&&) = delete;
33 | Event& operator=(Event&&) = delete;
34 |
35 | inline void wait() noexcept
36 | {
37 | {
38 | auto lock = sync_.getLock();
39 | if (flag_) return; // premature signalization
40 | waitingThreads_.push_back(std::this_thread::get_id());
41 | }
42 | auto lock = sync_.wait([this] { return flag_; });
43 | update_waiting_threads(waitingThreads_);
44 | if (autoReset_ && waitingThreads_.empty()) flag_ = false;
45 | }
46 |
47 | inline bool wait_for(std::chrono::milliseconds timeout) noexcept
48 | {
49 | {
50 | auto lock = sync_.getLock();
51 | if (flag_) return true; // premature signalization
52 | waitingThreads_.push_back(std::this_thread::get_id());
53 | }
54 | auto [result, lock] = sync_.wait_for(timeout, [this] { return flag_; });
55 | update_waiting_threads(waitingThreads_);
56 | if (autoReset_ && waitingThreads_.empty())
57 | flag_ = false; // for broadcast to work with auto reset flag set to true
58 |
59 | return result;
60 | }
61 |
62 | inline void signal() noexcept
63 | {
64 | sync_.notify_one([this] { flag_ = true; });
65 | }
66 |
67 | inline void broadcast() noexcept
68 | {
69 | sync_.notify_all([this] { flag_ = true; });
70 | }
71 |
72 | private:
73 | inline void update_waiting_threads(threads_ids_type& waitingThreads) noexcept
74 | {
75 | waitingThreads_.erase(
76 | std::remove(waitingThreads.begin(), waitingThreads.end(), std::this_thread::get_id()),
77 | waitingThreads.end());
78 | }
79 |
80 | private:
81 | Monitor<> sync_{};
82 |
83 | const bool autoReset_;
84 | bool flag_{false};
85 | threads_ids_type waitingThreads_;
86 | };
87 | } // namespace utils
88 | #endif // EVENT20_HPP
89 |
--------------------------------------------------------------------------------
/src/Event/Event_with_future_and_promises.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Event/Event_with_future_and_promises.pdf
--------------------------------------------------------------------------------
/src/Event/Monitor.hpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 |
8 | /**
9 | * @brief Implementation of the Monitor Object design pattern.
10 | * This will be used to monitor any client's method that requires inter-thread
11 | * synchronization - by accessing/modifying the client's internal state from
12 | * the different thread contexts.
13 | */
14 | template
15 | class Monitor final
16 | {
17 | using lock_t = Lock;
18 | using condition_t = Condition;
19 |
20 | public:
21 |
22 | [[nodiscard]] std::unique_lock getLock() const
23 | {
24 | return std::unique_lock {lock_};
25 | }
26 |
27 | // Inspired by: https://www.modernescpp.com/index.php/thread-safe-queue-two-serious-errors/
28 | template
29 | [[nodiscard]] auto getLockAndNotifyWhenDone() const
30 | {
31 | struct UnLockWithNotify final
32 | {
33 | explicit UnLockWithNotify(const Monitor& monitor) noexcept: monitor_{monitor}
34 | {}
35 | // Mutex interface
36 | inline void lock() { monitor_.lock_.lock();}
37 | inline void unlock()
38 | {
39 | if constexpr (broadcast) monitor_.condition_.notify_all();
40 | else monitor_.condition_.notify_one();
41 | monitor_.lock_.unlock();
42 | }
43 | private:
44 | const Monitor& monitor_;
45 |
46 | };
47 |
48 | return UnLockWithNotify{*this};
49 | }
50 |
51 | /**
52 | * Wait infinite on the predicate to be signaled, and return
53 | * afterwards the locking object to the client
54 | */
55 | template
56 | requires std::is_same_v>
57 | [[nodiscard]] auto wait(
58 | Predicate&& predicate,
59 | Args&&... args) const
60 | {
61 | std::unique_lock lock{lock_};
62 |
63 | condition_.wait(
64 | lock,
65 | [ p = std::forward(predicate), ...args = std::forward(args)]() mutable
66 | {
67 | return std::invoke(p, std::forward(args)...);
68 | });
69 |
70 | return lock;
71 | }
72 |
73 | /**
74 | *
75 | * Wait on the predicate to be signaled, or timeout being expired: whatever comes first,
76 | * and return the tuple containing the result of the wait operation and
77 | * the locking object itself
78 | */
79 | template
80 | requires std::is_same_v>
81 | [[nodiscard]] std::tuple>
82 | wait_for(std::chrono::milliseconds timeout,
83 | Predicate&& predicate,
84 | Args&&... args) const
85 | {
86 | std::unique_lock lock{lock_};
87 |
88 | const bool result = condition_.wait_for(
89 | lock,
90 | timeout,
91 | [ p = std::forward(predicate), ...args = std::forward(args)]() mutable
92 | {
93 | return std::invoke(p, std::forward(args)...);
94 | });
95 |
96 | return {result, std::move(lock)};
97 | }
98 |
99 | template
100 | auto notify_one(Func&& func, Args&&...args) const
101 | {
102 | return notify(std::forward(func), std::forward(args)...);
103 | }
104 |
105 | template
106 | auto notify_all(Func&& func, Args&&...args) const
107 | {
108 | return notify(std::forward(func), std::forward(args)...);
109 | }
110 |
111 | private:
112 |
113 | /**
114 | * Execute the given function (callable) under the locking
115 | * protection, and notify completion.
116 | * Returns the result of the function call - if any
117 | */
118 | template
119 | auto notify(Func&& func, Args&&...args) const
120 | {
121 | auto lockWithNotify = getLockAndNotifyWhenDone();
122 | std::lock_guard lock {lockWithNotify};
123 |
124 | if constexpr (not std::is_void_v>)
125 | {
126 | return std::invoke(std::forward(func), std::forward(args)...);
127 | }
128 |
129 | std::invoke(std::forward(func), std::forward(args)...);
130 | }
131 |
132 |
133 | private:
134 | mutable lock_t lock_;
135 | mutable condition_t condition_;
136 | };
137 |
--------------------------------------------------------------------------------
/src/Event/java/AutoResetEvent.java:
--------------------------------------------------------------------------------
1 | package ;
2 |
3 | public final class AutoResetEvent extends Event {
4 | public AutoResetEvent() {
5 | super(true);
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/src/Event/java/Event.java:
--------------------------------------------------------------------------------
1 | package ;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import java.util.ArrayList;
6 | import java.util.Date;
7 | import java.util.List;
8 | import java.util.concurrent.Callable;
9 | import java.util.concurrent.TimeUnit;
10 | import java.util.concurrent.locks.Condition;
11 | import java.util.concurrent.locks.Lock;
12 | import java.util.concurrent.locks.ReentrantLock;
13 |
14 | /** Helper class for event synchronization */
15 | public class Event {
16 | private final Lock mLock; // mutex
17 | private final Condition mCondition; // condition variable
18 | private volatile boolean mEventSignaled = false; // predicate to prevent spurious wake ups
19 |
20 | private static final class ThreadList {
21 | private final List threads = new ArrayList<>();
22 |
23 | public void add() {
24 | threads.add(Thread.currentThread().getId());
25 | }
26 |
27 | public void remove() {
28 | threads.remove(Thread.currentThread().getId());
29 | }
30 |
31 | public boolean empty() {
32 | return threads.isEmpty();
33 | }
34 | }
35 |
36 | private final ThreadList waitingThreads = new ThreadList();
37 | private final boolean autoReset;
38 |
39 | public Event(boolean autoReset) {
40 | mLock = new ReentrantLock();
41 | mCondition = mLock.newCondition();
42 |
43 | this.autoReset = autoReset;
44 | }
45 |
46 | @FunctionalInterface
47 | private interface ILockCallback {
48 | void apply() throws Exception; // signature relevant: methods of condition variable may throw
49 | }
50 |
51 | private void lockAndThen(@NonNull ILockCallback callback) {
52 | mLock.lock();
53 | try {
54 | callback.apply(); // Call condition variable related methods, without handling exceptions
55 | } catch (Exception e) {
56 | e.printStackTrace();
57 | } finally {
58 | mLock.unlock();
59 | }
60 | }
61 |
62 | /**
63 | * Signal the event to wake up a single thread, among the all threads which are waiting on the
64 | * same event
65 | */
66 | public void signal() {
67 | ILockCallback callback =
68 | () -> {
69 | mEventSignaled = true;
70 | if (!waitingThreads.empty()) mCondition.signal();
71 | };
72 |
73 | lockAndThen(callback);
74 | }
75 |
76 | /** Signal the event to the all threads that are waiting on the same event notification */
77 | public void signalAll() {
78 | ILockCallback callback =
79 | () -> {
80 | mEventSignaled = true;
81 | if (!waitingThreads.empty()) mCondition.signalAll();
82 | };
83 |
84 | lockAndThen(callback);
85 | }
86 |
87 | private boolean waitOnCondition(Callable waitStrategy) throws Exception {
88 | waitingThreads.add();
89 | try {
90 | while (!mEventSignaled) {
91 | if (!waitStrategy.call()) {
92 | return false; // Timeout or deadline reached
93 | }
94 | }
95 | return true;
96 | } finally {
97 | waitingThreads.remove();
98 | if (autoReset && waitingThreads.empty()) {
99 | mEventSignaled = false;
100 | }
101 | }
102 | }
103 |
104 | /**
105 | * Wait on event signalization, or until thread which waits on event notification is interrupted
106 | */
107 | public void waitEvent() {
108 | lockAndThen(
109 | () ->
110 | waitOnCondition(
111 | () -> {
112 | mCondition.await();
113 | return true;
114 | }));
115 | }
116 |
117 | /**
118 | * Wait on event being signaled, waiting thread interrupted, or timeout is expired
119 | *
120 | * @param time The relative time interval to be waited for
121 | * @param unit Unit of time to wait for (nanoseconds, microseconds, milliseconds, etc.)
122 | * @return Indication of the wait outcome: false - timeout expired before event is notified.
123 | * Otherwise - true
124 | */
125 | public boolean waitEventFor(final long time, final TimeUnit unit) {
126 | final boolean[] signaled = {false};
127 | lockAndThen(() -> signaled[0] = waitOnCondition(() -> mCondition.await(time, unit)));
128 | return signaled[0];
129 | }
130 |
131 | /**
132 | * Wait on the event being signaled, waiting thread interrupted, or the deadline reached
133 | *
134 | * @param deadline The absolute time to be waited on
135 | * @return Indication of the wait outcome: false - timeout expired before event is notified.
136 | * Otherwise - true
137 | */
138 | public boolean waitEventUntil(final Date deadline) {
139 | final boolean[] signaled = {false};
140 | lockAndThen(() -> signaled[0] = waitOnCondition(() -> mCondition.awaitUntil(deadline)));
141 | return signaled[0];
142 | }
143 | }
144 |
--------------------------------------------------------------------------------
/src/Event/java/TestEvent.java:
--------------------------------------------------------------------------------
1 | package ;
2 |
3 | import static junit.framework.Assert.assertTrue;
4 |
5 | import java.util.List;
6 | import java.util.Locale;
7 | import java.util.concurrent.TimeUnit;
8 |
9 | import org.jetbrains.annotations.NotNull;
10 | import org.junit.Test;
11 |
12 | public class TestEvent {
13 |
14 | @FunctionalInterface
15 | private interface ISignal {
16 | void invoke(TEvent e);
17 | }
18 |
19 | @NotNull
20 | private Thread signalThread(
21 | ISignal callback, final @NotNull AutoResetEvent event, final long timeout) {
22 | return new Thread(
23 | () -> {
24 | try {
25 | Thread.sleep(timeout);
26 | callback.invoke(event);
27 | } catch (InterruptedException e) {
28 | e.printStackTrace();
29 | }
30 | });
31 | }
32 |
33 | private Runnable waitTaskSignaled(@NotNull Event event, final long signaledAfterMs) {
34 | return () -> {
35 | long start = System.nanoTime();
36 | event.waitEvent();
37 | long elapsed = (System.nanoTime() - start) / 1_000_000;
38 | System.out.printf(
39 | Locale.ENGLISH,
40 | " (%s) received after: %d[ms]\n",
41 | Thread.currentThread().getName(),
42 | elapsed);
43 | assertTrue(elapsed >= signaledAfterMs);
44 | };
45 | }
46 |
47 | @Test
48 | public void testWaitSignaled() throws InterruptedException {
49 | final AutoResetEvent event = new AutoResetEvent();
50 | final long timeout = 1000L;
51 |
52 | Thread waiter = new Thread(waitTaskSignaled(event, timeout));
53 |
54 | Thread signaler = signalThread(Event::signal, event, timeout);
55 | signaler.start();
56 |
57 | waiter.start();
58 | waiter.join();
59 | }
60 |
61 | @Test
62 | public void testWaitSignaled_multipleThreads() throws InterruptedException {
63 | final AutoResetEvent event = new AutoResetEvent();
64 | final long timeout = 1000L;
65 |
66 | Thread signaler = signalThread(Event::signalAll, event, timeout);
67 | signaler.start();
68 |
69 | List threads =
70 | List.of(
71 | new Thread(waitTaskSignaled(event, timeout), "t_waiter#1"),
72 | new Thread(waitTaskSignaled(event, timeout), "t_waiter#2"),
73 | new Thread(waitTaskSignaled(event, timeout), "t_waiter#3"));
74 | for (Thread thread : threads) {
75 | thread.start();
76 | }
77 | for (Thread thread : threads) {
78 | thread.join();
79 | }
80 | }
81 |
82 | private Thread waitForTask(@NotNull Event event, final long timeout) {
83 | return new Thread(
84 | () -> {
85 | long start = System.nanoTime();
86 | boolean signaled = event.waitEventFor(timeout, TimeUnit.MILLISECONDS);
87 | long elapsed = (System.nanoTime() - start) / 1_000_000;
88 | if (signaled) {
89 | System.out.printf(Locale.ENGLISH, " received after: %d[ms]\n", elapsed);
90 | assertTrue(elapsed < timeout);
91 | } else {
92 | System.out.printf(Locale.ENGLISH, " timeout expired: %d[ms]\n", elapsed);
93 | assertTrue(elapsed >= timeout);
94 | }
95 | });
96 | }
97 |
98 | @Test
99 | public void testWaitForSignaled() throws InterruptedException {
100 | final AutoResetEvent event = new AutoResetEvent();
101 | final long timeout = 1000L;
102 |
103 | Thread waiter = waitForTask(event, timeout * 2);
104 |
105 | Thread signaler = signalThread(Event::signal, event, timeout);
106 | signaler.start();
107 |
108 | waiter.start();
109 | waiter.join();
110 | }
111 |
112 | @Test
113 | public void testWaitForSignaled_signal_first() throws InterruptedException {
114 | final AutoResetEvent event = new AutoResetEvent();
115 | final long timeout = 1000L;
116 |
117 | event.signal();
118 | Thread.sleep(100);
119 |
120 | Thread waiter = waitForTask(event, timeout);
121 |
122 | waiter.start();
123 | waiter.join();
124 | }
125 |
126 | @Test
127 | public void testWaitForSignaled_not_signaled() throws InterruptedException {
128 | final AutoResetEvent event = new AutoResetEvent();
129 | final long timeout = 1000L;
130 |
131 | Thread waiter = waitForTask(event, timeout);
132 |
133 | waiter.start();
134 | waiter.join();
135 | }
136 |
137 | @Test
138 | public void testWaitFor_missedSignal() throws InterruptedException {
139 | final AutoResetEvent event = new AutoResetEvent();
140 | final long timeout = 1000L;
141 |
142 | Thread waiter = waitForTask(event, timeout / 2);
143 |
144 | waiter.start();
145 |
146 | Thread signaler = signalThread(Event::signal, event, timeout);
147 | signaler.start();
148 | signaler.join();
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/FileStreams/BinaryInputFileStream.h:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2021. All rights reserved!
4 | //
5 |
6 | #ifndef INCLUDE_COMMON_FILE_BINARYINPUTFILESTREAM_HXX_
7 | #define INCLUDE_COMMON_FILE_BINARYINPUTFILESTREAM_HXX_
8 |
9 | #include "FileStream.h"
10 |
11 | namespace utils::file
12 | {
13 | // Strong (named) type - type alias
14 | using BinaryInputFileStream = InputFileStream; // std::byte
15 | }
16 |
17 |
18 | #endif /* INCLUDE_COMMON_FILE_BINARYINPUTFILESTREAM_HXX_ */
19 |
--------------------------------------------------------------------------------
/src/FileStreams/BinaryOutputFileStream.h:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2021. All rights reserved!
4 | //
5 |
6 | #ifndef FILE_BINARYOUTPUTSTREAM_H_
7 | #define FILE_BINARYOUTPUTSTREAM_H_
8 |
9 |
10 | #include "FileStream.h"
11 |
12 | namespace utils::file
13 | {
14 | // Strong (named) type - type alias
15 | using BinaryOutputFileStream = OutputFileStream; //std::byte
16 | }
17 |
18 | #endif /* FILE_BINARYOUTPUTSTREAM_H_ */
19 |
--------------------------------------------------------------------------------
/src/FileStreams/CharInputFileStream.h:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2021. All rights reserved!
4 | //
5 |
6 | #ifndef INCLUDE_COMMON_FILE_CHARINPUTFILESTREAM_HXX_
7 | #define INCLUDE_COMMON_FILE_CHARINPUTFILESTREAM_HXX_
8 |
9 |
10 | #include "FileStream.h"
11 |
12 | namespace utils::file
13 | {
14 | // Strong (named) type - type alias
15 | using CharInputFileStream = InputFileStream;
16 | }
17 |
18 | #endif /* INCLUDE_COMMON_FILE_CHARINPUTFILESTREAM_HXX_ */
19 |
--------------------------------------------------------------------------------
/src/FileStreams/CharOutputFileStream.h:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2021. All rights reserved!
4 | //
5 |
6 | #ifndef INCLUDE_COMMON_FILE_CHAROUTPUTFILESTREAM_H_
7 | #define INCLUDE_COMMON_FILE_CHAROUTPUTFILESTREAM_H_
8 |
9 | #include "FileStream.h"
10 |
11 | namespace utils::file
12 | {
13 | // Strong (named) type - type alias
14 | using CharOutputFileStream = OutputFileStream;
15 | }
16 |
17 |
18 | #endif /* INCLUDE_COMMON_FILE_CHAROUTPUTFILESTREAM_H_ */
19 |
--------------------------------------------------------------------------------
/src/FileStreams/FileStream.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2021. All rights reserved!
4 | //
5 |
6 | #include "FileStream.h"
7 |
8 | using namespace utils::file;
9 |
10 | FileStream::FileStream(const std::filesystem::path& path, std::ios_base::openmode mode) noexcept
11 | {
12 | m_file.open(path, mode);
13 | }
14 |
15 | FileStream::~FileStream()
16 | {
17 | m_file.close();
18 | }
19 |
20 |
21 | bool FileStream::isOpen() const
22 | {
23 | return m_file.is_open();
24 | }
25 |
26 | std::size_t FileStream::size() const
27 | {
28 | const auto pos = m_file.tellg(); //current file position
29 |
30 | m_file.seekg(0, std::ios::end);
31 | const auto size = static_cast(m_file.tellg());
32 |
33 | m_file.seekg(pos);
34 |
35 | return size;
36 | }
37 |
--------------------------------------------------------------------------------
/src/FileStreams/FileStream.h:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2021. All rights reserved!
4 | //
5 |
6 | #ifndef FILE_STREAMS_H_
7 | #define FILE_STREAMS_H_
8 |
9 |
10 | #include
11 | #include
12 |
13 | #include
14 | #include
15 | #include
16 | #include
17 |
18 |
19 | namespace utils::file
20 | {
21 | /**
22 | * RAII wrapper around the std::fstream
23 | */
24 | class FileStream
25 | {
26 | public:
27 |
28 |
29 | /**
30 | * C-tor
31 | * Create the file stream.
32 | * Open the file.
33 | *
34 | * @param path The absolute file path
35 | * @param mode Open mode, which specify the type of the file stream and the
36 | * way how it should be created
37 | */
38 | FileStream(const std::filesystem::path& path, std::ios_base::openmode mode) noexcept;
39 |
40 | // Copy operation forbidden
41 |
42 | FileStream(const FileStream& ) = delete;
43 | FileStream& operator = (const FileStream& ) = delete;
44 |
45 | bool isOpen() const;
46 |
47 | std::size_t size() const;
48 |
49 | /**
50 | * D-tor
51 | *
52 | * Close the file stream
53 | */
54 | virtual ~FileStream();
55 |
56 | protected:
57 |
58 | mutable std::fstream m_file;
59 | };
60 |
61 | template
62 | class OutputFileStream : public FileStream
63 | {
64 | public:
65 |
66 | explicit OutputFileStream(const std::filesystem::path& path) noexcept :
67 | FileStream(path, std::ofstream::out)
68 | {}
69 |
70 | OutputFileStream(const std::filesystem::path& path, std::ios_base::openmode mode) noexcept :
71 | FileStream(path, std::ofstream::out | mode)
72 | {}
73 |
74 | virtual ~OutputFileStream() override = default;
75 |
76 | using data_t = T;
77 | using chunk_t = std::vector;
78 |
79 | virtual void write(const chunk_t& data)
80 | {
81 | writeData(data);
82 | }
83 |
84 | virtual void write(chunk_t&& data)
85 | {
86 | writeData(std::move(data));
87 | }
88 |
89 | private:
90 |
91 | template
92 | static constexpr bool compatible = std::is_same_v, chunk_t> ||
93 | std::is_constructible_v ||
94 | std::is_convertible_v;
95 |
96 | template>
97 | void writeData(Data&& data)
98 | {
99 | auto&& rdata = std::forward(data);//universal reference
100 | m_file.write(reinterpret_cast(&rdata[0]), rdata.size());
101 | }
102 |
103 | };//class OutputFileStream
104 |
105 |
106 |
107 | template
108 | class InputFileStream: public FileStream
109 | {
110 | public:
111 |
112 | explicit InputFileStream(const std::filesystem::path& path) noexcept:
113 | FileStream(path, std::ifstream::in)
114 | {}
115 |
116 | InputFileStream(const std::filesystem::path& path, std::ios_base::openmode mode) noexcept :
117 | FileStream(path, std::ifstream::in | mode)
118 | {}
119 |
120 | virtual ~InputFileStream() override = default;
121 |
122 | using data_t = T;
123 | using chunk_t = std::vector;
124 |
125 | /**
126 | * Read entire file into memory
127 | * @note It will throw std::runtime_error in case that operation on
128 | * the input file stream (std::ifstream) is somehow failed
129 | */
130 | virtual chunk_t readAll();
131 |
132 | };//class InputFileStream
133 |
134 | template
135 | inline typename InputFileStream::chunk_t
136 | InputFileStream::readAll()
137 | {
138 | const auto file_size = size();
139 |
140 | chunk_t buffer(file_size);
141 |
142 | m_file.seekg(0);//rewind the position to the beginning of the stream
143 | if (!m_file.read(reinterpret_cast(&buffer[0]), file_size) )
144 | throw std::runtime_error(strerror(errno));
145 |
146 | return buffer;
147 | }
148 | }//namespace:utils::file
149 |
150 | #endif /*FILE_STREAMS_H_*/
151 |
--------------------------------------------------------------------------------
/src/Kotlin/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/src/Kotlin/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.androidApplication)
3 | alias(libs.plugins.jetbrainsKotlinAndroid)
4 | }
5 |
6 | android {
7 | namespace 'com.example.practice_kotlin'
8 | compileSdk 34
9 |
10 | defaultConfig {
11 | applicationId "com.example.practice_kotlin"
12 | minSdk 31
13 | targetSdk 34
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | vectorDrawables {
19 | useSupportLibrary true
20 | }
21 | }
22 |
23 | buildTypes {
24 | release {
25 | minifyEnabled false
26 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
27 | }
28 | }
29 | compileOptions {
30 | sourceCompatibility JavaVersion.VERSION_1_8
31 | targetCompatibility JavaVersion.VERSION_1_8
32 | }
33 | kotlinOptions {
34 | jvmTarget = '1.8'
35 | }
36 | buildFeatures {
37 | compose true
38 | }
39 | composeOptions {
40 | kotlinCompilerExtensionVersion '1.5.1'
41 | }
42 | packaging {
43 | resources {
44 | excludes += '/META-INF/{AL2.0,LGPL2.1}'
45 | }
46 | }
47 | }
48 |
49 | dependencies {
50 |
51 | implementation libs.androidx.core.ktx
52 | implementation libs.androidx.lifecycle.runtime.ktx
53 | implementation libs.androidx.activity.compose
54 | implementation platform(libs.androidx.compose.bom)
55 | implementation libs.androidx.ui
56 | implementation libs.androidx.ui.graphics
57 | implementation libs.androidx.ui.tooling.preview
58 | implementation libs.androidx.material3
59 |
60 | testImplementation libs.junit
61 | androidTestImplementation libs.androidx.junit
62 | androidTestImplementation libs.androidx.espresso.core
63 | androidTestImplementation platform(libs.androidx.compose.bom)
64 | androidTestImplementation libs.androidx.ui.test.junit4
65 | debugImplementation libs.androidx.ui.tooling
66 | debugImplementation libs.androidx.ui.test.manifest
67 |
68 |
69 | // For coroutines tests
70 | testImplementation libs.kotlinx.coroutines.test
71 | implementation libs.kotlin.reflect
72 |
73 | }
--------------------------------------------------------------------------------
/src/Kotlin/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
15 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/java/com/example/practice_kotlin/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.compose.foundation.layout.fillMaxSize
7 | import androidx.compose.material3.MaterialTheme
8 | import androidx.compose.material3.Surface
9 | import androidx.compose.material3.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.tooling.preview.Preview
13 | import com.example.practice_kotlin.helpers.func_elapsed
14 | import com.example.practice_kotlin.ui.theme.Practice_KotlinTheme
15 |
16 |
17 | class MainActivity : ComponentActivity() {
18 | override fun onCreate(savedInstanceState: Bundle?) {
19 | super.onCreate(savedInstanceState)
20 | setContent {
21 | Practice_KotlinTheme {
22 | // A surface container using the 'background' color from the theme
23 | Surface(
24 | modifier = Modifier.fillMaxSize(),
25 | color = MaterialTheme.colorScheme.background
26 | ) {
27 | Greeting()
28 | }
29 | }
30 | }
31 | }
32 | }
33 |
34 | private fun test() {
35 | for (i in 1..5) {
36 | Thread.sleep(10)
37 | }
38 | }
39 |
40 | @Composable
41 | fun Greeting(modifier: Modifier = Modifier) {
42 | val (_, elapsedTime) = func_elapsed(::test)
43 | Text(
44 | text = "Elapsed time: $elapsedTime",
45 | modifier = modifier
46 | )
47 |
48 | }
49 |
50 | @Preview(showBackground = true)
51 | @Composable
52 | fun GreetingPreview() {
53 |
54 | Practice_KotlinTheme {
55 | Greeting()
56 | }
57 | }
--------------------------------------------------------------------------------
/src/Kotlin/src/main/java/com/example/practice_kotlin/coroutines/CoroutinesWrapper.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin.coroutines
2 |
3 | import kotlinx.coroutines.CoroutineDispatcher
4 | import kotlinx.coroutines.CoroutineName
5 | import kotlinx.coroutines.CoroutineScope
6 | import kotlinx.coroutines.Job
7 | import kotlinx.coroutines.async
8 | import kotlinx.coroutines.coroutineScope
9 |
10 |
11 | class CoroutinesWrapper(dispatcher: CoroutineDispatcher) :
12 | CoroutineScope, AutoCloseable {
13 |
14 | // Job management - to ensure that all jobs launched within CoroutinesWrapper
15 | // will be properly closed - by calling job.cancel()
16 | private val job = Job()
17 | // Specifies the environment in which the coroutines will be executed (scheduler, name and other custom attributes)
18 | override val coroutineContext = dispatcher + job + CoroutineName("CoroutinesWrapper")
19 |
20 | /**
21 | * Turn the callable into (deferred) coroutine, that
22 | * will be executed within the specified CoroutineScope - within
23 | * the given thread/thread pool context specified by _dispatcher_ parameter of the c-tor, while
24 | * calling coroutine remains suspended
25 | */
26 | private suspend fun func2coroutine(f: () -> R): R = coroutineScope {
27 | val coro = async { f() } // coroutine builder: factory method to start the coroutine within the given scope
28 | coro.await()
29 | }
30 |
31 | /**
32 | * Reference implementation {@link CoroutinesWrapper#func2coroutine}
33 | * @param f Regular function that is turned into deferred coroutine
34 | * @return Result of the regular function, of type R
35 | */
36 | suspend fun await(f: () -> R): R {
37 | return func2coroutine(f)
38 | }
39 |
40 | /**
41 | * high-order function
42 | * Providing the thread/thread pool context (CoroutineScope) for the coroutine
43 | * that will be executed, while suspending the calling coroutine
44 | *
45 | * @param f Coroutine to be executed
46 | * @return The result of the coroutine, of type R
47 | */
48 | suspend fun awaitCoroutine(f: suspend () -> R): R {
49 | return coroutineScope { f() }
50 | }
51 |
52 | override fun close() {
53 | job.cancel()
54 | }
55 | }
--------------------------------------------------------------------------------
/src/Kotlin/src/main/java/com/example/practice_kotlin/design_patterns/builder/Builder.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin.design_patterns.builder
2 |
3 | class A private constructor(
4 | val id: Int? = null,
5 | val name: String? = null
6 | ) {
7 |
8 | companion object {
9 | class Builder() {
10 | private var id: Int? = null
11 | private var name: String? = null
12 |
13 | // For aggregated updates - initialize with the current one (copy-constructor)
14 | constructor (a: A) : this() {
15 | id = a.id
16 | name = a.name
17 | }
18 |
19 | // Setters
20 |
21 | fun setId(id: Int) = apply { this.id = id }
22 |
23 | fun setName(name: String) = apply { this.name = name }
24 |
25 | /**
26 | * For aggregated updates:
27 | * - create the builder with secondary c-tor, that takes the current value of outer class instance
28 | * - update the aggregate value with the new delta-update (not all properties are set).
29 | * This will be new aggregated value
30 | *
31 | * @param a The outer class instance, as aggregated value
32 | */
33 | fun update(a: A) = apply {
34 | a.id?.let { id = it }
35 | a.name?.let { name = it }
36 | }
37 |
38 | fun build() = A(id, name)
39 | }
40 |
41 | // Helper method for parsing the dynamic updates as partial one (deltas)
42 | fun update(curr: A, new: A) = Builder(curr).update(new).build()
43 | }
44 |
45 |
46 | override fun toString(): String {
47 | return "[id=$id, name=$name]"
48 | }
49 | }
--------------------------------------------------------------------------------
/src/Kotlin/src/main/java/com/example/practice_kotlin/design_patterns/builder/Builder2.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin.design_patterns.builder
2 |
3 | enum class Engine { ELECTRIC, HYBRID, DIESEL, PETROL, HYDROGEN, LNG }
4 |
5 | enum class Camera { FRONT, REAR, BOTH, NONE }
6 | enum class Smartphone { CarPlay, AndroidAuto, BOTH, NONE }
7 | enum class ADAS { AdaptiveControl, Copilot, BOTH, NONE }
8 |
9 |
10 | class CarConfiguration private constructor(
11 | // mandatory
12 | val id: Int,
13 | val brand: String,
14 | val engine: Engine,
15 | // optionals
16 | val camera: Camera?,
17 | val smartphone: Smartphone?,
18 | val adas: ADAS?
19 | ) {
20 |
21 | companion object {
22 | class Builder(var id: Int, var brand: String, var engine: Engine) {
23 |
24 | // optionals
25 | var camera: Camera? = null
26 | var smartphone: Smartphone? = null
27 | var adas: ADAS? = null
28 |
29 | // For aggregated updates - initialize the builder with previous configuration
30 | constructor (car: CarConfiguration) : this(car.id, car.brand, car.engine) {
31 | camera = car.camera
32 | smartphone = car.smartphone
33 | adas = car.adas
34 | }
35 |
36 | fun build() = CarConfiguration(id, brand, engine, camera, smartphone, adas)
37 |
38 | // For aggregated updates - the new (partial) configuration that will be applied on top of the current configuration
39 | fun update(car: CarConfiguration) = apply {
40 | id = car.id
41 | brand = car.brand
42 | engine = car.engine
43 |
44 | car.camera?.let { camera = it }
45 | car.smartphone?.let { smartphone = it }
46 | car.adas?.let { adas = it }
47 | }
48 | }
49 |
50 | /**
51 | * Helper methods, for building the outer class instance on the fly
52 | *
53 | * @param block This is lambda that extends Builder (placeholder for anonymous Extension Function) on the fly - Builder is receiver,
54 | * which means the callable can access any non-private (public) properties of the
55 | * receiver (all of them) through "this", in order to properly configure the outer class
56 | */
57 | inline fun build(id: Int, brand: String, engine: Engine, block: Builder.() -> Unit) =
58 | Builder(id, brand, engine).apply(block).build()
59 |
60 | // For aggregated updates
61 | inline fun build(currConfig: CarConfiguration, block: Builder.() -> Unit) =
62 | Builder(currConfig).apply(block).build()
63 |
64 | fun build(currConfig: CarConfiguration, newConfig: CarConfiguration) =
65 | Builder(currConfig).update(newConfig).build()
66 | }
67 |
68 | override fun toString(): String {
69 | return "[id=$id, brand=$brand, engine=$engine, camera=$camera, smartphone=$smartphone, adas=$adas]"
70 | }
71 | }
72 |
73 |
74 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/java/com/example/practice_kotlin/design_patterns/builder/Builder3.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin.design_patterns.builder
2 |
3 |
4 | class CarConfiguration2 (
5 | // mandatory
6 | var id: Int,
7 | var brand: String,
8 | var engine: Engine,
9 | // optionals
10 | var camera: Camera? = null,
11 | var smartphone: Smartphone? = null,
12 | var adas: ADAS? = null
13 | ) {
14 |
15 |
16 | // For aggregated updates - initialize with previous configuration (copy-constructor )
17 | constructor (car: CarConfiguration2) : this(car.id, car.brand, car.engine) {
18 | camera = car.camera
19 | smartphone = car.smartphone
20 | adas = car.adas
21 | }
22 |
23 | // For aggregated updates - the new (partial) configuration that will be applied on top of the current configuration
24 | fun update(car: CarConfiguration2) = apply {
25 | id = car.id
26 | brand = car.brand
27 | engine = car.engine
28 |
29 | car.camera?.let { camera = it }
30 | car.smartphone?.let { smartphone = it }
31 | car.adas?.let { adas = it }
32 | }
33 |
34 |
35 | companion object {
36 | /**
37 | * Helper method, for building the outer class, as wrapper around the apply()
38 | * We don't call it directly, since it's about communicating the intention clearly (naming)
39 | *
40 | * @param block This is lambda that extends the receiver - instance of the outer class,
41 | * which means the callable can access any non-private (public) properties of the
42 | * receiver (all of them), in order to properly configure the outer class
43 | */
44 |
45 | inline fun build(
46 | id: Int,
47 | brand: String,
48 | engine: Engine,
49 | block: CarConfiguration2.() -> Unit
50 | ) =
51 | CarConfiguration2(id, brand, engine).apply(block)
52 |
53 | // For aggregated updates
54 | inline fun build(currConfig: CarConfiguration2, block: CarConfiguration2.() -> Unit) =
55 | CarConfiguration2(currConfig).apply(block)
56 |
57 | fun build(currConfig: CarConfiguration2, newConfig: CarConfiguration2) =
58 | CarConfiguration2(currConfig).update(newConfig)
59 | }
60 |
61 |
62 | override fun toString(): String {
63 | return "[id=$id, brand=$brand, engine=$engine, camera=$camera, smartphone=$smartphone, adas=$adas]"
64 | }
65 | }
66 |
67 |
68 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/java/com/example/practice_kotlin/helpers/ElapsedTime.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin.helpers
2 |
3 | import kotlinx.coroutines.runBlocking
4 | import kotlin.reflect.KFunction
5 |
6 |
7 | private suspend fun elapsed(f: suspend () -> R): Pair {
8 | val start = System.currentTimeMillis()
9 | val result = f()
10 | return Pair(result, System.currentTimeMillis() - start)
11 | }
12 |
13 | suspend fun elapsedAny(f: KFunction, vararg args : Any?): Pair {
14 | return elapsed { f.call(*args) }
15 | }
16 |
17 | /**
18 | * For measuring the elapsed time of a synchronous function.
19 | * It will be turned into blocking call - waiting on the function to be
20 | * executed within the coroutine context
21 | */
22 | fun func_elapsed(f: () -> R): Pair {
23 | val result: Pair
24 | runBlocking {
25 | result = elapsed(f) // suspension point
26 | }
27 | return result
28 | }
29 |
30 | /**
31 | * For measuring elapsed time of a suspend call
32 | */
33 | suspend fun coro_elapsed(f: suspend () -> R): Pair = elapsed(f)
34 |
35 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/java/com/example/practice_kotlin/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple80 = Color(0xFFD0BCFF)
6 | val PurpleGrey80 = Color(0xFFCCC2DC)
7 | val Pink80 = Color(0xFFEFB8C8)
8 |
9 | val Purple40 = Color(0xFF6650a4)
10 | val PurpleGrey40 = Color(0xFF625b71)
11 | val Pink40 = Color(0xFF7D5260)
--------------------------------------------------------------------------------
/src/Kotlin/src/main/java/com/example/practice_kotlin/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin.ui.theme
2 |
3 | import android.app.Activity
4 | import android.os.Build
5 | import androidx.compose.foundation.isSystemInDarkTheme
6 | import androidx.compose.material3.MaterialTheme
7 | import androidx.compose.material3.darkColorScheme
8 | import androidx.compose.material3.dynamicDarkColorScheme
9 | import androidx.compose.material3.dynamicLightColorScheme
10 | import androidx.compose.material3.lightColorScheme
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.SideEffect
13 | import androidx.compose.ui.graphics.toArgb
14 | import androidx.compose.ui.platform.LocalContext
15 | import androidx.compose.ui.platform.LocalView
16 | import androidx.core.view.WindowCompat
17 |
18 | private val DarkColorScheme = darkColorScheme(
19 | primary = Purple80,
20 | secondary = PurpleGrey80,
21 | tertiary = Pink80
22 | )
23 |
24 | private val LightColorScheme = lightColorScheme(
25 | primary = Purple40,
26 | secondary = PurpleGrey40,
27 | tertiary = Pink40
28 |
29 | /* Other default colors to override
30 | background = Color(0xFFFFFBFE),
31 | surface = Color(0xFFFFFBFE),
32 | onPrimary = Color.White,
33 | onSecondary = Color.White,
34 | onTertiary = Color.White,
35 | onBackground = Color(0xFF1C1B1F),
36 | onSurface = Color(0xFF1C1B1F),
37 | */
38 | )
39 |
40 | @Composable
41 | fun Practice_KotlinTheme(
42 | darkTheme: Boolean = isSystemInDarkTheme(),
43 | // Dynamic color is available on Android 12+
44 | dynamicColor: Boolean = true,
45 | content: @Composable () -> Unit
46 | ) {
47 | val colorScheme = when {
48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
49 | val context = LocalContext.current
50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
51 | }
52 |
53 | darkTheme -> DarkColorScheme
54 | else -> LightColorScheme
55 | }
56 | val view = LocalView.current
57 | if (!view.isInEditMode) {
58 | SideEffect {
59 | val window = (view.context as Activity).window
60 | window.statusBarColor = colorScheme.primary.toArgb()
61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
62 | }
63 | }
64 |
65 | MaterialTheme(
66 | colorScheme = colorScheme,
67 | typography = Typography,
68 | content = content
69 | )
70 | }
--------------------------------------------------------------------------------
/src/Kotlin/src/main/java/com/example/practice_kotlin/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-anydpi/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-anydpi/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/damirlj/modern_cpp_tutorials/5f7ded88e43991498e64e3975104572f80a1c543/src/Kotlin/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Practice_Kotlin
3 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/src/Kotlin/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/src/Kotlin/src/test/java/com/example/practice_kotlin/coroutines/TestCoroutines.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin.coroutines
2 |
3 | import kotlinx.coroutines.*
4 | import kotlinx.coroutines.test.runTest
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 |
8 |
9 | class TestCoroutines {
10 |
11 | private suspend fun simulateWork(wrapper: CoroutinesWrapper, timeoutInMs: Long): Long {
12 | return wrapper.awaitCoroutine {
13 | delay(timeoutInMs)
14 | timeoutInMs
15 | }
16 | }
17 |
18 | @OptIn(ExperimentalCoroutinesApi::class)
19 | @Test
20 | fun test_coroutineAwait() = runTest {
21 | CoroutinesWrapper(Dispatchers.Default).use { wrapper ->
22 | val timeoutInMs = 1000L
23 | val result = simulateWork(wrapper, timeoutInMs)
24 | println("Timeout: $result[ms]")
25 | assertEquals(result, timeoutInMs)
26 | }
27 | }
28 |
29 | @OptIn(ExperimentalCoroutinesApi::class)
30 | @Test
31 | fun test_coroutineAwaitNoResult() = runTest {
32 | CoroutinesWrapper(Dispatchers.Default).use { wrapper ->
33 | wrapper.awaitCoroutine {
34 | delay(1000L)
35 | println("Inside coroutine")
36 | }
37 | }
38 | }
39 |
40 | @OptIn(ExperimentalCoroutinesApi::class)
41 | @Test
42 | fun test_regularFunctionAwait() = runTest {
43 | CoroutinesWrapper(Dispatchers.Default).use { wrapper ->
44 | val timeoutInMs = 1000L
45 | val result = wrapper.await{
46 | Thread.sleep(timeoutInMs)
47 | timeoutInMs
48 | }
49 | assertEquals(result, timeoutInMs)
50 | }
51 | }
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/src/Kotlin/src/test/java/com/example/practice_kotlin/design_patterns/builder/TestBuilderPattern.kt:
--------------------------------------------------------------------------------
1 | package com.example.practice_kotlin.design_patterns.builder
2 |
3 | import org.junit.Test
4 |
5 | class TestBuilderPattern {
6 |
7 | @Test
8 | fun testBuilderPattern() {
9 | var builder = A.Companion.Builder()
10 | val a = builder.setId(11).setName("Alex").build()
11 | println(a)
12 | println(A.Companion.Builder(a).setName("Divna").build())
13 |
14 | builder = A.Companion.Builder()
15 | println(builder.setName("Damir").build())
16 | println(A.update(a, builder.build()))
17 | }
18 |
19 | @Test
20 | fun testBuilderPatternDSL() {
21 |
22 | val car = CarConfiguration.build(123, "Audi", Engine.DIESEL) {
23 | camera = Camera.FRONT
24 | smartphone = Smartphone.CarPlay
25 | }
26 |
27 | println(car)
28 | println(car.smartphone)
29 |
30 | println(CarConfiguration.build(car, CarConfiguration.build(car) {
31 | adas = ADAS.AdaptiveControl
32 | }))
33 | println(CarConfiguration.build(car, CarConfiguration.build(124, "VW", Engine.ELECTRIC) {
34 | camera = Camera.REAR
35 | adas = ADAS.Copilot
36 | }))
37 | }
38 |
39 | data class User2 (val id: Int, val name: String, var email : String? = null)
40 | @Test
41 | fun testBuilderPatternWithApplyOnly() {
42 | var car = CarConfiguration2(123, "Audi", Engine.DIESEL)
43 | println(car)
44 | car = CarConfiguration2.build(car) {
45 | camera = Camera.REAR
46 | adas = ADAS.Copilot
47 | }
48 | println(car)
49 |
50 | var user = User2(1, "Alex")
51 | println(user)
52 |
53 | user = user.apply{
54 | email = "alexlj@yahoo.com"
55 | }
56 | println(user)
57 | }
58 | }
--------------------------------------------------------------------------------
/src/Lock/ClassLevelMutex.h:
--------------------------------------------------------------------------------
1 | /*
2 | * ClassLevelMutex.h
3 | *
4 | * Created on: Jan 31, 2021
5 | * Author: Damir Ljubic
6 | */
7 |
8 | #ifndef LOCK_CLASSLEVELMUTEX_H_
9 | #define LOCK_CLASSLEVELMUTEX_H_
10 |
11 | #include
12 |
13 | namespace utils::lock
14 | {
15 | /**
16 | * Class-level mutex
17 | *
18 | * @tparam Host The host class for which the mutex will provide class-level
19 | * mutex protection: for all instances of the Host class
20 | */
21 | template
22 | class CLMutex final : public std::mutex
23 | {
24 | public:
25 |
26 | static CLMutex& instance()
27 | {
28 | static CLMutex clMutex;//Scott Meyers singleton pattern
29 | return clMutex;
30 | }
31 |
32 | private:
33 |
34 | CLMutex() = default;
35 | };
36 | }
37 |
38 |
39 |
40 | #endif /* LOCK_CLASSLEVELMUTEX_H_ */
41 |
--------------------------------------------------------------------------------
/src/Logging/ConsoleLogger.h:
--------------------------------------------------------------------------------
1 | /*
2 | * ConsoleLogger.h
3 | *
4 | * Created on: Jan 31, 2021
5 | * Author: Damir Ljubic
6 | */
7 |
8 | #ifndef LOGGING_CONSOLELOGGER_H_
9 | #define LOGGING_CONSOLELOGGER_H_
10 |
11 | #include
12 | #include
13 |
14 | // Application
15 | #include "ClassLevelMutex.h"
16 |
17 | namespace utils::log
18 | {
19 | /**
20 | * Simplified console logger.
21 | *
22 | * Uses class-level lock for ensuring the proper console logging
23 | * in concurrent environment
24 | */
25 | class ConsoleLogger final
26 | {
27 | public:
28 |
29 | using lock_t = utils::lock::CLMutex;
30 |
31 | template
32 | void logAll(Args&&...args) const noexcept
33 | {
34 | std::lock_guard lock {lock_t::instance()};
35 |
36 | (std::cout << ... << std::forward(args));
37 | }
38 |
39 | template
40 | void log(const std::string& fmt, Args&&...args) const
41 | {
42 | std::lock_guard lock {lock_t::instance()};
43 |
44 | const auto msg = utils::string_format(fmt, std::forward(args)...);//this can throw!
45 | std::cout << msg << std::endl;
46 | }
47 |
48 | void log(const std::string& msg) const noexcept
49 | {
50 | std::lock_guard lock {lock_t::instance()};
51 |
52 | std::cout << msg << std::endl;
53 | }
54 | };
55 | }
56 |
57 |
58 | #endif /* LOGGING_CONSOLELOGGER_H_ */
59 |
--------------------------------------------------------------------------------
/src/Logging/console/ConsoleLogger.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Damir Ljubic (damirlj@yahoo.com) on 05.03.2021.
3 | // Copyright (c) 2021. All rights reserved.
4 | //
5 |
6 | #ifndef LOGGING_CONSOLELOGGER_H_
7 | #define LOGGING_CONSOLELOGGER_H_
8 |
9 |
10 | #include
11 |
12 | // Class-level lock
13 | #include "ClassMutex.h"
14 |
15 | // Logging
16 | #include "Logger.h"
17 | #include "LoggingHelper.h"
18 |
19 |
20 | namespace utils::log
21 | {
22 |
23 | template
24 | void logging(android_LogPriority level, const std::string& tag, Args&&...args)
25 | {
26 | __android_log_print(level, tag.c_str(), std::forward(args)...);
27 | }
28 |
29 |
30 |
31 | /**
32 | * @brief Android specific logging to terminal (Logcat) implementation
33 | */
34 | class ConsoleLogger final : public LoggerBase
35 | {
36 | public:
37 |
38 | using super = LoggerBase;
39 | using super::super; // Using base class c-tor(s) - to provide the logging tag
40 |
41 |
42 | template
43 | void logImplWithTag(log_verbosity_t verbosity, string_t&& msg) const
44 | {
45 | using namespace std;
46 | {
47 | typename utils::locking::ClassLevelLock::Lock lock{};//class level lock
48 |
49 | logging(
50 | [](log_verbosity_t v)
51 | {
52 | switch(v)
53 | {
54 | case log_verbosity_t::LOG_LEVEL_TRACE: return ANDROID_LOG_VERBOSE;
55 | case log_verbosity_t::LOG_LEVEL_DEBUG: return ANDROID_LOG_DEBUG;
56 | case log_verbosity_t::LOG_LEVEL_INFO: return ANDROID_LOG_INFO;
57 | case log_verbosity_t::LOG_LEVEL_WARNING: return ANDROID_LOG_WARN;
58 | case log_verbosity_t::LOG_LEVEL_ERROR: return ANDROID_LOG_ERROR;
59 | }
60 | }(verbosity)
61 | , tag().c_str()
62 | , "%s\n"
63 | , std::forward(msg).c_str()
64 | );
65 | }
66 | }
67 | };//class:ConsoleLogger
68 |
69 | }//namespace utils::log
70 |
71 | #endif /* LOGGING_CONSOLELOGGER_H_ */
72 |
--------------------------------------------------------------------------------
/src/Logging/console/CoutLogger.h:
--------------------------------------------------------------------------------
1 | //
2 | // Created by Damir Ljubic (damirlj@yahoo.com) on 06.03.2021.
3 | // Copyright (c) 2021. All rights reserved.
4 | //
5 |
6 | #ifndef AIRPLAYSERVICE_COUTLOGGER_H
7 | #define AIRPLAYSERVICE_COUTLOGGER_H
8 |
9 | #include
10 |
11 | //Class-level lock
12 | #include "ClassMutex.h"
13 |
14 | // Logging
15 | #include "Logger.h"
16 | #include "LoggingHelper.h"
17 |
18 |
19 |
20 | namespace utils::log
21 | {
22 | static inline constexpr auto LOG_LEVEL = log_verbosity_t::LOG_LEVEL_INFO; //tracing filter
23 |
24 | /**
25 | * @brief Platform independent Logging to the std::cout.
26 | * Meant to be used with unit tests that needs to run
27 | * on the local machine
28 | */
29 | class CoutLogger final : public LoggerBase
30 | {
31 | public:
32 | using super = LoggerBase;
33 | using super::super; // Using base class c-tor(s): to provide the logging tag
34 |
35 |
36 | template
37 | void logImplWithTag(log_verbosity_t verbosity, string_t&& msg) const
38 | {
39 | using namespace std;
40 |
41 | typename utils::locking::ClassLevelLock::Lock lock{};//class level lock
42 |
43 | auto s = utils::string_format("<%s>: %s\n", tag().c_str(), std::forward(msg).c_str());
44 |
45 | switch(verbosity)
46 | {
47 | case log_verbosity_t::LOG_LEVEL_ERROR:
48 | cerr << s;
49 | break;
50 | default:
51 | if (verbosity >= LOG_LEVEL)
52 | {
53 | cout << s;
54 | }
55 | break;
56 | }
57 | }
58 | };// CoutLogger
59 |
60 | }//namespace utils::log
61 |
62 | #endif //AIRPLAYSERVICE_COUTLOGGER_H
63 |
--------------------------------------------------------------------------------
/src/Logging/console/LoggingHelper.h:
--------------------------------------------------------------------------------
1 | //
2 | // LoggerHelper.h
3 | // Author: Damir Ljubic
4 | //
5 |
6 | #ifndef LOGGINGHELPER_H
7 | #define LOGGINGHELPER_H
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | // Application
15 | #include "Commons.h"
16 |
17 | namespace utils::log
18 | {
19 | template
20 | inline constexpr bool is_string = std::is_same_v> ||
21 | std::is_constructible_v ||
22 | std::is_convertible_v;
23 |
24 |
25 | template >>
26 | using string_t = T;
27 |
28 | /*
29 | * For java-like objects with public toString() method
30 | */
31 | template
32 | struct has_to_string : std::false_type {};
33 |
34 | template
35 | struct has_to_string> : std::is_same().toString())> {};
37 |
38 | template
39 | inline constexpr bool has_to_string_v = has_to_string::value;
40 |
41 | /**
42 | * String conversion
43 | *
44 | * it will return the value, if the value itself is std::string convertible.
45 | * If it's numeric type, or enum class - std::to_string() conversion function will be called.
46 | * For java-like custom types with non-static member public toString() method will be invoked.
47 | * Otherwise, the empty string will be returned
48 | *
49 | */
50 | template
51 | std::optional str(T &&value)
52 | {
53 | if constexpr (is_string) // for string or string-like types
54 | {
55 | return value;
56 | }
57 | else
58 | {
59 | if constexpr (std::is_arithmetic_v>) // for numerics: integral and floating-point arithmetic
60 | {
61 | return std::to_string(std::forward(value));
62 | }
63 | else
64 | {
65 | if constexpr (std::is_enum_v>) // for enum (scoped) classes
66 | {
67 | return std::to_string(utils::convertEnum(std::forward(value)));
68 | }
69 | else
70 | {
71 | if constexpr (has_to_string_v>) // for java-like types with toString() method
72 | {
73 | return std::forward(value).toString();
74 | }
75 | }
76 | }
77 | }
78 | return {};
79 | }
80 | }
81 | #endif //LOGGINGHELPER_H
82 |
--------------------------------------------------------------------------------
/src/Logging/console/LoggingMerge.h:
--------------------------------------------------------------------------------
1 | /*
2 | * LoggingMerge.h
3 | *
4 | * Created on: Jan 31, 2021
5 | * Author: Damir Ljubic
6 | */
7 |
8 | #ifndef LOGGING_MERGE_H_
9 | #define LOGGING_MERGE_H_
10 |
11 | #include
12 | #include
13 |
14 | #include "Logger.h"
15 | #include "LoggingHelper.h"
16 |
17 |
18 | namespace utils::log
19 | {
20 |
21 | /**
22 | * For parallel logging on different logging mediums
23 | */
24 | template
25 | class LoggingMerge final : public Policies...
26 | {
27 | public:
28 |
29 | using policy_t = std::tuple;
30 |
31 | LoggingMerge(): m_policies{Policies{}...}//default, parameterless c-tors
32 | {}
33 |
34 | template >
35 | void log(log_verbosity_t verbosity, T&& msg)
36 | {
37 | for (auto i = 0; i < nPolicies; ++i)
38 | {
39 | std::get(m_policies).log(verbosity, std::forward(msg));
40 | }
41 | }
42 |
43 | template >
44 | void log(std::size_t policy, log_verbosity_t verbosity, T&& msg)
45 | {
46 | if (policy >= nPolicies) throw std::out_of_range("Policy index out of range!");
47 |
48 | std::get(m_policies).log(verbosity, std::forward(msg));
49 | }
50 |
51 | private:
52 |
53 | inline static constexpr std::size_t nPolicies = sizeof...(Policies);
54 | policy_t m_policies;
55 | };
56 | }
57 |
58 |
59 | #endif /*LOGGING_MERGE_H_*/
60 |
--------------------------------------------------------------------------------
/src/Logging/console/LoggingTag.h:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2021. All rights reserved!
4 | //
5 |
6 | #ifndef LOGGINGTAG_H
7 | #define LOGGINGTAG_H
8 |
9 | #include
10 | #include "LoggingHelper.h"
11 |
12 |
13 |
14 | namespace utils::log
15 | {
16 |
17 | /**
18 | * Adding the subchannels to the logging tag, as "(.subchannel)+" pattern
19 | * @tparam Ts string or anything from which string is constructible
20 | * @param subchannel The subchannels
21 | * @return Generated subchannels pattern
22 | *
23 | */
24 | template
25 | auto appendSubchannels(const string_t&...subchannels)
26 | {
27 | static const std::string delm = ".";
28 | std::ostringstream s;
29 |
30 | (s << ... << (delm + subchannels));
31 |
32 | return s.str();
33 | }
34 |
35 | template
36 | auto generateLogTag(const std::string &app
37 | , const std::string &channel
38 | , const string_t&...subchannels)
39 | {
40 | std::string tag = app + std::string(":") + channel;
41 | if (sizeof...(subchannels))
42 | {
43 | tag += appendSubchannels(subchannels...);
44 | }
45 | return tag;
46 | }
47 | }
48 | #endif //LOGGINGTAG_H
49 |
--------------------------------------------------------------------------------
/src/Logging/file/DataLogger.h:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2021. All rights reserved!
4 | //
5 |
6 | #ifndef LOGGING_DATALOGGER_H_
7 | #define LOGGING_DATALOGGER_H_
8 |
9 | namespace utils::log
10 | {
11 |
12 | template
13 | class ILoggerL
14 | {
15 | public:
16 |
17 | virtual ~ILoggerL() = default;
18 | virtual void log(const T& ) = 0;
19 |
20 | protected:
21 |
22 | ILoggerL() = default;
23 | };
24 |
25 | template
26 | class ILoggerR
27 | {
28 | public:
29 |
30 | virtual ~ILoggerR() = default;
31 | virtual void log(T&& ) = 0;
32 |
33 | protected:
34 |
35 | ILoggerR() = default;
36 | };
37 |
38 | }//namespace: utils::log
39 |
40 |
41 |
42 | #endif /* LOGGING_DATALOGGER_H_ */
43 |
--------------------------------------------------------------------------------
/src/Logging/file/FileLogger.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // damirlj@yahoo.com
3 | // Copyright (c) 2021. All rights reserved!
4 | //
5 |
6 | #include
7 |
8 | #include "FileLogger.h"
9 |
10 | using namespace utils::log;
11 |
12 |
13 |
14 | template
15 | FileLogger::FileLogger(std::size_t cache
16 | , std::unique_ptr> file
17 | , std::string name
18 | , utils::ThreadWrapper::schedule_policy_t scheduling
19 | , utils::ThreadWrapper::priority_t priority
20 | ): m_pLogFile(std::move(file))
21 | , m_plogThread(std::make_unique(name, scheduling, priority))
22 | {
23 | m_logBuffer.reserve(cache);
24 | }
25 |
26 |
27 | template
28 | FileLogger