Qore Programming Language 2.0.0
Loading...
Searching...
No Matches
RSet.h
1/* -*- mode: c++; indent-tabs-mode: nil -*- */
2/*
3 RSet.h
4
5 Qore Programming Language
6
7 Copyright (C) 2003 - 2024 Qore Technologies, s.r.o.
8
9 Permission is hereby granted, free of charge, to any person obtaining a
10 copy of this software and associated documentation files (the "Software"),
11 to deal in the Software without restriction, including without limitation
12 the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 and/or sell copies of the Software, and to permit persons to whom the
14 Software is furnished to do so, subject to the following conditions:
15
16 The above copyright notice and this permission notice shall be included in
17 all copies or substantial portions of the Software.
18
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 DEALINGS IN THE SOFTWARE.
26
27 Note that the Qore library is released under a choice of three open-source
28 licenses: MIT (as above), LGPL 2+, or GPL 2+; see README-LICENSE for more
29 information.
30*/
31
32#ifndef _QORE_INTERN_RSETHELPER_H
33
34#define _QORE_INTERN_RSETHELPER_H
35
36#include "qore/intern/RSection.h"
37#include "qore/vector_set"
38#include "qore/vector_map"
39
40#include <set>
41#include <atomic>
42
43class RSet;
44class RSetHelper;
45
46class RObject {
47 friend class robject_dereference_helper;
48
49public:
50 // read-write lock with special rsection handling
51 mutable RSectionLock rml;
52 // weak references
54
55 // ensures atomicity of robject reference counting and notification actions
56 mutable QoreThreadLock rlck;
57
58 QoreCondition rcond; // condition variable (used with rlck)
59
60 int rscan = 0, // TID flag for starting a recursive scan
61 rcount = 0, // the number of unique recursive references to this object
62 rwaiting = 0, // the number of threads waiting for a scan of this object
63 rcycle = 0, // the recursive cycle/transaction number to see if the object has been scanned since a transaction restart
64 ref_inprogress = 0, // the number of dereference actions in progress
65 ref_waiting = 0, // the number of threads waiting on a dereference action to complete
66 rref_waiting = 0, // the number of threads waiting on an rset invalidation to complete
67 rrefs = 0; // the number of "real" refs (i.e. refs not possibly part of a recursive graph)
68
69 // set of objects in a cyclic directed graph
70 RSet* rset = nullptr;
71
72 // reference count
73 std::atomic_int& references;
74
75 bool deferred_scan : 1, // do we need to make a scan when the object is eligible for it?
76 needs_is_valid : 1, // do we need to call isValidImpl()
77 rref_wait : 1; // rset invalidation in progress
78
79 DLLLOCAL RObject(std::atomic_int& n_refs, bool niv = false) :
80 references(n_refs), deferred_scan(false), needs_is_valid(niv), rref_wait(false) {
81 }
82
83 DLLLOCAL virtual ~RObject();
84
85 DLLLOCAL void tRef() const {
86#ifdef QORE_DEBUG_OBJ_REFS
87 printd(QORE_DEBUG_OBJ_REFS, "RObject::tRef() this: %p tref %d->%d\n", this, tRefs.reference_count(), tRefs.reference_count() + 1);
88#endif
89 tRefs.ROreference();
90 }
91
92 DLLLOCAL void tDeref() {
93#ifdef QORE_DEBUG_OBJ_REFS
94 printd(QORE_DEBUG_OBJ_REFS, "RObject::tDeref() this: %p tref %d->%d\n", this, tRefs.reference_count(), tRefs.reference_count() - 1);
95#endif
96 if (tRefs.ROdereference())
97 deleteObject();
98 }
99
100 // real: decrement rref too
101 // do_scan: is the object eleigible for a scan? (rrefs = 0)
102 // rescan: do we need to force a rescan of the object?
103 // return value: the final reference value after the deref
104 DLLLOCAL int deref(bool real, bool& do_scan, bool& rescan);
105
106 // decrements rref
107 DLLLOCAL void derefRealIntern();
108
109 DLLLOCAL void derefDone(bool del);
110
111 DLLLOCAL int refs() const {
112 return references;
113 }
114
115 DLLLOCAL void setRSet(RSet* rs, int rcnt);
116
117 // check if we should defer the scan, marks the object for a deferred scan if necessary
118 // returns 0 if the scan can be made now, -1 if deferred
119 DLLLOCAL int checkDeferScan();
120
121 DLLLOCAL void removeInvalidateRSet();
122 DLLLOCAL void removeInvalidateRSetIntern();
123
124 DLLLOCAL bool scanCheck(RSetHelper& rsh, AbstractQoreNode* n);
125
126 // very fast check if the object might have recursive references
127 DLLLOCAL bool mightHaveRecursiveReferences() const {
128 return rset || rcount;
129 }
130
131 // if the object is valid (and can be deleted)
132 DLLLOCAL bool isValid() const {
133 return !needs_is_valid ? true : isValidImpl();
134 }
135
136 // if the object is valid (and can be deleted)
137 DLLLOCAL virtual bool isValidImpl() const {
138 assert(false);
139 return true;
140 }
141
142 // returns true if a lock error has occurred and the transaction should be aborted or restarted; the rsection lock is held when this function is called
143 DLLLOCAL virtual bool scanMembers(RSetHelper& rsh) = 0;
144
145 // returns true if the object needs to be scanned for recursive references (ie could contain an object or closure or a container containing one of those)
148 DLLLOCAL virtual bool needsScan(bool scan_now) = 0;
149
150 // deletes the object itself
151 DLLLOCAL virtual void deleteObject() = 0;
152
153 // returns the name of the object
154 DLLLOCAL virtual const char* getName() const = 0;
155};
156
157// use a vector set for performance
158typedef vector_set_t<RObject*> rset_t;
159
160/* Qore recursive reference handling works as follows: objects are sorted into sets making up
161 directed cyclic graphs.
162
163 The objects go out of scope when all nodes have references = the number of recursive references.
164
165 If any one member still has valid references (meaning a reference count > # of recursive refs), then *none*
166 of the members of the graph can be dereferenced.
167 */
168
169// set of objects in recursive directed graph
170class RSet {
171protected:
172 rset_t set;
173 unsigned acnt;
174 bool valid;
175
176 // called with the write lock held
177 DLLLOCAL void invalidateIntern() {
178 assert(valid);
179 valid = false;
180 // remove the weak references to all contained objects
181 for (rset_t::iterator i = begin(), e = end(); i != e; ++i)
182 (*i)->tDeref();
183 clear();
184 //printd(6, "RSet::invalidateIntern() this: %p\n", this);
185 }
186
187public:
188 QoreRWLock rwl;
189
190 DLLLOCAL RSet() : acnt(0), valid(true) {
191 }
192
193 DLLLOCAL RSet(RObject* o) : acnt(0), valid(true) {
194 set.insert(o);
195 }
196
197 DLLLOCAL RSet(bool n_valid) : acnt(1), valid(n_valid) {
198 }
199
200 DLLLOCAL ~RSet() {
201 //printd(5, "RSet::~RSet() this: %p (acnt: %d)\n", this, acnt);
202 assert(!acnt);
203 }
204
205 DLLLOCAL void deref() {
206 bool del = false;
207 {
208 QoreAutoRWWriteLocker al(rwl);
209 if (valid)
210 valid = false;
211 //printd(5, "RSet::deref() this: %p %d -> %d\n", this, acnt, acnt - 1);
212 assert(acnt > 0);
213 del = !--acnt;
214 }
215 if (del)
216 delete this;
217 }
218
219 DLLLOCAL void invalidate() {
220 QoreAutoRWWriteLocker al(rwl);
221 if (valid)
222 invalidateIntern();
223 }
224
225 DLLLOCAL void invalidateDeref() {
226 bool del = false;
227 {
228 QoreAutoRWWriteLocker al(rwl);
229 if (valid) {
230 invalidateIntern();
231 valid = false;
232 }
233 //printd(5, "RSet::invalidateDeref() this: %p %d -> %d\n", this, acnt, acnt - 1);
234 assert(acnt > 0);
235 del = !--acnt;
236 }
237 if (del)
238 delete this;
239 }
240
241 DLLLOCAL void ref() {
242 QoreAutoRWWriteLocker al(rwl);
243 ++acnt;
244 }
245
246 DLLLOCAL bool active() const {
247 return valid;
248 }
249
250 /* return values:
251 -1: error, rset invalid
252 0: cannot delete
253 1: the rset has been invalidated already, the object can be deleted
254 */
255 DLLLOCAL int canDelete(int ref_copy, int rcount);
256
257#ifdef DEBUG
258 DLLLOCAL void dbg();
259
260 DLLLOCAL static bool isValid(const RSet* rs) {
261 return rs ? rs->valid : false;
262 }
263#endif
264
265 DLLLOCAL bool assigned() const {
266 return (bool)acnt;
267 }
268
269 DLLLOCAL void insert(RObject* o) {
270 assert(set.find(o) == set.end());
271 set.insert(o);
272 }
273
274 DLLLOCAL void clear() {
275 set.clear();
276 }
277
278 DLLLOCAL rset_t::iterator begin() {
279 return set.begin();
280 }
281
282 DLLLOCAL rset_t::iterator end() {
283 return set.end();
284 }
285
286 DLLLOCAL rset_t::iterator find(RObject* o) {
287 return set.find(o);
288 }
289
290 DLLLOCAL size_t size() const {
291 return set.size();
292 }
293
294 // called when rolling back an rset transaction
295 DLLLOCAL bool pop() {
296 assert(!set.empty());
297 set.erase(set.begin());
298 return set.empty();
299 }
300
301#ifdef DEBUG
302 DLLLOCAL unsigned getCount() const {
303 return acnt;
304 }
305#endif
306};
307
308typedef std::vector<RObject*> rvec_t;
309class RSetHelper;
310//typedef std::set<RSet*> rs_set_t;
311typedef vector_set_t<RSet*> rs_set_t;
312
313hashdecl RSetStat {
314 RSet* rset = nullptr;
315 int rcount = 0;
316 bool in_cycle : 1,
317 ok : 1;
318
319 DLLLOCAL RSetStat() : in_cycle(false), ok(false) {
320 }
321
322 DLLLOCAL RSetStat(const RSetStat& old) : rset(old.rset), rcount(old.rcount), in_cycle(old.in_cycle), ok(old.ok) {
323 }
324
325 DLLLOCAL void finalize(RSet* rs = nullptr) {
326 assert(!ok);
327 assert(!rset);
328 rset = rs;
329 }
330};
331
332class QoreClosureBase;
333
334class RSetHelper {
335 friend class RSectionScanHelper;
336 friend class RObject;
337private:
338 DLLLOCAL RSetHelper(const RSetHelper&);
339
340protected:
341 // these must be a map and a set for performance reasons
342 typedef std::map<RObject*, RSetStat> omap_t;
343 typedef std::set<QoreClosureBase*> closure_set_t;
344 // map of all objects scanned to rset (rset = finalized, 0 = not finalized, in current list)
345 omap_t fomap;
346
347 typedef std::vector<omap_t::iterator> ovec_t;
348 // current objects being scanned, used to establish a cycle
349 ovec_t ovec;
350
351 // list of RSet objects to be invalidated when the transaction is committed
352 rs_set_t tr_invalidate;
353
354 // set of RObjects not participating in any recursive set
355 rset_t tr_out;
356
357 // RSectionLock notification helper when waiting on locks
358 RNotifier notifier;
359
360 // set of scanned closures
361 closure_set_t closure_set;
362
363#ifdef DEBUG
364 int lcnt;
365 DLLLOCAL void inccnt() { ++lcnt; }
366 DLLLOCAL void deccnt() { --lcnt; }
367#else
368 DLLLOCAL void inccnt() {}
369 DLLLOCAL void deccnt() {}
370#endif
371
372 // rollback transaction due to lock error
373 DLLLOCAL void rollback();
374
375 // commit transaction
376 DLLLOCAL void commit(RObject& obj);
377
378 // returns true if a lock error has occurred, false if otherwise
379 DLLLOCAL bool checkIntern(RObject& obj);
380 // returns true if a lock error has occurred, false if otherwise
381 DLLLOCAL bool checkIntern(AbstractQoreNode* n);
382
383 // queues nodes not scanned to tr_invalidate and tr_out
384 DLLLOCAL bool removeInvalidate(RSet* ors, int tid = q_gettid());
385
386 DLLLOCAL bool inCurrentSet(omap_t::iterator fi) {
387 for (size_t i = 0; i < ovec.size(); ++i)
388 if (ovec[i] == fi)
389 return true;
390 return false;
391 }
392
393 DLLLOCAL bool addToRSet(omap_t::iterator oi, RSet* rset, int tid);
394
395 DLLLOCAL void mergeRSet(int i, RSet*& rset);
396
397 DLLLOCAL bool makeChain(int i, omap_t::iterator fi, int tid);
398
399public:
400 DLLLOCAL RSetHelper(RObject& obj);
401
402 DLLLOCAL ~RSetHelper() {
403 assert(ovec.empty());
404 assert(!lcnt);
405 }
406
407 DLLLOCAL unsigned size() const {
408 return fomap.size();
409 }
410
411 DLLLOCAL void add(RObject* ro) {
412 if (fomap.find(ro) != fomap.end())
413 return;
414 rset_t::iterator i = tr_out.lower_bound(ro);
415 if (i == tr_out.end() || *i != ro)
416 tr_out.insert(i, ro);
417 }
418
419 // returns true if a lock error has occurred, false if otherwise
420 DLLLOCAL bool checkNode(AbstractQoreNode* n) {
421 return checkIntern(n);
422 }
423
424 // returns true if a lock error has occurred, false if otherwise
425 DLLLOCAL bool checkNode(RObject& robj) {
426 return checkIntern(robj);
427 }
428};
429
430class qore_object_private;
431
435protected:
436 RObject* o;
437 qore_object_private* qo = nullptr;
438 int refs;
439 bool del,
440 do_scan = false,
441 deferred_scan;
442
443public:
444 DLLLOCAL robject_dereference_helper(RObject* obj, bool real = false);
445
447
448 // return our reference count as captured atomically in the constructor
449 DLLLOCAL int getRefs() const {
450 return refs;
451 }
452
453 // return an indicator if we have a deferred scan or not
454 DLLLOCAL bool deferredScan() {
455 if (deferred_scan) {
456 deferred_scan = false;
457 return true;
458 }
459 return false;
460 }
461
462 // return an indicator if we should do a scan or not
463 DLLLOCAL bool doScan() const {
464 return do_scan;
465 }
466
467 // mark for final dereferencing
468 DLLLOCAL void finalDeref(qore_object_private* obj) {
469 assert(!qo);
470 qo = obj;
471 }
472
473 // mark that we will be deleting the object
474 // (and therefore need to wait for any in progress dereferences to complete before deleting)
475 DLLLOCAL void willDelete() {
476 assert(!del);
477 del = true;
478 }
479};
480
481#endif
The base class for all value and parse types in Qore expression trees.
Definition AbstractQoreNode.h:57
provides a safe and exception-safe way to hold write locks in Qore, only to be used on the stack,...
Definition QoreRWLock.h:144
a thread condition class implementing a wrapper for pthread_cond_t
Definition QoreCondition.h:45
provides a simple POSIX-threads-based read-write lock
Definition QoreRWLock.h:47
Provides atomic reference counting to Qore objects.
Definition QoreReferenceCounter.h:44
DLLEXPORT void ROreference() const
Atomically increments the reference count.
DLLEXPORT int reference_count() const
Gets the reference count.
DLLEXPORT bool ROdereference() const
Atomically decrements the reference count.
provides a mutually-exclusive thread lock
Definition QoreThreadLock.h:49
Definition RSet.h:434
DLLEXPORT int q_gettid() noexcept
returns the current TID number