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) Lumiera.org
5  2023, Hermann Vosseler <Ichthyostega@web.de>
6 
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of
10  the License, or (at your option) any later version.
11 
12  This program is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 
21 * *****************************************************/
22 
28 #include "lib/test/run.hpp"
29 #include "lib/thread.hpp"
30 #include "lib/test/testdummy.hpp"
31 
32 #include <atomic>
33 #include <chrono>
34 #include <memory>
35 
36 using test::Test;
37 using lib::explore;
38 using lib::test::Dummy;
39 using std::atomic_uint;
40 using std::this_thread::yield;
41 using std::this_thread::sleep_for;
42 using namespace std::chrono_literals;
43 using std::chrono::system_clock;
44 using std::unique_ptr;
45 
46 
47 namespace lib {
48 namespace test{
49 
50  namespace {
51  using CLOCK_SCALE = std::micro; // Results are in µ-sec
52  }
53 
54 
55  /*******************************************************************/
62  {
63 
64  virtual void
65  run (Arg)
66  {
67  defaultWrapperLifecycle();
68  verifyThreadLifecycleHooks();
69  demonstrateExplicitThreadLifecycle();
70  }
71 
72 
74  void
76  {
77  using Dur = std::chrono::duration<double, CLOCK_SCALE>;
78  using Point = system_clock::time_point;
79  Point threadStart;
80  Point afterCtor;
81 
82  // the new thread starts immediately from ctor-call...
83  Thread thread("lifecycle", [&]{
84  threadStart = system_clock::now();
85  });
86  afterCtor = system_clock::now();
87  CHECK (thread); // thread marked as running
88 
89  while (thread) yield();
90  CHECK (not thread); // thread now marked as detached/dead
91 
92  double offset = Dur{threadStart - afterCtor}.count();
93  CHECK (offset > 0);
94  } // Note: in practice we see here values > 100µs
95  // but in theory the thread might even overtake the launcher
96 
97 
98 
101  void
103  {
104  atomic_uint stage{0}; // flexible launch-builder syntax:
105  ThreadHookable thread{ThreadHookable::Launch([]{ sleep_for (5ms); })
106  .atStart([&]{ stage = 1; })
107  .atExit ([&]{ stage = 2; })
108  .threadID("hooked thread")};
109  CHECK (thread);
110  CHECK (0 == stage);
111 
112  sleep_for (1ms);
113  CHECK (thread);
114  CHECK (1 == stage);
115 
116  while (thread) yield();
117  CHECK (not thread);
118  CHECK (2 == stage);
119  }
120 
121 
132  void
134  {
135  struct TestThread
137  {
138  using ThreadHookable::ThreadHookable;
139 
140  atomic_uint processVal{23};
141 
142  void
143  doIt (uint haveFun)
144  {
145  sleep_for (100us);
146  processVal = haveFun;
147  sleep_for (5ms);
148  }
149  };
150  // Note the Dummy member allows to watch instance lifecycle
151  CHECK (0 == Dummy::checksum());
152 
153  // the frontEnd allows to access the TestThread component
154  // and also represents the running state
155  unique_ptr<TestThread> frontEnd;
156  CHECK (not frontEnd); // obviously not running yet
157 
158  // start the thread and wire lifecycle callbacks
159  frontEnd.reset (new TestThread{
160  TestThread::Launch{&TestThread::doIt, 55u}
161  .atExit([&]{ frontEnd.reset(); })
162  .onOrphan([](thread::ThreadWrapper& wrapper)
163  { wrapper.detach_thread_from_wrapper(); })
164  });
165 
166  CHECK (frontEnd); // thread now marked as running
167 
168  CHECK (23 == frontEnd->processVal); // this value was set by the ctor in this thread
169  sleep_for (1ms); // wait for the thread function to become active
170  CHECK (55 == frontEnd->processVal); // changed by thread function
171  sleep_for (10ms);
172 
173  CHECK (not frontEnd); // meanwhile thread has finished
174  } // and also cleared the front-end from the `atExit`-hook
175  };
176 
177 
178 
180  LAUNCHER (ThreadWrapperLifecycle_test, "function common");
181 
182 
183 
184 }} // namespace lib::test
auto explore(IT &&srcSeq)
start building a IterExplorer by suitably wrapping the given iterable source.
Definition: run.hpp:49
Implementation namespace for support and library code.
unittest helper code: test dummy objects to track instances.
Abstract Base Class for all testcases.
Definition: run.hpp:62
Simple test class runner.
Lumiera GTK UI implementation root.
Definition: guifacade.cpp:46
Convenience front-end to simplify and codify basic thread handling.
A Dummy object for tests.
Definition: testdummy.hpp:50
Extended variant of the standard case, allowing to install callbacks (hook functions) to be invoked d...
Definition: thread.hpp:724
A thin convenience wrapper to simplify thread-handling.
Definition: thread.hpp:656
void detach_thread_from_wrapper()
allow to detach explicitly — independent from thread-function&#39;s state.
Definition: thread.hpp:239