OpenCog Framework  Branch: master, revision 6f0b7fc776b08468cf1b74aa9db028f387b4f0c0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
Atom.cc
Go to the documentation of this file.
1 /*
2  * opencog/atomspace/Atom.cc
3  *
4  * Copyright (C) 2002-2007 Novamente LLC
5  * All Rights Reserved
6  *
7  * Written by Thiago Maia <thiago@vettatech.com>
8  * Andre Senna <senna@vettalabs.com>
9  * Welter Silva <welter@vettalabs.com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU Affero General Public License v3 as
13  * published by the Free Software Foundation and including the exceptions
14  * at http://opencog.org/wiki/Licenses
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU Affero General Public License
22  * along with this program; if not, write to:
23  * Free Software Foundation, Inc.,
24  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25  */
26 
27 #include <set>
28 
29 #ifndef WIN32
30 #include <unistd.h>
31 #endif
32 
33 #include <opencog/util/Logger.h>
34 #include <opencog/util/exceptions.h>
35 #include <opencog/util/misc.h>
36 #include <opencog/util/platform.h>
37 
38 #include <opencog/atomspace/Atom.h>
41 #include <opencog/atomspace/Link.h>
44 
46 // #define WRITE_MUTEX 1 //BIT0
47 #define MARKED_FOR_REMOVAL 2 //BIT1
48 // #define MULTIPLE_TRUTH_VALUES 4 //BIT2
49 // #define FIRED_ACTIVATION 8 //BIT3
50 // #define HYPOTETHICAL_FLAG 16 //BIT4
51 // #define REMOVED_BY_DECAY 32 //BIT5
52 
53 //#define DPRINTF printf
54 #define DPRINTF(...)
55 
56 #undef Type
57 
58 using namespace opencog;
59 
60 #define _avmtx _mtx
61 
63 {
64  _atomTable = NULL;
65  if (0 < getIncomingSetSize()) {
66  // This can't ever possibly happen. If it does, then there is
67  // some very sick bug with the reference counting that the
68  // shared pointers are doing. (Or someone explcitly called the
69  // destructor! Which they shouldn't do.)
70  OC_ASSERT(0 == getIncomingSet().size(),
71  "Atom deletion failure; incoming set not empty for %s h=%d",
72  classserver().getTypeName(_type).c_str(), _uuid);
73  }
75 }
76 
77 // ==============================================================
78 // Whole lotta truthiness going on here. Does it really need to be
79 // this complicated!?
80 
82 {
83  if (newTV->isNullTv()) return;
84 
85  // We need to guarantee that the signal goes out with the
86  // correct truth value. That is, another setter could be changing
87  // this, even as we are. So make a copy, first.
89 
90  // ... and we still need to make sure that only one thread is
91  // writing this at a time. std:shared_ptr is NOT thread-safe against
92  // multiple writers: see "Example 5" in
93  // http://www.boost.org/doc/libs/1_53_0/libs/smart_ptr/shared_ptr.htm#ThreadSafety
94  std::unique_lock<std::mutex> lck (_mtx);
95  _truthValue = newTV;
96  lck.unlock();
97 
98  if (_atomTable != NULL) {
100  tvch(getHandle(), oldTV, newTV);
101  }
102 }
103 
105 {
106  // OK. The atomic thread-safety of shared-pointers is subtle. See
107  // http://www.boost.org/doc/libs/1_53_0/libs/smart_ptr/shared_ptr.htm#ThreadSafety
108  // and http://cppwisdom.quora.com/shared_ptr-is-almost-thread-safe
109  // What it boils down to here is that we must *always* make a copy
110  // of _truthValue before we use it, since it can go out of scope
111  // because it can get set in another thread. Viz, using it to
112  // dereference can return a raw pointer to an object that has been
113  // deconstructed. The AtomSpaceAsyncUTest will hit this, as will
114  // the multi-threaded async atom store in the SQL peristance backend.
115  // Furthermore, we must make a copy while holding the lock! Got that?
116 
117  std::lock_guard<std::mutex> lck(_mtx);
118  TruthValuePtr local(_truthValue);
119  return local;
120 }
121 
123 {
124  if (NULL == tvn or tvn->isDefaultTV() or tvn->isNullTv()) return;
125 
126  // No locking to be done here. It is possible that between the time
127  // that we read the TV here (i.e. set currentTV) and the time that
128  // we look to see if currentTV is default, that some other thread
129  // will have changed _truthValue. This is a race, but we don't care,
130  // because if two threads are trying to simultaneously set the TV on
131  // one atom, without co-operating with one-another, they get what
132  // they deserve -- a race. (We still use getTruthValue() to avoid a
133  // read-write race on the shared_pointer itself!)
134  TruthValuePtr currentTV(getTruthValue());
135  if (currentTV->isDefaultTV() or currentTV->isNullTv()) {
136  setTruthValue(tvn);
137  return;
138  }
139 
140  TruthValuePtr mergedTV(currentTV->merge(tvn));
141  setTruthValue(mergedTV);
142 }
143 
144 // ==============================================================
145 
147 {
148  // OK. The atomic thread-safety of shared-pointers is subtle. See
149  // http://www.boost.org/doc/libs/1_53_0/libs/smart_ptr/shared_ptr.htm#ThreadSafety
150  // and http://cppwisdom.quora.com/shared_ptr-is-almost-thread-safe
151  // What it boils down to here is that we must *always* make a copy
152  // of _attentionValue before we use it, since it can go out of scope
153  // because it can get set in another thread. Viz, using it to
154  // dereference can return a raw pointer to an object that has been
155  // deconstructed. Furthermore, we must make a copy while holding
156  // the lock! Got that?
157 
158  std::lock_guard<std::mutex> lck(_mtx);
160  return local;
161 }
162 
164 {
165  // Must obtain a local copy of the AV, since there may be
166  // parallel writers in other threads.
168  if (av == local) return;
169  if (*av == *local) return;
170 
171  // Need to lock, shared_ptr is NOT atomic!
172  std::unique_lock<std::mutex> lck (_avmtx);
173  local = _attentionValue; // Get it again, to avoid races.
175  lck.unlock();
176 
177  // If the atom free-floating, we are done.
178  if (NULL == _atomTable) return;
179 
180  // Get old and new bins.
181  int oldBin = ImportanceIndex::importanceBin(local->getSTI());
182  int newBin = ImportanceIndex::importanceBin(av->getSTI());
183 
184  // If the atom importance has changed its bin,
185  // update the importance index.
186  if (oldBin != newBin) {
187  AtomPtr a(shared_from_this());
188  _atomTable->updateImportanceIndex(a, oldBin);
189  }
190 
191  // Notify any interested parties that the AV changed.
193  avch(getHandle(), local, av);
194 }
195 
196 void Atom::chgVLTI(int unit)
197 {
199  AttentionValuePtr new_av = createAV(
200  old_av->getSTI(),
201  old_av->getLTI(),
202  old_av->getVLTI() + unit);
203  setAttentionValue(new_av);
204 }
205 
206 // ==============================================================
207 // Flag stuff
209 {
210  return (_flags & MARKED_FOR_REMOVAL) != 0;
211 }
212 
213 bool Atom::getFlag(int flag) const
214 {
215  return (_flags & flag) != 0;
216 }
217 
218 void Atom::setFlag(int flag, bool value)
219 {
220  if (value) {
221  _flags |= flag;
222  } else {
223  _flags &= ~(flag);
224  }
225 }
226 
228 {
230 }
231 
233 {
235 }
236 
237 // ==============================================================
238 
240 {
241  if (tb == _atomTable) return;
242 
243  // Either the existing _atomTable is null, and tb is not, i.e. this
244  // atom is being inserted into tb, or _atomTable is not null, while
245  // tb is null, i.e. atom is being removed from _atomTable. It is
246  // illegal to just switch membership: one or the other of these two
247  // pointers must be null.
248  OC_ASSERT (NULL == _atomTable or tb == NULL, "Atom table is not null!");
249  if (NULL != _atomTable) {
250  // Atom is being removed from the atom table.
251  // UUID's belong to the atom table, not the atom. Reclaim it.
253  }
254  _atomTable = tb;
255 }
256 
257 // ==============================================================
258 // Incoming set stuff
259 
271 {
272  if (_incoming_set) return;
273  _incoming_set = std::make_shared<InSet>();
274 }
275 
280 {
281  if (NULL == _incoming_set) return;
282  std::lock_guard<std::mutex> lck (_mtx);
283  _incoming_set->_iset.clear();
284  // delete _incoming_set;
285  _incoming_set = NULL;
286 }
287 
290 {
291  if (NULL == _incoming_set) return;
292  std::lock_guard<std::mutex> lck (_mtx);
293  _incoming_set->_iset.insert(a);
294 #ifdef INCOMING_SET_SIGNALS
295  _incoming_set->_addAtomSignal(shared_from_this(), a);
296 #endif /* INCOMING_SET_SIGNALS */
297 }
298 
301 {
302  if (NULL == _incoming_set) return;
303  std::lock_guard<std::mutex> lck (_mtx);
304 #ifdef INCOMING_SET_SIGNALS
305  _incoming_set->_removeAtomSignal(shared_from_this(), a);
306 #endif /* INCOMING_SET_SIGNALS */
307  _incoming_set->_iset.erase(a);
308 }
309 
311 {
312  if (NULL == _incoming_set) return 0;
313  std::lock_guard<std::mutex> lck (_mtx);
314  return _incoming_set->_iset.size();
315 }
316 
317 // We return a copy here, and not a reference, because the set itself
318 // is not thread-safe during reading while simultaneous insertion and
319 // deletion. Besides, the incoming set is weak; we have to make it
320 // strong in order to hand it out.
322 {
323  static IncomingSet empty_set;
324  if (NULL == _incoming_set) return empty_set;
325 
326  // Prevent update of set while a copy is being made.
327  std::lock_guard<std::mutex> lck (_mtx);
328  IncomingSet iset;
329  for (WinkPtr w : _incoming_set->_iset)
330  {
331  LinkPtr l(w.lock());
332  if (l) iset.push_back(l);
333  }
334  return iset;
335 }
336 
338  HandleSeq inhs;
339  getIncomingSetByType(std::back_inserter(inhs), type, subclass);
340  IncomingSet inlinks;
341  for (const Handle& h : inhs)
342  inlinks.push_back(LinkCast(h));
343  return inlinks;
344 }
AttentionValuePtr getAttentionValue()
Definition: Atom.cc:146
TVCHSigl & TVChangedSignal()
Definition: AtomTable.h:350
UUID _uuid
Definition: Atom.h:93
void setFlag(int, bool)
Definition: Atom.cc:218
#define _avmtx
Definition: Atom.cc:60
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
boost::signals2::signal< void(const Handle &, const TruthValuePtr &, const TruthValuePtr &)> TVCHSigl
Definition: AtomTable.h:69
void chgVLTI(int unit)
Definition: Atom.cc:196
std::shared_ptr< TruthValue > TruthValuePtr
Definition: TruthValue.h:85
void drop_incoming_set()
Definition: Atom.cc:279
std::shared_ptr< AttentionValue > AttentionValuePtr
void markForRemoval()
Marks the atom for removal.
Definition: Atom.cc:232
Handle getHandle()
Definition: Atom.h:211
void unsetRemovalFlag()
Unsets removal flag.
Definition: Atom.cc:227
char _flags
Definition: Atom.h:99
std::shared_ptr< Link > LinkPtr
Definition: Atom.h:53
void setTruthValue(TruthValuePtr)
Sets the TruthValue object of the atom.
Definition: Atom.cc:81
ClassServer & classserver(ClassServerFactory *=ClassServer::createInstance)
Definition: ClassServer.cc:159
static unsigned int importanceBin(short)
bool isMarkedForRemoval() const
Definition: Atom.cc:208
void setAttentionValue(AttentionValuePtr)
Sets the AttentionValue object of the atom.
Definition: Atom.cc:163
void remove_atom(LinkPtr)
Remove an atom from the incoming set.
Definition: Atom.cc:300
AVCHSigl & AVChangedSignal()
Definition: AtomTable.h:347
void updateImportanceIndex(AtomPtr a, int bin)
Definition: AtomTable.h:255
std::weak_ptr< Link > WinkPtr
Definition: Atom.h:56
static const Handle UNDEFINED
Definition: Handle.h:77
std::mutex _mtx
Definition: Atom.h:109
void merge(TruthValuePtr)
Definition: Atom.cc:122
boost::signals2::signal< void(const Handle &, const AttentionValuePtr &, const AttentionValuePtr &)> AVCHSigl
Definition: AtomTable.h:66
static LinkPtr LinkCast(const Handle &h)
Definition: Link.h:263
bool getFlag(int) const
Definition: Atom.cc:213
std::vector< LinkPtr > IncomingSet
Definition: Atom.h:55
TruthValuePtr _truthValue
Definition: Atom.h:101
virtual ~Atom()
Definition: Atom.cc:62
InSetPtr _incoming_set
Definition: Atom.h:150
UUID value(void) const
Definition: Handle.h:85
TruthValuePtr getTruthValue()
Definition: Atom.cc:104
void insert_atom(LinkPtr)
Add an atom to the incoming set.
Definition: Atom.cc:289
Type _type
Definition: Atom.h:96
void setAtomTable(AtomTable *)
Sets the AtomTable in which this Atom is inserted.
Definition: Atom.cc:239
unsigned short Type
type of Atoms, represented as short integer (16 bits)
Definition: types.h:40
size_t getIncomingSetSize()
Get the size of the incoming set.
Definition: Atom.cc:310
void keep_incoming_set()
Definition: Atom.cc:270
#define createAV
OutputIterator getIncomingSetByType(OutputIterator result, Type type, bool subclass=false)
Definition: Atom.h:353
AttentionValuePtr _attentionValue
Definition: Atom.h:102
#define MARKED_FOR_REMOVAL
Atom flag.
Definition: Atom.cc:47
AtomTable * _atomTable
Definition: Atom.h:94