Lumiera  0.pre.03
»edit your freedom«
thread-wrapper-lifecycle-test.cpp
Go to the documentation of this file.
1 /*
2  ThreadWrapperLifecycle(Test) - verify lifecycle aspects of the thread wrapper
3 
4  Copyright (C)
5  2023, Hermann Vosseler <Ichthyostega@web.de>
6 
7   **Lumiera** is free software; you can redistribute it and/or modify it
8   under the terms of the GNU General Public License as published by the
9   Free Software Foundation; either version 2 of the License, or (at your
10   option) any later version. See the file COPYING for further details.
11 
12 * *****************************************************************/
13 
19 #include "lib/test/run.hpp"
20 #include "lib/thread.hpp"
22 
23 #include <atomic>
24 #include <chrono>
25 #include <memory>
26 
27 using test::Test;
28 using lib::explore;
29 using lib::test::Dummy;
30 using std::atomic_uint;
31 using std::this_thread::yield;
32 using std::this_thread::sleep_for;
33 using namespace std::chrono_literals;
34 using std::chrono::system_clock;
35 using std::unique_ptr;
36 
37 
38 namespace lib {
39 namespace test{
40 
41  namespace {
42  using CLOCK_SCALE = std::micro; // Results are in µ-sec
43  }
44 
45 
46  /*******************************************************************/
53  {
54 
55  virtual void
56  run (Arg)
57  {
58  defaultWrapperLifecycle();
59  verifyThreadLifecycleHooks();
60  demonstrateExplicitThreadLifecycle();
61  }
62 
63 
65  void
67  {
68  using Dur = std::chrono::duration<double, CLOCK_SCALE>;
69  using Point = system_clock::time_point;
70  Point threadStart;
71  Point afterCtor;
72 
73  // the new thread starts immediately from ctor-call...
74  Thread thread("lifecycle", [&]{
75  threadStart = system_clock::now();
76  });
77  afterCtor = system_clock::now();
78  CHECK (thread); // thread marked as running
79 
80  while (thread) yield();
81  CHECK (not thread); // thread now marked as detached/dead
82 
83  double offset = Dur{threadStart - afterCtor}.count();
84  CHECK (offset > 0);
85  } // Note: in practice we see here values > 100µs
86  // but in theory the thread might even overtake the launcher
87 
88 
89 
92  void
94  {
95  atomic_uint stage{0}; // flexible launch-builder syntax:
96  ThreadHookable thread{ThreadHookable::Launch([]{ sleep_for (5ms); })
97  .atStart([&]{ stage = 1; })
98  .atExit ([&]{ stage = 2; })
99  .threadID("hooked thread")};
100  CHECK (thread);
101  CHECK (0 == stage);
102 
103  sleep_for (1ms);
104  CHECK (thread);
105  CHECK (1 == stage);
106 
107  while (thread) yield();
108  CHECK (not thread);
109  CHECK (2 == stage);
110  }
111 
112 
123  void
125  {
126  struct TestThread
128  {
129  using ThreadHookable::ThreadHookable;
130 
131  atomic_uint processVal{23};
132 
133  void
134  doIt (uint haveFun)
135  {
136  sleep_for (100us);
137  processVal = haveFun;
138  sleep_for (5ms);
139  }
140  };
141  // Note the Dummy member allows to watch instance lifecycle
142  CHECK (0 == Dummy::checksum());
143 
144  // the frontEnd allows to access the TestThread component
145  // and also represents the running state
146  unique_ptr<TestThread> frontEnd;
147  CHECK (not frontEnd); // obviously not running yet
148 
149  // start the thread and wire lifecycle callbacks
150  frontEnd.reset (new TestThread{
151  TestThread::Launch{&TestThread::doIt, 55u}
152  .atExit([&]{ frontEnd.reset(); })
153  .onOrphan([](thread::ThreadWrapper& wrapper)
154  { wrapper.detach_thread_from_wrapper(); })
155  });
156 
157  CHECK (frontEnd); // thread now marked as running
158 
159  CHECK (23 == frontEnd->processVal); // this value was set by the ctor in this thread
160  sleep_for (1ms); // wait for the thread function to become active
161  CHECK (55 == frontEnd->processVal); // changed by thread function
162  sleep_for (10ms);
163 
164  CHECK (not frontEnd); // meanwhile thread has finished
165  } // and also cleared the front-end from the `atExit`-hook
166  };
167 
168 
169 
171  LAUNCHER (ThreadWrapperLifecycle_test, "function common");
172 
173 
174 
175 }} // namespace lib::test
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
Definition: run.hpp:40
Implementation namespace for support and library code.
Abstract Base Class for all testcases.
Definition: run.hpp:53
Simplistic test class runner.
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:37
unittest helper code: test dummy objects to track instances.
Convenience front-end to simplify and codify basic thread handling.
A Dummy object for tests.
Extended variant of the standard case, allowing to install callbacks (hook functions) to be invoked d...
Definition: thread.hpp:716
A thin convenience wrapper to simplify thread-handling.
Definition: thread.hpp:648
void detach_thread_from_wrapper()
allow to detach explicitly — independent from thread-function&#39;s state.
Definition: thread.hpp:231