Joedb 10.5.0
The Journal-Only Embedded Database
Loading...
Searching...
No Matches
Writable_Client.h
Go to the documentation of this file.
1#ifndef joedb_Writable_Client_declared
2#define joedb_Writable_Client_declared
3
6
7namespace joedb
8{
9 /// Writable specialization of Client
10 ///
11 /// @ingroup concurrency
12 class Writable_Client: public Client
13 {
14 friend class Client_Lock;
15
16 protected:
17 template<typename F> auto transaction(F f)
18 {
19 const Tail_Exclusive_Lock lock(get_tail_lock());
20
21 start_transaction();
22
23 using T = decltype(f());
24
25 if constexpr (std::is_void<T>::value)
26 {
27 try
28 {
29 f();
30 do_checkpoint();
31 }
32 catch (...)
33 {
34 emergency_unlock();
35 throw;
36 }
37
39 }
40 else
41 {
42 T result = [&]()
43 {
44 try
45 {
46 T inner_result = f();
47 do_checkpoint();
48 return inner_result;
49 }
50 catch (...)
51 {
52 emergency_unlock();
53 throw;
54 }
55 } ();
56
58 return result;
59 }
60 }
61
62 private:
63 void emergency_unlock() noexcept
64 {
65 try {connection.unlock();} catch (...) {}
66 }
67
68 Writable_Journal &get_writable_journal()
69 {
70 return static_cast<Writable_Journal&>(Client::journal);
71 }
72
73 const Content_Check content_check;
74 bool use_valid_data = false;
75 bool use_timestamp = false;
76 bool use_hard_checkpoint = false;
77
78 void do_checkpoint()
79 {
80 if (use_valid_data)
81 get_writable_journal().valid_data();
82
83 if (use_timestamp)
84 get_writable_journal().timestamp(std::time(nullptr));
85
86 get_writable_journal().soft_checkpoint();
87
88 if (use_hard_checkpoint)
89 get_writable_journal().hard_checkpoint();
90 }
91
92 Tail_Exclusive_Lock get_tail_lock()
93 {
95 throw Exception("pulling when ahead of connection");
96
97 Tail_Exclusive_Lock lock(get_writable_journal());
98
99 connection.check_matching_content
100 (
101 get_writable_journal(),
102 content_check,
105 );
106
107 return lock;
108 }
109
110 void start_transaction()
111 {
113 (
116 get_writable_journal()
117 );
118
119 try
120 {
121 read_journal();
122 }
123 catch (...)
124 {
125 emergency_unlock();
126 throw;
127 }
128 }
129
130 public:
132 (
135 Content_Check content_check = Content_Check::fast
136 ):
137 Client(journal, connection, content_check),
138 content_check(content_check)
139 {
140 }
141
142 void touch()
143 {
144 get_writable_journal().touch();
145 }
146
147 /// Automatically write valid_data at every checkpoint (default = false)
148 void set_valid_data(bool b) {use_valid_data = b;}
149
150 /// Automatically write time stamp at every checkpoint (default = false)
151 void set_timestamp(bool b) {use_timestamp = b;}
152
153 /// Use hard checkpoints (default = false)
154 void set_hard_checkpoint(bool b) {use_hard_checkpoint = b;}
155
156 /// @param wait indicates how long the connection may wait for new data
157 ///
158 /// @return number of bytes pulled
159 int64_t pull
160 (
161 std::chrono::milliseconds wait = std::chrono::milliseconds(0)
162 ) override
163 {
164 const int64_t old_checkpoint = get_journal_checkpoint();
165
166 const Tail_Exclusive_Lock lock(get_tail_lock());
167
169 (
172 get_writable_journal(),
173 wait
174 );
175
176 read_journal();
177
178 return get_journal_checkpoint() - old_checkpoint;
179 }
180
181 int64_t push_unlock()
182 {
184 }
185
186 int64_t push_if_ahead(int64_t until) override
187 {
190 else
192 }
193
195 };
196
197 /// Lock object that allows writing to a database managed by a joedb::Client
198 ///
199 /// At the end of the life of this object, right before destruction, you
200 /// should call either @ref unlock to cancel the transaction, or
201 /// @ref push_unlock to confirm it. If you fail to do so, the destructor
202 /// will call @ref unlock. But calling unlock explicitly is better,
203 /// if possible, because it can throw exceptions, unlike the destructor.
204 ///
205 /// @ingroup concurrency
207 {
208 protected:
211 bool locked;
212
213 public:
215 client(client),
216 journal_lock(client.get_tail_lock()),
217 locked(true)
218 {
219 client.start_transaction();
220 }
221
222 Client_Lock(const Client_Lock &) = delete;
224
225 /// Checkpoint current journal, but do not push yet
227 {
229 client.do_checkpoint();
230 }
231
232 /// Push if the journal checkpoint is ahead of the connection checkpoint
233 ///
234 /// This function keeps the connection locked
236 {
238 if (client.get_journal_checkpoint() > client.get_connection_checkpoint())
240 }
241
242 /// Checkpoint current journal, and push to the connection
243 ///
244 /// Unlike @ref push_unlock, you can call this function multiple
245 /// times during the life of the lock.
247 {
249 client.do_checkpoint();
251 }
252
253 /// Confirm the transaction right before lock destruction
254 ///
255 /// Destruction should happen right after this function.
256 /// Do not call any other member function after this one.
258 {
260 client.do_checkpoint();
262 locked = false;
263 }
264
265 /// Cancel the transaction right before lock destruction
266 ///
267 /// Destruction should happen right after this function.
268 /// Do not call any other member function after this one.
269 void unlock()
270 {
272 client.connection.unlock();
273 locked = false;
274 }
275
276 /// The destructor unlocks the connection if necessary
278 {
279 if (locked)
280 {
281 Global_Logger::warning("Client_Lock destructor: locked, cancelling transaction");
282 try { unlock(); } catch (...) {}
283 }
284 }
285 };
286}
287
288#endif
const Tail_Exclusive_Lock journal_lock
void checkpoint_and_push_unlock()
Confirm the transaction right before lock destruction.
Writable_Client & client
void do_checkpoint()
Checkpoint current journal, but do not push yet.
void push_if_ahead()
Push if the journal checkpoint is ahead of the connection checkpoint.
Client_Lock(Writable_Client &client)
void checkpoint_and_push()
Checkpoint current journal, and push to the connection.
void unlock()
Cancel the transaction right before lock destruction.
Client_Lock(const Client_Lock &)=delete
~Client_Lock()
The destructor unlocks the connection if necessary.
Client_Lock & operator=(const Client_Lock &)=delete
virtual void read_journal()
Definition Client.h:14
int64_t connection_checkpoint
Definition Client.h:17
int64_t get_journal_checkpoint() const
Definition Client.h:67
int64_t push(int64_t until, Unlock_Action unlock_action)
Definition Client.h:19
Readonly_Journal & journal
Definition Client.h:15
Connection & connection
Definition Client.h:16
virtual int64_t push_if_ahead(int64_t until)=0
int64_t get_connection_checkpoint() const
Definition Client.h:77
Client(Readonly_Journal &journal, Connection &connection, Content_Check content_check)
Definition Client.h:41
static Connection dummy
Since this class has no internal state, this global variable can be used instead of creating an insta...
Definition Connection.h:116
virtual void unlock()
Unlock the connection.
static void warning(beman::cstring_view message) noexcept
Writable specialization of Client.
void set_valid_data(bool b)
Automatically write valid_data at every checkpoint (default = false).
void set_hard_checkpoint(bool b)
Use hard checkpoints (default = false).
void set_timestamp(bool b)
Automatically write time stamp at every checkpoint (default = false).
int64_t pull(std::chrono::milliseconds wait=std::chrono::milliseconds(0)) override
Writable_Client(Writable_Journal &journal, Connection &connection=Connection::dummy, Content_Check content_check=Content_Check::fast)
int64_t push_if_ahead(int64_t until) override
Content_Check
Definition Connection.h:20
#define JOEDB_DEBUG_ASSERT(x)
assertion tested in debug mode
Definition assert.h:19