OpenCog Framework  Branch: master, revision 6f0b7fc776b08468cf1b74aa9db028f387b4f0c0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
AtomTable.cc
Go to the documentation of this file.
1 /*
2  * opencog/atomspace/AtomTable.cc
3  *
4  * Copyright (C) 2002-2007 Novamente LLC
5  * Copyright (C) 2013-2015 Linas Vepstas <linasvepstas@gmail.com>
6  * All Rights Reserved
7  *
8  * Written by Thiago Maia <thiago@vettatech.com>
9  * Andre Senna <senna@vettalabs.com>
10  * Welter Silva <welter@vettalabs.com>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU Affero General Public License v3 as
14  * published by the Free Software Foundation and including the exceptions
15  * at http://opencog.org/wiki/Licenses
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU Affero General Public License
23  * along with this program; if not, write to:
24  * Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27 
28 #include "AtomTable.h"
29 
30 #include <iterator>
31 #include <set>
32 
33 #include <stdlib.h>
34 #include <boost/bind.hpp>
35 
37 #include <opencog/atomspace/Link.h>
38 #include <opencog/atomspace/Node.h>
39 #include <opencog/atomspace/TLB.h>
41 #include <opencog/atoms/TypeNode.h>
52 #include <opencog/util/exceptions.h>
53 #include <opencog/util/functional.h>
54 #include <opencog/util/Logger.h>
55 
56 //#define DPRINTF printf
57 //#define tableId (0) // Hack around some DPRINTF statements that want an old tableID member variable
58 #define DPRINTF(...)
59 
60 using namespace opencog;
61 
62 std::recursive_mutex AtomTable::_mtx;
63 
65  : _index_queue(this, &AtomTable::put_atom_into_index)
66 {
67  _as = holder;
68  _environ = parent;
70  size = 0;
71 
72  // Set resolver before doing anything else, such as getting
73  // the atom-added signals. Just in case some other thread
74  // is busy adding types while we are being created.
76 
77  // Connect signal to find out about type additions
79  classserver().addTypeSignal().connect(
80  boost::bind(&AtomTable::typeAdded, this, _1));
81 }
82 
84 {
85  // Disconnect signals. Only then clear the resolver.
86  std::lock_guard<std::recursive_mutex> lck(_mtx);
87  addedTypeConnection.disconnect();
89 
90  // No one who shall look at these atoms ahall ever again
91  // find a reference to this atomtable.
92  UUID undef = Handle::UNDEFINED.value();
93  for (const Handle& h : _atom_set) {
94  h->_atomTable = NULL;
95  h->_uuid = undef;
96  }
97 }
98 
99 bool AtomTable::isCleared(void) const
100 {
101  // XXX Currently only check if stuff in derived space is gone. No
102  // checking is done on the parent. This is inline with how clear()
103  // is expected to work.
104 
105  if (size != 0) {
106  DPRINTF("AtomTable::size is not 0\n");
107  return false;
108  }
109 
110  std::lock_guard<std::recursive_mutex> lck(_mtx);
111  // if (nameIndex.size() != 0) return false;
112  if (typeIndex.size() != 0) return false;
113  if (importanceIndex.size() != 0) return false;
114  return true;
115 }
116 
118 {
119  throw opencog::RuntimeException(TRACE_INFO,
120  "AtomTable - Cannot copy an object of this class");
121 }
122 
124  :_index_queue(this, &AtomTable::put_atom_into_index)
125 {
126  throw opencog::RuntimeException(TRACE_INFO,
127  "AtomTable - Cannot copy an object of this class");
128 }
129 
130 Handle AtomTable::getHandle(Type t, std::string name) const
131 {
132  // Special types need validation
133  try {
134  if (NUMBER_NODE == t) {
135  name = NumberNode::validate(name);
136  } else if (TYPE_NODE == t) {
137  TypeNode::validate(name);
138  }
139  }
140  catch (...) { return Handle::UNDEFINED; }
141 
142  std::lock_guard<std::recursive_mutex> lck(_mtx);
143  Atom* atom = nodeIndex.getAtom(t, name);
144  if (atom) return atom->getHandle();
145  if (_environ and NULL == atom)
146  return _environ->getHandle(t, name);
147  return Handle::UNDEFINED;
148 }
149 
154 {
155  const AtomTable *env = this;
156  do {
157  if (n->_atomTable == env) return Handle(n);
158  env = env->_environ;
159  } while (env);
160 
161  return getHandle(n->getType(), n->getName());
162 }
163 
165 {
166  // Make sure all the atoms in the outgoing set are resolved :-)
167  HandleSeq resolved_seq;
168  for (Handle ho : seq) {
169  resolved_seq.push_back(getHandle(ho));
170  }
171 
172  // Aiieee! unordered link!
173  if (classserver().isA(t, UNORDERED_LINK)) {
174  struct HandleComparison
175  {
176  bool operator()(const Handle& h1, const Handle& h2) const {
177  return (Handle::compare(h1, h2) < 0);
178  }
179  };
180  std::sort(resolved_seq.begin(), resolved_seq.end(), HandleComparison());
181  }
182 
183  std::lock_guard<std::recursive_mutex> lck(_mtx);
184  Handle h(linkIndex.getHandle(t, resolved_seq));
185  if (_environ and Handle::UNDEFINED == h)
186  return _environ->getHandle(t, resolved_seq);
187  return h;
188 }
189 
194 {
195  const AtomTable *env = this;
196  do {
197  if (l->_atomTable == env) return Handle(l);
198  env = env->_environ;
199  } while (env);
200 
201  return getHandle(l->getType(), l->getOutgoingSet());
202 }
203 
208 {
209  NodePtr nnn(NodeCast(a));
210  if (nnn)
211  return getHandle(nnn);
212  else {
213  LinkPtr lll(LinkCast(a));
214  if (lll)
215  return getHandle(lll);
216  }
217  return Handle::UNDEFINED;
218 }
219 
221 {
222  // If we have an atom, but don't know the uuid, find uuid.
223  if (Handle::UNDEFINED.value() == h.value())
224  return getHandle(AtomPtr(h));
225 
226  // If we have both a uuid and pointer, AND the pointer is
227  // pointing to an atom that is in this table (not some other
228  // table), then there's nothing to do. Otherwise, we have to
229  // find the equivalent atom in this atomspace.
230  // Note: we access the naked pointer itself; that's because
231  // Handle itself calls this method to resolve null pointers.
232  if (h._ptr) {
233  if (this == h._ptr->_atomTable)
234  return h;
235 
236  if (_environ) {
237  Handle henv = _environ->getHandle(h);
238  if (henv) return henv;
239  }
240  return getHandle(AtomPtr(h));
241  }
242 
243  // Read-lock for the _atom_set.
244  std::lock_guard<std::recursive_mutex> lck(_mtx);
245 
246  // If we have a uuid but no atom pointer, find the atom pointer.
247  auto hit = _atom_set.find(h);
248  if (hit != _atom_set.end())
249  return *hit;
250  return Handle::UNDEFINED;
251 }
252 
253 
257 {
258  AtomTable* atab = atom->getAtomTable();
259  AtomTable* env = this;
260  while (env) {
261  if (atab == env) return true;
262  env = env->_environ;
263  }
264  return false;
265 }
266 
267 // Experimental C++ atom types support code
268 // Try to cast, if possible.
270 {
271  // Nodes of various kinds -----------
272  if (NUMBER_NODE == atom_type) {
273  if (NULL == NumberNodeCast(atom))
274  return createNumberNode(*NodeCast(atom));
275  } else if (TYPE_NODE == atom_type) {
276  if (NULL == TypeNodeCast(atom))
277  return createTypeNode(*NodeCast(atom));
278 
279  // Links of various kinds -----------
280  } else if (BIND_LINK == atom_type) {
281  if (NULL == BindLinkCast(atom))
282  return createBindLink(*LinkCast(atom));
283  } else if (DEFINE_LINK == atom_type) {
284  if (NULL == DefineLinkCast(atom))
285  return createDefineLink(*LinkCast(atom));
286 /*
287  XXX FIXME: cannot do this, due to a circular shared library
288  dependency between python and itself: python depends on
289  ExecutionOutputLink, and ExecutionOutputLink depends on python.
290  Boo. I tried fixing this, but it is hard, somehow.
291 
292 */
293  } else if (EVALUATION_LINK == atom_type) {
294 /*
295  if (NULL == EvaluationLinkCast(atom))
296  return createEvaluationLink(*LinkCast(atom));
297 */
298  } else if (EXECUTION_OUTPUT_LINK == atom_type) {
299 /*
300  if (NULL == ExecutionOutputLinkCast(atom))
301  return createExecutionOutputLink(*LinkCast(atom));
302 */
303  } else if (GET_LINK == atom_type) {
304  if (NULL == PatternLinkCast(atom))
305  return createPatternLink(*LinkCast(atom));
306  } else if (PUT_LINK == atom_type) {
307  if (NULL == PutLinkCast(atom))
308  return createPutLink(*LinkCast(atom));
309  } else if (SATISFACTION_LINK == atom_type) {
310  if (NULL == PatternLinkCast(atom))
311  return createPatternLink(*LinkCast(atom));
312  } else if (LAMBDA_LINK == atom_type) {
313  if (NULL == LambdaLinkCast(atom))
314  return createLambdaLink(*LinkCast(atom));
315  } else if (VARIABLE_LIST == atom_type) {
316  if (NULL == VariableListCast(atom))
317  return createVariableList(*LinkCast(atom));
318  } else if (classserver().isA(atom_type, FUNCTION_LINK)) {
319 /* More circular-dependency heart-ache
320  if (NULL == FunctionLinkCast(atom))
321  return FunctionLink::factory(LinkCast(atom));
322 */
323  }
324  return atom;
325 }
326 
327 // create a clone
329 {
330  // Nodes of various kinds -----------
331  if (NUMBER_NODE == atom_type)
332  return createNumberNode(*NodeCast(atom));
333  if (TYPE_NODE == atom_type)
334  return createTypeNode(*NodeCast(atom));
335  if (classserver().isA(atom_type, NODE))
336  return createNode(*NodeCast(atom));
337 
338  // Links of various kinds -----------
339  if (BIND_LINK == atom_type)
340  return createBindLink(*LinkCast(atom));
341  if (DEFINE_LINK == atom_type)
342  return createDefineLink(*LinkCast(atom));
343 /*
344  XXX FIXME: cannot do this, due to a circular shared library
345  dependency between python and itself: python depends on
346  ExecutionOutputLink, and ExecutionOutputLink depends on python.
347  Boo. I tried fixing this, but it is hard, somehow.
348 */
349  if (EVALUATION_LINK == atom_type)
350  // return createEvaluationLink(*LinkCast(atom));
351  return createLink(*LinkCast(atom));
352  if (EXECUTION_OUTPUT_LINK == atom_type)
353  //return createExecutionOutputLink(*LinkCast(atom));
354  return createLink(*LinkCast(atom));
355  if (GET_LINK == atom_type)
356  return createPatternLink(*LinkCast(atom));
357  if (PUT_LINK == atom_type)
358  return createPutLink(*LinkCast(atom));
359  if (SATISFACTION_LINK == atom_type)
360  return createPatternLink(*LinkCast(atom));
361  if (LAMBDA_LINK == atom_type)
362  return createLambdaLink(*LinkCast(atom));
363  if (VARIABLE_LIST == atom_type)
364  return createVariableList(*LinkCast(atom));
365  if (classserver().isA(atom_type, FUNCTION_LINK))
366  // XXX FIXME more circular-dependency heart-ache
367  // return FunctionLink::factory(LinkCast(atom));
368  return createLink(*LinkCast(atom));
369  if (classserver().isA(atom_type, LINK))
370  return createLink(*LinkCast(atom));
371 
372  throw RuntimeException(TRACE_INFO,
373  "AtomTable - failed factory call!");
374 }
375 
376 static void prt_diag(AtomPtr atom, size_t i, size_t arity, const HandleSeq& ogs)
377 {
378  Logger::Level save = logger().getBackTraceLevel();
379  logger().setBackTraceLevel(Logger::Level::NONE);
380  logger().error() << "AtomTable - Insert link with "
381  "invalid outgoing members";
382  logger().error() << "Failing index i=" << i
383  << " and arity=" << arity;
384  logger().error() << "Failing outset is this:";
385  for (unsigned int fk=0; fk<arity; fk++)
386  logger().error() << "outset i=" << fk
387  << " uuid=" << ogs[fk].value();
388 
389  logger().error() << "link is " << atom->toString();
390  logger().flush();
391  logger().setBackTraceLevel(save);
392 }
393 
395 {
396  // Is the atom already in this table, or one of its environments?
397  if (inEnviron(atom))
398  return atom->getHandle();
399 
400 #if LATER
401  // XXX FIXME -- technically, this throw is correct, except
402  // that SavingLoading gives us atoms with handles preset.
403  // So we have to accept that, and hope its correct and consistent.
404  // XXX this can also occur if the atom is in some other atomspace;
405  // so we need to move this check elsewhere.
406  if (atom->_uuid != Handle::UNDEFINED.value())
407  throw RuntimeException(TRACE_INFO,
408  "AtomTable - Attempting to insert atom with handle already set!");
409 #endif
410 
411  // Lock before checking to see if this kind of atom can already
412  // be found in the atomspace. We need to lock here, to avoid two
413  // different threads from trying to add exactly the same atom.
414  std::unique_lock<std::recursive_mutex> lck(_mtx);
415 
416  // Check again, under the lock this time.
417  if (inEnviron(atom))
418  return atom->getHandle();
419 
420  // Factory implements experimental C++ atom types support code
421  Type atom_type = atom->getType();
422  atom = factory(atom_type, atom);
423 
424  // Is the equivalent of this atom already in the table?
425  // If so, then return the existing atom. (Note that this 'existing'
426  // atom might be in another atomspace, or might not be in any
427  // atomspace yet.)
428  Handle hexist(getHandle(atom));
429  if (hexist) return hexist;
430 
431  // If this atom is in some other atomspace, then we need to clone
432  // it. We cannot insert it into this atomtable as-is. (We already
433  // know that its not in this atomspace, or its environ.)
434  AtomTable* at = atom->getAtomTable();
435  if (at != NULL) {
436  LinkPtr lll(LinkCast(atom));
437  if (lll) {
438  // Well, if the link was in some other atomspace, then
439  // the outgoing set will probably be too. (It might not
440  // be if the other atomspace is a child of this one).
441  // So we recursively clone that too.
442  HandleSeq closet;
443  for (const Handle& h : lll->getOutgoingSet()) {
444  closet.push_back(add(h, async));
445  }
446  atom = createLink(atom_type, closet,
447  atom->getTruthValue(),
448  atom->getAttentionValue());
449  }
450  atom = clone_factory(atom_type, atom);
451  }
452 
453  // Sometimes one inserts an atom that was previously deleted.
454  // In this case, the removal flag might still be set. Clear it.
455  atom->unsetRemovalFlag();
456 
457  // Check for bad outgoing set members; fix them up if needed.
458  // "bad" here means outgoing set members that have UUID's but
459  // no pointers to actual atoms. We want to have the actual atoms,
460  // because later steps need the pointers to do stuff, in particular,
461  // to make sure the child atoms are in an atomtable, too.
462  LinkPtr lll(LinkCast(atom));
463  if (lll) {
464  const HandleSeq& ogs(lll->getOutgoingSet());
465  size_t arity = ogs.size();
466 
467  // First, make sure that every member of the outgoing set has
468  // a valid atom pointer. We need this, cause we need to call
469  // methods on those atoms.
470  bool need_copy = false;
471  for (size_t i = 0; i < arity; i++) {
472  Handle h(ogs[i]);
473  // It can happen that the uuid is assigned, but the pointer
474  // is NULL. In that case, we should at least know about this
475  // uuid. We explicitly test h._ptr.get() so as not to
476  // accidentally call resolve() during the test.
477  // XXX ??? How? How can this happen ??? How could we have a
478  // UUID but no pointer? Some persistance scenario ???
479  // Please explain ...
480  if (NULL == h._ptr.get()) {
481  if (Handle::UNDEFINED == h) {
482  prt_diag(atom, i, arity, ogs);
483  throw RuntimeException(TRACE_INFO,
484  "AtomTable - Attempting to insert link with "
485  "invalid outgoing members");
486  }
487  auto it = _atom_set.find(h);
488  if (it != _atom_set.end()) {
489  h = *it;
490 
491  // OK, here's the deal. We really need to fixup
492  // link so that it holds a valid atom pointer. We
493  // do that here. Unfortunately, this is not really
494  // thread-safe, and there is no particularly elegant
495  // way to lock. So we punt. This makes sense,
496  // because it is unlikely that one thread is going to
497  // be wingeing on the outgoing set, while another
498  // thread is performing an atom-table add. I'm pretty
499  // sure its a user error if the user fails to serialize
500  // atom table adds appropriately for their app.
501  lll->_outgoing[i]->remove_atom(lll);
502  lll->_outgoing[i] = h;
503  lll->_outgoing[i]->insert_atom(lll);
504  } else {
505  // XXX FIXME. This can trigger when external code
506  // removes atoms from the atomspace, but retains
507  // copies of the (now defunct, because deleted)
508  // UUID's. That is, when an atom is removed from
509  // the atomtable, it's UUID is no longer valid, and
510  // So that external code should not have saved the
511  // UUID's. However, if it did, and then created a
512  // handle out of them, then the handle would have
513  // a null atom pointer and a positive UUID, and we
514  // end up here. This is a user error. Note: the
515  // atomspace benchmark has been known to do this.
516  //
517  // Perhaps there are other weird secenarios, an we
518  // should search the environmnet first, before
519  // throwing... (we did not search environmnet,
520  // above ... this may need fixing...)
521  prt_diag(atom, i, arity, ogs);
522  throw RuntimeException(TRACE_INFO,
523  "AtomTable - Atom in outgoing set isn't known!");
524  }
525  }
526 
527  // h has to point to an actual atom, else below will crash.
528  // Anyway, the outgoing set must consist entirely of atoms
529  // either in this atomtable, or its environment.
530  if (not inEnviron(h)) need_copy = true;
531  }
532 
533  if (need_copy) {
534  atom = clone_factory(atom_type, atom);
535  }
536 
537  // llc not lll, in case a copy was made.
538  LinkPtr llc(LinkCast(atom));
539  for (size_t i = 0; i < arity; i++) {
540 
541  // Make sure all children have correct incoming sets
542  Handle ho(llc->_outgoing[i]);
543  if (not inEnviron(ho)) {
544  ho->remove_atom(llc);
545  llc->_outgoing[i] = add(ho, async);
546  }
547  else if (ho == Handle::UNDEFINED) {
548  // If we are here, then the atom is in the atomspace,
549  // but the handle has an invalid UUID. This can happen
550  // if the atom appears more than once in the outgoing
551  // set. Fix the handles' UUID, by forcing a cast.
552  llc->_outgoing[i] = ((AtomPtr) llc->_outgoing[i]);
553  }
554  // Build the incoming set of outgoing atom h.
555  llc->_outgoing[i]->insert_atom(llc);
556  }
557 
558  // OK, so if the above fixed up the outgoing set, and
559  // this is an unordered link, then we have to fix it up
560  // and put it back into the default sort order. That's
561  // because the default sort order uses UUID's, which have
562  // now changed.
563  if (classserver().isA(llc->getType(), UNORDERED_LINK)) {
564  llc->resort();
565  }
566  }
567 
568  // Its possible that the atom already has a UUID assigned,
569  // e.g. if it was fetched from persistent storage; this
570  // was done to preserve handle consistency. SavingLoading does
571  // this too. XXX Review SavingLoading for correctness...
572  if (atom->_uuid == Handle::UNDEFINED.value()) {
573  // Atom doesn't yet have a valid uuid assigned to it. Ask the TLB
574  // to issue a valid uuid. And then memorize it.
575  TLB::addAtom(atom);
576  } else {
577  TLB::reserve_upto(atom->_uuid);
578  }
579  Handle h(atom->getHandle());
580  size++;
581  _atom_set.insert(h);
582 
583  atom->keep_incoming_set();
584  atom->setAtomTable(this);
585 
586  if (not async)
587  put_atom_into_index(atom);
588 
589  // We can now unlock, since we are done. In particular, the signals
590  // need to run unlocked, since they may result in more atom table
591  // additions.
592  lck.unlock();
593 
594  // Update the indexes asynchronously
595  if (async)
596  _index_queue.enqueue(atom);
597 
598  // Now that we are completely done, emit the added signal.
599  _addAtomSignal(h);
600 
601  DPRINTF("Atom added: %ld => %s\n", atom->_uuid, atom->toString().c_str());
602  return h;
603 }
604 
606 {
607  std::unique_lock<std::recursive_mutex> lck(_mtx);
608  Atom* pat = atom.operator->();
609  nodeIndex.insertAtom(pat);
610  linkIndex.insertAtom(atom);
611  typeIndex.insertAtom(pat);
613 }
614 
616 {
617  _index_queue.flush_queue();
618 }
619 
620 size_t AtomTable::getSize() const
621 {
622  return size;
623 }
624 
626 {
627  std::lock_guard<std::recursive_mutex> lck(_mtx);
628  return nodeIndex.size();
629 }
630 
632 {
633  std::lock_guard<std::recursive_mutex> lck(_mtx);
634  return linkIndex.size();
635 }
636 
637 size_t AtomTable::getNumAtomsOfType(Type type, bool subclass) const
638 {
639  std::lock_guard<std::recursive_mutex> lck(_mtx);
640  size_t result = typeIndex.getNumAtomsOfType(type, subclass);
641 
642  if (_environ)
643  result += _environ->getNumAtomsOfType(type, subclass);
644 
645  return result;
646 }
647 
648 Handle AtomTable::getRandom(RandGen *rng) const
649 {
650  size_t x = rng->randint(getSize());
651 
652  Handle randy(Handle::UNDEFINED);
653 
654  // XXX TODO it would be considerably more efficient to go into the
655  // the type index, and decrement x by the size of the index for
656  // each type. This would speed up the algo by about 100 (by about
657  // the number of types that are in use...).
659  [&](Handle h)->void {
660  if (0 == x) randy = h;
661  x--;
662  },
663  ATOM, true);
664  return randy;
665 }
666 
667 AtomPtrSet AtomTable::extract(Handle& handle, bool recursive)
668 {
670 
671  // Make sure the atom is fully resolved before we go about
672  // deleting it.
673  handle = getHandle(handle);
674  AtomPtr atom(handle);
675  if (!atom || atom->isMarkedForRemoval()) return result;
676 
677  // Perhaps the atom is not in any table? Or at least, not in this
678  // atom table? Its a user-error if the user is trying to extract
679  // atoms that are not in this atomspace, but we're going to be
680  // silent about this error -- it seems pointless to throw.
681  if (atom->getAtomTable() != this) return result;
682 
683  // Lock before fetching the incoming set. Since getting the
684  // incoming set also grabs a lock, we need this mutex to be
685  // recursive. We need to lock here to avoid confusion if multiple
686  // threads are trying to delete the same atom.
687  std::unique_lock<std::recursive_mutex> lck(_mtx);
688 
689  if (atom->isMarkedForRemoval()) return result;
690  atom->markForRemoval();
691 
692  // If recursive-flag is set, also extract all the links in the atom's
693  // incoming set
694  if (recursive) {
695  // We need to make a copy of the incoming set because the
696  // recursive call will trash the incoming set when the atom
697  // is removed.
698  IncomingSet is(handle->getIncomingSet());
699 
700  IncomingSet::iterator is_it = is.begin();
701  IncomingSet::iterator is_end = is.end();
702  for (; is_it != is_end; ++is_it)
703  {
704  Handle his(*is_it);
705  DPRINTF("[AtomTable::extract] incoming set: %s",
706  (his) ? his->toString().c_str() : "INVALID HANDLE");
707 
708  // Something is seriously screwed up if the incoming set
709  // is not in this atomtable, and its not a child of this
710  // atom table. So flag that as an error; it will assert
711  // a few dozen lines later, below.
712  AtomTable* other = his->getAtomTable();
713  if (other and other != this and not other->inEnviron(handle)) {
714  logger().warn() << "AtomTable::extract() internal error, "
715  << "non-DAG membership.";
716  }
717  if (not his->isMarkedForRemoval()) {
718  DPRINTF("[AtomTable::extract] marked for removal is false");
719  if (other) {
720  AtomPtrSet ex = other->extract(his, true);
721  result.insert(ex.begin(), ex.end());
722  }
723  }
724  }
725  }
726 
727  // The check is done twice: the call to getIncomingSetSize() can
728  // return a non-zero value if the incoming set has weak pointers to
729  // deleted atoms. Thus, a second check is made for strong pointers,
730  // since getIncomingSet() converts weak to strong.
731  if (0 < handle->getIncomingSetSize())
732  {
733  IncomingSet iset(handle->getIncomingSet());
734  if (0 < iset.size())
735  {
736  if (not recursive)
737  {
738  // User asked for a non-recursive remove, and the
739  // atom is still referenced. So, do nothing.
740  handle->unsetRemovalFlag();
741  return result;
742  }
743 
744  // Check for an invalid condition that should not occur. See:
745  // https://github.com/opencog/opencog/commit/a08534afb4ef7f7e188e677cb322b72956afbd8f#commitcomment-5842682
746  size_t ilen = iset.size();
747  for (size_t i=0; i<ilen; i++)
748  {
749  // Its OK if the atom being extracted is in a link
750  // that is not currently in any atom space, or if that
751  // link is in a child subspace, in which case, we
752  // extract from the child.
753  //
754  // A bit of a race can happen: when the unlock is
755  // done below, to send the removed signal, another
756  // thread can sneak in and get to here, if it is
757  // deleting a different atom with an overlapping incoming
758  // set. Since the incoming set hasn't yet been updated
759  // (that happens after re-acquiring the lock),
760  // it will look like the incoming set has not yet been
761  // fully cleared. Well, it hasn't been, but as long as
762  // we are marked for removal, things should end up OK.
763  //
764  // XXX this might not be exactly thread-safe, if
765  // other atomspaces are involved...
766  if (iset[i]->getAtomTable() != NULL and
767  (not iset[i]->getAtomTable()->inEnviron(handle) or
768  not iset[i]->isMarkedForRemoval()))
769  {
770  Logger::Level lev = logger().getBackTraceLevel();
771  logger().setBackTraceLevel(Logger::ERROR);
772  logger().warn() << "AtomTable::extract() internal error";
773  logger().warn() << "Non-empty incoming set of size "
774  << ilen << " First trouble at " << i;
775  logger().warn() << "This atomtable=" << ((void*) this)
776  << " other atomtale=" << ((void*) iset[i]->getAtomTable())
777  << " inEnviron=" << iset[i]->getAtomTable()->inEnviron(handle);
778  logger().warn() << "This atom: " << handle->toString();
779  for (size_t j=0; j<ilen; j++) {
780  logger().warn() << "Atom j=" << j << " " << iset[j]->toString();
781  logger().warn() << "Marked: " << iset[j]->isMarkedForRemoval()
782  << " Table: " << ((void*) iset[j]->getAtomTable());
783  }
784  logger().setBackTraceLevel(lev);
785  atom->unsetRemovalFlag();
786  throw RuntimeException(TRACE_INFO,
787  "Internal Error: Cannot extract an atom with "
788  "a non-empty incoming set!");
789  }
790  }
791  }
792  }
793 
794  // Issue the atom removal signal *BEFORE* the atom is actually
795  // removed. This is needed so that certain subsystems, e.g. the
796  // Agent system activity table, can correctly manage the atom;
797  // it needs info that gets blanked out during removal.
798  lck.unlock();
799  _removeAtomSignal(atom);
800  lck.lock();
801 
802  // Decrements the size of the table
803  size--;
804  _atom_set.erase(handle);
805 
806  Atom* pat = atom.operator->();
807  nodeIndex.removeAtom(pat);
808  linkIndex.removeAtom(atom);
809  typeIndex.removeAtom(pat);
810  LinkPtr lll(LinkCast(atom));
811  if (lll) {
812  for (AtomPtr a : lll->_outgoing) {
813  a->remove_atom(lll);
814  }
815  }
817 
818  // XXX Setting the atom table causes AVChanged signals to be emitted.
819  // We should really do this unlocked, but I'm too lazy to fix, and
820  // am hoping no one will notice. This will probably need to be fixed
821  // someday.
822  atom->setAtomTable(NULL);
823 
824  result.insert(atom);
825  return result;
826 }
827 
828 // This is the resize callback, when a new type is dynamically added.
830 {
831  std::lock_guard<std::recursive_mutex> lck(_mtx);
832  //resize all Type-based indexes
833  nodeIndex.resize();
834  linkIndex.resize();
835  typeIndex.resize();
836 }
837 
boost::signals2::connection addedTypeConnection
Definition: AtomTable.h:121
NodeIndex nodeIndex
Definition: AtomTable.h:109
static TypeNodePtr TypeNodeCast(const Handle &h)
Definition: TypeNode.h:88
static PutLinkPtr PutLinkCast(const Handle &h)
Definition: PutLink.h:85
void resize(void)
Definition: TypeIndex.cc:33
#define createLink
Definition: Link.h:269
static DefineLinkPtr DefineLinkCast(const Handle &h)
Definition: DefineLink.h:101
#define createDefineLink
Definition: DefineLink.h:107
static AtomPtr clone_factory(Type atom_type, AtomPtr atom)
Definition: AtomTable.cc:328
void insertAtom(Atom *a)
Definition: TypeIndex.h:59
IncomingSet getIncomingSet()
Definition: Atom.cc:321
std::vector< Handle > HandleSeq
a list of handles
Definition: Handle.h:246
std::shared_ptr< Atom > AtomPtr
Definition: Handle.h:48
AtomTable * getAtomTable() const
Returns the AtomTable in which this Atom is inserted.
Definition: Atom.h:90
#define DPRINTF(...)
Definition: AtomTable.cc:58
size_t size(size_t i) const
void insertAtom(Atom *a)
Definition: NodeIndex.h:50
static std::string validate(const std::string &str)
Definition: NumberNode.h:68
std::unordered_set< Handle, handle_hash > _atom_set
Definition: AtomTable.h:104
void removeAtom(const AtomPtr &)
Definition: LinkIndex.cc:60
#define createPatternLink
Definition: PatternLink.h:188
static std::recursive_mutex _mtx
Definition: AtomTable.h:90
Atom * getAtom(Type type, const std::string &str) const
Definition: NodeIndex.h:63
Handle getHandle()
Definition: Atom.h:211
AtomTable(const AtomTable &)
Definition: AtomTable.cc:123
void removeAtom(Atom *a)
Definition: TypeIndex.h:63
void unsetRemovalFlag()
Unsets removal flag.
Definition: Atom.cc:227
void barrier(void)
Definition: AtomTable.cc:615
static BindLinkPtr BindLinkCast(const Handle &h)
Definition: BindLink.h:62
virtual std::string toString(std::string indent="")=0
Handle add(AtomPtr, bool async)
Definition: AtomTable.cc:394
static void prt_diag(AtomPtr atom, size_t i, size_t arity, const HandleSeq &ogs)
Definition: AtomTable.cc:376
LinkIndex linkIndex
Definition: AtomTable.h:110
#define createTypeNode
Definition: TypeNode.h:94
std::shared_ptr< Link > LinkPtr
Definition: Atom.h:53
TypeIndex typeIndex
Definition: AtomTable.h:108
static void reserve_upto(UUID hi)
Definition: TLB.h:123
AtomPtrSet extract(Handle &handle, bool recursive=true)
Definition: AtomTable.cc:667
ClassServer & classserver(ClassServerFactory *=ClassServer::createInstance)
Definition: ClassServer.cc:159
size_t getNumAtomsOfType(Type type, bool subclass) const
Definition: TypeIndex.cc:39
Handle getRandom(RandGen *rng) const
Definition: AtomTable.cc:648
static NodePtr NodeCast(const Handle &h)
Definition: Node.h:113
#define createPutLink
Definition: PutLink.h:91
async_caller< AtomTable, AtomPtr > _index_queue
Definition: AtomTable.h:113
bool isMarkedForRemoval() const
Definition: Atom.cc:208
void remove_atom(LinkPtr)
Remove an atom from the incoming set.
Definition: Atom.cc:300
static const Handle UNDEFINED
Definition: Handle.h:77
Handle getHandle(Type, std::string) const
Definition: AtomTable.cc:130
#define createLambdaLink
Definition: LambdaLink.h:91
size_t getNumNodes() const
Definition: AtomTable.cc:625
static void validate(const std::string &str)
Definition: TypeNode.h:76
unsigned long UUID
UUID == Universally Unique Identifier.
Definition: Handle.h:46
#define createVariableList
Definition: VariableList.h:101
TypeSignal & addTypeSignal()
Definition: ClassServer.cc:115
bool isCleared() const
Definition: AtomTable.cc:99
void insertAtom(const AtomPtr &)
Definition: LinkIndex.cc:49
void removeAtom(Atom *a)
Definition: NodeIndex.h:55
void foreachHandleByType(Function func, Type type, bool subclass=false, bool parent=true) const
Definition: AtomTable.h:219
AtomPtr _ptr
Definition: Handle.h:63
size_t getNumAtomsOfType(Type type, bool subclass=true) const
Definition: AtomTable.cc:637
static VariableListPtr VariableListCast(const Handle &h)
Definition: VariableList.h:95
#define createNode
Definition: Node.h:119
size_t getNumLinks() const
Definition: AtomTable.cc:631
static LinkPtr LinkCast(const Handle &h)
Definition: Link.h:263
size_t size() const
Definition: NodeIndex.cc:39
AtomSpace * _as
Definition: AtomTable.h:150
static PatternLinkPtr PatternLinkCast(const Handle &h)
Definition: PatternLink.h:182
static NumberNodePtr NumberNodeCast(const Handle &h)
Definition: NumberNode.h:77
AtomTable & operator=(const AtomTable &)
Definition: AtomTable.cc:117
std::vector< LinkPtr > IncomingSet
Definition: Atom.h:55
AtomSignal _addAtomSignal
Definition: AtomTable.h:127
std::shared_ptr< Node > NodePtr
Definition: Node.h:112
UUID value(void) const
Definition: Handle.h:85
void put_atom_into_index(AtomPtr &)
Definition: AtomTable.cc:605
static LambdaLinkPtr LambdaLinkCast(const Handle &h)
Definition: LambdaLink.h:85
static AtomPtr factory(Type atom_type, AtomPtr atom)
Definition: AtomTable.cc:269
static UUID reserve_extent(UUID extent)
Definition: TLB.h:115
void insert_atom(LinkPtr)
Add an atom to the incoming set.
Definition: Atom.cc:289
bool inEnviron(AtomPtr)
Definition: AtomTable.cc:256
AtomTable * _environ
Definition: AtomTable.h:145
void typeAdded(Type)
Definition: AtomTable.cc:829
static void set_resolver(const AtomTable *)
Definition: Handle.cc:52
std::set< AtomPtr > AtomPtrSet
Definition: AtomTable.h:60
ImportanceIndex importanceIndex
Definition: AtomTable.h:111
unsigned short Type
type of Atoms, represented as short integer (16 bits)
Definition: types.h:40
#define createNumberNode
Definition: NumberNode.h:83
size_t getIncomingSetSize()
Get the size of the incoming set.
Definition: Atom.cc:310
size_t getSize() const
Definition: AtomTable.cc:620
static void clear_resolver(const AtomTable *)
Definition: Handle.cc:57
size_t size() const
Definition: LinkIndex.cc:42
Handle getHandle(Type type, const HandleSeq &) const
Definition: LinkIndex.cc:71
#define createBindLink
Definition: BindLink.h:68
AtomPtrSignal _removeAtomSignal
Definition: AtomTable.h:128
static int compare(const Handle &h1, const Handle &h2)
Definition: Handle.h:168
static void addAtom(AtomPtr atom)
Definition: TLB.h:142