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