Qore Programming Language  1.12.0
RSet.h
1 /* -*- mode: c++; indent-tabs-mode: nil -*- */
2 /*
3  RSet.h
4 
5  Qore Programming Language
6 
7  Copyright (C) 2003 - 2022 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 
43 class RSet;
44 class RSetHelper;
45 
46 class RObject {
47  friend class robject_dereference_helper;
48 
49 public:
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
161 typedef 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
173 class RSet {
174 protected:
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 
190 public:
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 
312 typedef std::vector<RObject*> rvec_t;
313 class RSetHelper;
314 //typedef std::set<RSet*> rs_set_t;
315 typedef vector_set_t<RSet*> rs_set_t;
316 
317 hashdecl 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 
336 class QoreClosureBase;
337 
338 class RSetHelper {
339  friend class RSectionScanHelper;
340  friend class RObject;
341 private:
342  DLLLOCAL RSetHelper(const RSetHelper&);
343 
344 protected:
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 
403 public:
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 
434 class qore_object_private;
435 
439 protected:
440  RObject* o;
441  qore_object_private* qo;
442  int refs;
443  bool del,
444  do_scan,
445  deferred_scan;
446 
447 public:
448  DLLLOCAL robject_dereference_helper(RObject* obj, bool real = false);
449 
450  DLLLOCAL ~robject_dereference_helper();
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