Qore Programming Language 2.1.2
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#ifdef _QORE_CYCLE_CHECK
73 // set of objects pointing at this object
74 std::set<RObject*> refmap;
75#endif
76
77 // reference count
78 std::atomic_int& references;
79
80 bool deferred_scan : 1, // do we need to make a scan when the object is eligible for it?
81 needs_is_valid : 1, // do we need to call isValidImpl()
82 rref_wait : 1; // rset invalidation in progress
83
84 DLLLOCAL RObject(std::atomic_int& n_refs, bool niv = false) :
85 references(n_refs), deferred_scan(false), needs_is_valid(niv), rref_wait(false) {
86 }
87
88 DLLLOCAL virtual ~RObject();
89
90 DLLLOCAL void tRef() const {
91#ifdef QORE_DEBUG_OBJ_REFS
92 printd(QORE_DEBUG_OBJ_REFS, "RObject::tRef() this: %p tref %d->%d\n", this, tRefs.reference_count(),
93 tRefs.reference_count() + 1);
94#endif
95 tRefs.ROreference();
96 }
97
98 DLLLOCAL void tDeref() {
99#ifdef QORE_DEBUG_OBJ_REFS
100 printd(QORE_DEBUG_OBJ_REFS, "RObject::tDeref() this: %p tref %d->%d\n", this, tRefs.reference_count(),
101 tRefs.reference_count() - 1);
102#endif
103 if (tRefs.ROdereference())
104 deleteObject();
105 }
106
107 // real: decrement rref too
108 // do_scan: is the object eleigible for a scan? (rrefs = 0)
109 // rescan: do we need to force a rescan of the object?
110 // return value: the final reference value after the deref
111 DLLLOCAL int deref(bool real, bool& do_scan, bool& rescan);
112
113 // decrements rref
114 DLLLOCAL void derefRealIntern();
115
116 DLLLOCAL void derefDone(bool del);
117
118 DLLLOCAL int refs() const {
119 return references;
120 }
121
122 DLLLOCAL void setRSet(RSet* rs, int rcnt);
123
124 // check if we should defer the scan, marks the object for a deferred scan if necessary
125 // returns 0 if the scan can be made now, -1 if deferred
126 DLLLOCAL int checkDeferScan();
127
128 DLLLOCAL void removeInvalidateRSet();
129 DLLLOCAL void removeInvalidateRSetIntern();
130
131 DLLLOCAL bool scanCheck(RSetHelper& rsh, AbstractQoreNode* n);
132
133 // very fast check if the object might have recursive references
134 DLLLOCAL bool mightHaveRecursiveReferences() const {
135 return rset || rcount;
136 }
137
138 // if the object is valid (and can be deleted)
139 DLLLOCAL bool isValid() const {
140 return !needs_is_valid ? true : isValidImpl();
141 }
142
143 // if the object is valid (and can be deleted)
144 DLLLOCAL virtual bool isValidImpl() const {
145 assert(false);
146 return true;
147 }
148
149 // 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
150 DLLLOCAL virtual bool scanMembers(RSetHelper& rsh) = 0;
151
152 // 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)
155 DLLLOCAL virtual bool needsScan(bool scan_now) = 0;
156
157 // deletes the object itself
158 DLLLOCAL virtual void deleteObject() = 0;
159
160 // returns the name of the object
161 DLLLOCAL virtual const char* getName() const = 0;
162};
163
164// use a vector set for performance
165typedef vector_set_t<RObject*> rset_t;
166
167/* Qore recursive reference handling works as follows: objects are sorted into sets making up
168 directed cyclic graphs.
169
170 The objects go out of scope when all nodes have references = the number of recursive references.
171
172 If any one member still has valid references (meaning a reference count > # of recursive refs), then *none*
173 of the members of the graph can be dereferenced.
174 */
175
176// set of objects in a recursive directed graph
177class RSet {
178public:
179 QoreRWLock rwl;
180
181 DLLLOCAL RSet() : acnt(0), valid(true) {
182 }
183
184 DLLLOCAL RSet(RObject* o) : acnt(0), valid(true) {
185 set.insert(o);
186 }
187
188 DLLLOCAL RSet(bool n_valid) : acnt(1), valid(n_valid) {
189 }
190
191 DLLLOCAL ~RSet() {
192 //printd(5, "RSet::~RSet() this: %p (acnt: %d)\n", this, acnt);
193 assert(!acnt);
194 }
195
196 DLLLOCAL void deref() {
197 bool del = false;
198 {
199 QoreAutoRWWriteLocker al(rwl);
200 if (valid) {
201 valid = false;
202 }
203 //printd(5, "RSet::deref() this: %p %d -> %d\n", this, acnt, acnt - 1);
204 assert(acnt > 0);
205 del = !--acnt;
206 }
207 if (del) {
208 delete this;
209 }
210 }
211
212 DLLLOCAL void invalidate() {
213 QoreAutoRWWriteLocker al(rwl);
214 if (valid) {
215 invalidateIntern();
216 }
217 }
218
219 DLLLOCAL void invalidateDeref() {
220 bool del = false;
221 {
222 QoreAutoRWWriteLocker al(rwl);
223 if (valid) {
224 invalidateIntern();
225 valid = false;
226 }
227 //printd(5, "RSet::invalidateDeref() this: %p %d -> %d\n", this, acnt, acnt - 1);
228 assert(acnt > 0);
229 del = !--acnt;
230 }
231 if (del) {
232 delete this;
233 }
234 }
235
236 DLLLOCAL void ref() {
237 QoreAutoRWWriteLocker al(rwl);
238 ++acnt;
239 }
240
241 DLLLOCAL bool active() const {
242 return valid;
243 }
244
245 /* return values:
246 -1: error, rset invalid
247 0: cannot delete
248 1: the rset has been invalidated already, the object can be deleted
249 */
250 DLLLOCAL int canDelete(int ref_copy, int rcount);
251
252#ifdef DEBUG
253 DLLLOCAL void dbg();
254
255 DLLLOCAL static bool isValid(const RSet* rs) {
256 return rs ? rs->valid : false;
257 }
258#endif
259
260 DLLLOCAL bool assigned() const {
261 return (bool)acnt;
262 }
263
264 DLLLOCAL void insert(RObject* o) {
265 assert(set.find(o) == set.end());
266 set.insert(o);
267 }
268
269 DLLLOCAL void clear() {
270 set.clear();
271 }
272
273 DLLLOCAL rset_t::iterator begin() {
274 return set.begin();
275 }
276
277 DLLLOCAL rset_t::iterator end() {
278 return set.end();
279 }
280
281 DLLLOCAL rset_t::iterator find(RObject* o) {
282 return set.find(o);
283 }
284
285 DLLLOCAL size_t size() const {
286 return set.size();
287 }
288
289 // called when rolling back an rset transaction
290 DLLLOCAL bool pop() {
291 assert(!set.empty());
292 set.erase(set.begin());
293 return set.empty();
294 }
295
296#ifdef DEBUG
297 DLLLOCAL unsigned getCount() const {
298 return acnt;
299 }
300#endif
301
302protected:
303 rset_t set;
304 unsigned acnt;
305 bool valid;
306
307 // called with the write lock held
308 DLLLOCAL void invalidateIntern() {
309 assert(valid);
310 valid = false;
311 // remove the weak references to all contained objects
312 for (rset_t::iterator i = begin(), e = end(); i != e; ++i) {
313 (*i)->tDeref();
314 }
315 clear();
316 //printd(6, "RSet::invalidateIntern() this: %p\n", this);
317 }
318};
319
320typedef std::vector<RObject*> rvec_t;
321class RSetHelper;
322//typedef std::set<RSet*> rs_set_t;
323typedef vector_set_t<RSet*> rs_set_t;
324
325hashdecl RSetStat {
326 RSet* rset = nullptr;
327 int rcount = 0;
328 bool in_cycle : 1,
329 ok : 1;
330
331 DLLLOCAL RSetStat() : in_cycle(false), ok(false) {
332 }
333
334 DLLLOCAL RSetStat(const RSetStat& old) : rset(old.rset), rcount(old.rcount), in_cycle(old.in_cycle), ok(old.ok) {
335 }
336
337 DLLLOCAL void finalize(RSet* rs = nullptr) {
338 assert(!ok);
339 assert(!rset);
340 rset = rs;
341 }
342};
343
344class QoreClosureBase;
345
346class RSetHelper {
347 friend class RSectionScanHelper;
348 friend class RObject;
349#ifdef _QORE_CYCLE_CHECK
350 friend class RSetContextHelper;
351 friend class RSetScanContextHelper;
352#endif
353public:
354 DLLLOCAL RSetHelper(RObject& obj);
355
356 DLLLOCAL ~RSetHelper() {
357 assert(ovec.empty());
358 assert(!lcnt);
359 }
360
361 DLLLOCAL unsigned size() const {
362 return fomap.size();
363 }
364
365 DLLLOCAL void add(RObject* ro) {
366 if (fomap.find(ro) != fomap.end())
367 return;
368 rset_t::iterator i = tr_out.lower_bound(ro);
369 if (i == tr_out.end() || *i != ro)
370 tr_out.insert(i, ro);
371 }
372
373 // returns true if a lock error has occurred, false if otherwise
374 DLLLOCAL bool checkNode(AbstractQoreNode* n) {
375 return checkIntern(n);
376 }
377
378 // returns true if a lock error has occurred, false if otherwise
379 DLLLOCAL bool checkNode(RObject& robj) {
380 return checkIntern(robj);
381 }
382
383#ifdef _QORE_CYCLE_CHECK
384 DLLLOCAL void setScanContext(RObject* scan_context) {
385 this->scan_context = scan_context;
386 }
387#endif
388
389protected:
390 // these must be a map and a set for performance reasons
391 typedef std::map<RObject*, RSetStat> omap_t;
392 typedef std::set<QoreClosureBase*> closure_set_t;
393 // map of all objects scanned to rset (rset = finalized, 0 = not finalized, in current list)
394 omap_t fomap;
395
396 // map scanned hashes to ensure they are only scanned once
397 typedef std::set<QoreHashNode*> hset_t;
398 hset_t hset;
399
400 // map scanned lists to ensure they are only scanned once
401 typedef std::set<QoreListNode*> lset_t;
402 lset_t lset;
403
404 typedef std::vector<omap_t::iterator> ovec_t;
405 // current objects being scanned, used to establish a cycle
406 ovec_t ovec;
407
408 // list of RSet objects to be invalidated when the transaction is committed
409 rs_set_t tr_invalidate;
410
411 // set of RObjects not participating in any recursive set
412 rset_t tr_out;
413
414 // RSectionLock notification helper when waiting on locks
415 RNotifier notifier;
416
417 // set of scanned closures
418 closure_set_t closure_set;
419
420#ifdef _QORE_CYCLE_CHECK
421 RObject* scan_context = nullptr;
422#endif
423
424#ifdef DEBUG
425 int lcnt;
426 DLLLOCAL void inccnt() { ++lcnt; }
427 DLLLOCAL void deccnt() { --lcnt; }
428#else
429 DLLLOCAL void inccnt() {}
430 DLLLOCAL void deccnt() {}
431#endif
432
433 // rollback transaction due to lock error
434 DLLLOCAL void rollback();
435
436 // commit transaction
437 DLLLOCAL void commit(RObject& obj);
438
439 // returns true if a lock error has occurred, false if otherwise
440 DLLLOCAL bool checkIntern(RObject& obj);
441 // returns true if a lock error has occurred, false if otherwise
442 DLLLOCAL bool checkIntern(AbstractQoreNode* n);
443
444 // queues nodes not scanned to tr_invalidate and tr_out
445 DLLLOCAL bool removeInvalidate(RSet* ors, int tid = q_gettid());
446
447 DLLLOCAL bool inCurrentSet(omap_t::iterator fi) {
448 for (size_t i = 0; i < ovec.size(); ++i) {
449 if (ovec[i] == fi) {
450 return true;
451 }
452 }
453 return false;
454 }
455
456 // returns true if there is a lock failure
457 DLLLOCAL bool addToRSet(omap_t::iterator oi, RSet* rset, int tid);
458
459 DLLLOCAL void mergeRSet(int i, RSet*& rset);
460
461 DLLLOCAL void mergeRSetIntern(RSet*& rset, RSet* old_rset);
462
463 DLLLOCAL bool makeChain(int i, omap_t::iterator fi, int tid);
464
465#ifdef _QORE_CYCLE_CHECK
466 DLLLOCAL void setObjectScanContext(RObject& obj, int rcount);
467#endif
468
469private:
470 DLLLOCAL RSetHelper(const RSetHelper&);
471};
472
473#ifdef _QORE_CYCLE_CHECK
474class RSetContextHelper {
475public:
476 DLLLOCAL RSetContextHelper(RSetHelper& rsh) : rsh(rsh), scan_context_save(rsh.scan_context) {
477 }
478
479 DLLLOCAL ~RSetContextHelper() {
480 if (rsh.scan_context != scan_context_save) {
481 rsh.scan_context = scan_context_save;
482 }
483 }
484
485private:
486 RSetHelper& rsh;
487 RObject* scan_context_save;
488};
489
490class RSetScanContextHelper {
491public:
492 DLLLOCAL RSetScanContextHelper(RSetHelper& rsh, RObject* new_context) : rsh(rsh),
493 scan_context_save(rsh.scan_context) {
494 rsh.scan_context = new_context;
495 }
496
497 DLLLOCAL ~RSetScanContextHelper() {
498 if (rsh.scan_context != scan_context_save) {
499 rsh.scan_context = scan_context_save;
500 }
501 }
502
503private:
504 RSetHelper& rsh;
505 RObject* scan_context_save;
506};
507#endif
508
509class qore_object_private;
510
514protected:
515 RObject* o;
516 qore_object_private* qo = nullptr;
517 int refs;
518 bool del,
519 do_scan = false,
520 deferred_scan;
521
522public:
523 DLLLOCAL robject_dereference_helper(RObject* obj, bool real = false);
524
526
527 // return our reference count as captured atomically in the constructor
528 DLLLOCAL int getRefs() const {
529 return refs;
530 }
531
532 // return an indicator if we have a deferred scan or not
533 DLLLOCAL bool deferredScan() {
534 if (deferred_scan) {
535 deferred_scan = false;
536 return true;
537 }
538 return false;
539 }
540
541 // return an indicator if we should do a scan or not
542 DLLLOCAL bool doScan() const {
543 return do_scan;
544 }
545
546 // mark for final dereferencing
547 DLLLOCAL void finalDeref(qore_object_private* obj) {
548 assert(!qo);
549 qo = obj;
550 }
551
552 // mark that we will be deleting the object
553 // (and therefore need to wait for any in progress dereferences to complete before deleting)
554 DLLLOCAL void willDelete() {
555 assert(!del);
556 del = true;
557 }
558};
559
560#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:143
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:513
DLLEXPORT int q_gettid() noexcept
returns the current TID number