Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
Service/Sources/Service.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#include "Stroika/Frameworks/StroikaPreComp.h"
5
6#include <cstdlib>
7#include <iostream>
8
13#include "Stroika/Foundation/Execution/WaitableEvent.h"
14#include "Stroika/Frameworks/Service/Main.h"
15
16#include "AppVersion.h"
17
18#include "Service.h"
19
20using namespace std;
21
22using namespace Stroika::Foundation;
24using namespace Stroika::Foundation::Execution;
25using namespace Stroika::Frameworks::Service;
26
27using namespace Samples::Service;
28
29#if qUseLogger
32#endif
33
34namespace {
35 const Main::ServiceDescription kServiceDescription_{"Stroika-Sample-Service"_k, "Stroika Sample Service"_k};
36}
37
38namespace {
39 //
40 // Typically have one like this for each major module your service manages.
41 //
42 // These get defined/declared in that module, and just referenced here
43 //
44 struct SomeModuleALikeWebServer_ {
45 // initialize that service//module here, including starting any threads
46 SomeModuleALikeWebServer_ ()
47 : fSomeOtherTaskDoingRealWork_{Thread::CleanupPtr::eAbortBeforeWaiting, Thread::New (
48 [] () {
49 Execution::Sleep (24h); // wait 1 day ... simple test....
50 },
51 Thread::eAutoStart)}
52 {
53 }
54 ~SomeModuleALikeWebServer_ () = default;
55 Thread::CleanupPtr fSomeOtherTaskDoingRealWork_;
56 };
57}
58
59void SampleAppServiceRep::MainLoop (const std::function<void ()>& startedCB)
60{
61 /*
62 * The lifetime of the service roughly matches the lifetime of this MainLoop. Terminating the service (myService --stop)
63 * will cause a Thread::AbortException to be sent to this MainLoop, so that it unwinds.
64 *
65 * The simplest and safest way to construct this MainLoop is using RAII, referencing external
66 * 'modules' which start and stop any needed threads and do whatever setup/shutdown is needed.
67 */
68
69#if qUseLogger
70 // Just so you get a clear message in the log that the service didn't startup. The things that actually caused the problem should
71 bool successfullyStarted{false};
72 [[maybe_unused]] auto&& cleanup = Execution::Finally ([&] () {
73 if (not successfullyStarted) {
74 Logger::sThe.Log (Logger::eError, "Failed to successfully start service"_f);
75 }
76 });
77#endif
78
79 /*
80 * Startup modules.
81 *
82 * \note initialized in the order given here, and shutdown in the reverse order, so order really does matter.
83 * Put the more general ones with fewest dependencies first.
84 */
85 SomeModuleALikeWebServer_ moduleA;
86 SomeModuleALikeWebServer_ moduleB; // typically of a differnt type
87
88 startedCB (); // Notify service control mgr that the service has started
89
90#if qUseLogger
91 Logger::sThe.Log (Logger::eInfo, "{} (version {}) service started successfully"_f, kServiceDescription_.fPrettyName, AppVersion::kVersion);
92 successfullyStarted = true;
93
94 // the final object delcared on the stack before we wait, so its the first run when we are handling the
95 // thread aboort exception, and unwinding this call.
96 [[maybe_unused]] auto&& cleanup2 = Execution::Finally ([&] () { Logger::sThe.Log (Logger::eInfo, "Beginning service shutdown"_f); });
97#endif
98
99 /*
100 * This thread will block here, and never go any further. When the service is terminated, WaitableEvent will
101 * abort (raise exception) and all the destructors on the stack (above) from this routine will get played
102 * backewards to cleanup.
103 */
104 Execution::WaitableEvent{}.Wait (); // until told to stop by abort exception
105
107}
108
109Main::ServiceDescription SampleAppServiceRep::GetServiceDescription () const
110{
111 return kServiceDescription_;
112}
#define AssertNotReached()
Definition Assertions.h:355
A simple/portable wrapper on syslog/log4j/WindowsEventlog, with features like throttling,...
Definition Logger.h:94
nonvirtual void Wait(Time::DurationSeconds timeout=Time::kInfinity)
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31
STL namespace.