Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
ThreadTest.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#include <random>
9#include <tuple>
10
12#include "Stroika/Foundation/Containers/Set.h"
19
20#include "ThreadTest.h"
21
22using namespace std;
23
24using namespace Stroika::Foundation;
27using namespace Stroika::Foundation::Database;
28using namespace Stroika::Foundation::Execution;
29using namespace Stroika::Foundation::Time;
30
31using namespace Database::SQL;
32
33namespace {
34 void SetupDB_ (Connection::Ptr conn)
35 {
36 constexpr Common::Version kCurrentVersion_ = Common::Version{1, 0, Common::VersionStage::Alpha, 0};
37 SQL::ORM::ProvisionForVersion (
38 conn, kCurrentVersion_,
39 initializer_list<SQL::ORM::TableProvisioner>{
40 {"EMPLOYEES"sv,
41 [] (SQL::Connection::Ptr c, optional<Common::Version> v, [[maybe_unused]] Common::Version targetDBVersion) -> void {
42 // for now no upgrade support
43 if (not v) {
44 c.Exec ("CREATE TABLE EMPLOYEES("
45 "ID INTEGER PRIMARY KEY AUTOINCREMENT,"
46 "NAME TEXT NOT NULL,"
47 "AGE INT NOT NULL,"
48 "ADDRESS CHAR(50),"
49 "SALARY REAL,"
50 "STILL_EMPLOYED INT"
51 ");"sv);
52 }
53 }},
54 {"PAYCHECKS"sv,
55 [] (SQL::Connection::Ptr c, optional<Common::Version> v, [[maybe_unused]] Common::Version targetDBVersion) -> void {
56 // for now no upgrade support
57 if (not v) {
58 c.Exec ("CREATE TABLE PAYCHECKS("
59 "ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
60 "EMPLOYEEREF INT NOT NULL,"
61 "AMOUNT REAL,"
62 "DATE TEXT"
63 ");"sv);
64 }
65 }},
66 });
67 }
68
69 void PeriodicallyUpdateEmployeesTable_ (Connection::Ptr conn)
70 {
71 Statement addEmployeeStatement = conn.mkStatement (
72 "INSERT INTO EMPLOYEES (NAME,AGE,ADDRESS,SALARY,STILL_EMPLOYED) values (:NAME, :AGE, :ADDRESS, :SALARY, :STILL_EMPLOYED);"sv);
73
74 // Add Initial Employees
75 addEmployeeStatement.Execute (initializer_list<Statement::ParameterDescription>{
76 {":NAME", L"Paul"},
77 {":AGE", 32},
78 {":ADDRESS", L"California"},
79 {":SALARY", 20000.00},
80 {":STILL_EMPLOYED", 1},
81 });
82 addEmployeeStatement.Execute (initializer_list<Statement::ParameterDescription>{
83 {":NAME", L"Allen"},
84 {":AGE", 25},
85 {":ADDRESS", L"Texas"},
86 {":SALARY", 15000.00},
87 {":STILL_EMPLOYED", 1},
88 });
89 addEmployeeStatement.Execute (initializer_list<Statement::ParameterDescription>{
90 {":NAME", "Teddy"sv},
91 {":AGE", 23},
92 {":ADDRESS", "Norway"sv},
93 {":SALARY", 20000.00},
94 {":STILL_EMPLOYED", 1},
95 });
96 addEmployeeStatement.Execute (initializer_list<Statement::ParameterDescription>{
97 {":NAME", "Mark"sv},
98 {":AGE", 25},
99 {":ADDRESS", "Rich-Mond"sv},
100 {":SALARY", 65000.00},
101 {":STILL_EMPLOYED", 1},
102 });
103 addEmployeeStatement.Execute (initializer_list<Statement::ParameterDescription>{
104 {":NAME", "David"sv},
105 {":AGE", 27},
106 {":ADDRESS", "Texas"sv},
107 {":SALARY", 85000.00},
108 {":STILL_EMPLOYED", 1},
109 });
110 addEmployeeStatement.Execute (initializer_list<Statement::ParameterDescription>{
111 {":NAME", "Kim"sv},
112 {":AGE", 22},
113 {":ADDRESS", L"South-Hall"},
114 {":SALARY", 45000.00},
115 {":STILL_EMPLOYED", 1},
116 });
117 addEmployeeStatement.Execute (initializer_list<Statement::ParameterDescription>{
118 {":NAME", "James"sv},
119 {":AGE", 24},
120 {":ADDRESS", "Houston"sv},
121 {":SALARY", 10000.00},
122 {":STILL_EMPLOYED", 1},
123 });
124
125 default_random_engine generator;
126 uniform_int_distribution<int> distribution{1, 6};
127
128 Statement getAllActiveEmployees = conn.mkStatement ("Select ID,NAME from EMPLOYEES where STILL_EMPLOYED=1;"sv);
129
130 Statement fireEmployee = conn.mkStatement ("Update EMPLOYEES Set STILL_EMPLOYED=0 where ID=:ID;"sv);
131
132 // then keep adding/removing people randomly (but dont really remove just mark no longer employed so we
133 // can REF in paycheck table
134 while (true) {
135 static const Sequence<String> kNames_{"Joe", "Phred", "Barny", "Sue", "Anne"};
136 uniform_int_distribution<int> namesDistr{0, static_cast<int> (kNames_.size () - 1)};
137 uniform_int_distribution<int> ageDistr{25, 50};
138 static const Sequence<String> kAddresses{"Houston", "Pittsburg", "New York", "Paris", "California"};
139 uniform_int_distribution<int> addressesDistr{0, static_cast<int> (kAddresses.size () - 1)};
140 uniform_real_distribution<float> salaryDistr{10000.00, 50000.00};
141
142 try {
143 uniform_int_distribution<int> whatTodoDistr{0, 3};
144 switch (whatTodoDistr (generator)) {
145 case 0:
146 case 1: {
147 String name = kNames_[namesDistr (generator)];
148 cout << "Adding employee {}"_f(name) << endl;
149 addEmployeeStatement.Execute (initializer_list<Statement::ParameterDescription>{
150 {":NAME"sv, name},
151 {":AGE"sv, ageDistr (generator)},
152 {":ADDRESS"sv, kAddresses[addressesDistr (generator)]},
153 {":SALARY"sv, salaryDistr (generator)},
154 {":STILL_EMPLOYED"sv, 1},
155 });
156 } break;
157 case 2: {
158 // Look somebody up, and fire them
159 Sequence<tuple<VariantValue, VariantValue>> activeEmps = getAllActiveEmployees.GetAllRows (0, 1);
160 if (not activeEmps.empty ()) {
161 uniform_int_distribution<int> empDistr{0, static_cast<int> (activeEmps.size () - 1)};
162 tuple<VariantValue, VariantValue> killMe = activeEmps[empDistr (generator)];
163 cout << "Firing employee: {}, {}"_f(get<0> (killMe).As<int> (), get<1> (killMe).As<String> ()) << endl;
164 fireEmployee.Execute (initializer_list<Statement::ParameterDescription>{{L":ID", get<0> (killMe).As<int> ()}});
165 }
166 } break;
167 }
168 }
169 catch (...) {
170 // no need to check for ThreadAbort excepton, since Sleep is a cancelation point
171 cout << "Exception processing SQL - this should generally not happen: {}"_f(current_exception ()) << endl;
172 }
173
174 Sleep (1s); // **cancelation point**
175 }
176 }
177
178 void PeriodicallyWriteChecksForEmployeesTable_ (Connection::Ptr conn)
179 {
180 Statement addPaycheckStatement = conn.mkStatement ("INSERT INTO PAYCHECKS (EMPLOYEEREF,AMOUNT,DATE) values (:EMPLOYEEREF, :AMOUNT, :DATE);"sv);
181 Statement getAllActiveEmployees = conn.mkStatement ("Select ID,NAME,SALARY from EMPLOYEES where STILL_EMPLOYED=1;"sv);
182
183 while (true) {
184 try {
185 for (const auto& employee : getAllActiveEmployees.GetAllRows (0, 1, 2)) {
186 int id = get<0> (employee).As<int> ();
187 String name = get<1> (employee).As<String> ();
188 double salary = get<2> (employee).As<double> ();
189 cout << "Writing paycheck for employee #{} ({}) amount {}"_f(id, name, salary) << endl;
190 addPaycheckStatement.Execute (initializer_list<Statement::ParameterDescription>{
191 {":EMPLOYEEREF"sv, id},
192 {":AMOUNT"sv, salary / 12},
193 {":DATE"sv, DateTime::Now ().Format (DateTime::kISO8601Format)},
194 });
195 }
196 }
197 catch (...) {
198 // no need to check for ThreadAbort exception, since Sleep is a cancelation point
199 cout << "Exception processing SQL - this should generally not happen: {}"_f(current_exception ()) << endl;
200 }
201 Sleep (2s); // **cancelation point**
202 }
203 }
204}
205
206void Stroika::Samples::SQL::ThreadTest (const function<Connection::Ptr ()>& connectionFactory)
207{
208 /*
209 * Create threads for each of our activities.
210 * When the waitable even times out, the threads will automatically be 'canceled' as they go out of scope.
211 */
212 SQL::Connection::Ptr conn1 = connectionFactory ();
213 SQL::Connection::Ptr conn2 = connectionFactory ();
214 SetupDB_ (conn1);
215 Thread::CleanupPtr updateEmpDBThread{Thread::CleanupPtr::eAbortBeforeWaiting,
216 Thread::New ([=] () { PeriodicallyUpdateEmployeesTable_ (conn1); }, Thread::eAutoStart, "Update Employee Table"sv)};
217 Thread::CleanupPtr writeChecks{Thread::CleanupPtr::eAbortBeforeWaiting,
218 Thread::New ([=] () { PeriodicallyWriteChecksForEmployeesTable_ (conn2); }, Thread::eAutoStart, "Write Checks"sv)};
220}
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
A generalization of a vector: a container whose elements are keyed by the natural numbers.
nonvirtual Sequence< Row > GetAllRows()
call Reset (), and then GetAllRemainingRows () - which always starts current statement with current b...
Definition Statement.inl:64
nonvirtual WaitStatus WaitQuietly(Time::DurationSeconds timeout=Time::kInfinity)
nonvirtual size_t size() const
Returns the number of items contained.
Definition Iterable.inl:302
nonvirtual bool empty() const
Returns true iff size() == 0.
Definition Iterable.inl:308
Ptr New(const function< void()> &fun2CallOnce, const optional< Characters::String > &name, const optional< Configuration > &configuration)
Definition Thread.cpp:955
void Sleep(Time::Duration seconds2Wait)
Definition Sleep.cpp:18
STL namespace.