OverviewFilesCommitsIssuesDocsReleasesPeers

opencog-atomspace / opencog / atomspace / TypeIndex.cc

236 lines · cc ref:
/*
 * opencog/atomspace/TypeIndex.cc
 *
 * Copyright (C) 2008,2025 Linas Vepstas <linasvepstas@gmail.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License v3 as
 * published by the Free Software Foundation and including the exceptions
 * at http://opencog.org/wiki/Licenses
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program; if not, write to:
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */

#include "TypeIndex.h"
#include <opencog/atoms/atom_types/NameServer.h>

using namespace opencog;

TypeIndex::TypeIndex(void) :
	_reserved(TYPE_RESERVE_SIZE),
	_nameserver(nameserver()),
	_idx(VEC_SIZE)
{
	_num_types = nameserver().getNumberOfClasses();
	_offset_to_atom = ATOM;
	resize();
}

#define GET_BFL(vec) \
	for (int ibu = 0; ibu < (int) vec.size(); ibu++) { \
		AtomSet& s(vec[ibu]); \
		s._mtx.lock(); }

#define DROP_BFL(vec) \
	for (int ibu = 0; ibu < (int) vec.size(); ibu++) { \
		AtomSet& s(vec[ibu]); \
		s._mtx.unlock(); }

void TypeIndex::resize(void) const
{
	int newsz = nameserver().getNumberOfClasses();
	if (newsz < _reserved + _offset_to_atom) return;

	// If we are here, we need to resize. Get the BFL.

	while (_reserved + _offset_to_atom < newsz)
		_reserved *= 2;

	std::vector<AtomSet> newvec(_reserved * POOL_SIZE);
	GET_BFL(_idx)
	newvec.swap(_idx);
	_num_types = newsz;
	DROP_BFL(newvec)
}

void TypeIndex::clear(void)
{
	std::vector<AtomSet> dead(_reserved * POOL_SIZE);
	GET_BFL(_idx)
	dead.swap(_idx);

	// Clear the AtomSpace before releasing the lock.
	for (auto& s : dead)
	{
		for (auto& h : s)
			h->_atom_space = nullptr;
	}

	DROP_BFL(dead)

	// Do the final cleanup after releasing the lock. This enables
	// the very unlikely situation of having other threads start
	// using the AtomSpace again, while we do final cleanup. BTW,
	// the matching `install()` for the `remove()` below happened
	// in the `AtomSpace::add()` method. We do it here cause its
	// easier. Anyway, we can't do the `remove()` under the lock,
	// that would result in lock inversion.
	for (auto& s : dead)
	{
		for (auto& h : s)
			h->remove();
		s.clear();
	}
}

// ================================================================

void TypeIndex::get_handles_by_type(HandleSeq& hseq,
                                    Type type,
                                    bool subclass) const
{
	if (not subclass and type < _offset_to_atom) return;

	// Get the initial size of the handles vector.
	size_t initial_size = hseq.size();

	// Determine the number of atoms we'll be adding.
	size_t size_of_append = size(type, subclass);

	// Now reserve size for the addition. This is faster for large
	// append iterations since appends to the list won't require new
	// allocations and copies whenever the allocated size is exceeded.
	hseq.reserve(initial_size + size_of_append);

	if (type >= _offset_to_atom)
	{
		int start = get_bucket_start(type);
		for (int ibu = start; ibu < start + POOL_SIZE; ibu++)
		{
			const AtomSet& s(_idx[ibu]);
			TYPE_INDEX_SHARED_LOCK(s);
			for (const Handle& h : s)
				hseq.push_back(h);
		}
	}

	// Not subclassing? We are done!
	if (not subclass) return;

	Type tstar = std::max(type+1, _offset_to_atom);
	for (Type t = tstar; t<_num_types; t++)
	{
		if (not _nameserver.isA(t, type)) continue;

		int start = get_bucket_start(t);
		for (int ibu = start; ibu < start + POOL_SIZE; ibu++)
		{
			const AtomSet& s(_idx[ibu]);
			TYPE_INDEX_SHARED_LOCK(s);
			for (const Handle& h : s)
				hseq.push_back(h);
		}
	}
}

// Same as above, except using an unordered set.
void TypeIndex::get_handles_by_type(UnorderedHandleSet& hset,
                                    Type type,
                                    bool subclass) const
{
	if (not subclass and type < _offset_to_atom) return;

	if (type >= _offset_to_atom)
	{
		int start = get_bucket_start(type);
		for (int ibu = start; ibu < start + POOL_SIZE; ibu++)
		{
			const AtomSet& s(_idx[ibu]);
			TYPE_INDEX_SHARED_LOCK(s);
			hset.insert(s.begin(), s.end());
		}
	}

	// Not subclassing? We are done!
	if (not subclass) return;

	Type tstar = std::max(type+1, _offset_to_atom);
	for (Type t = tstar; t<_num_types; t++)
	{
		if (not _nameserver.isA(t, type)) continue;

		int start = get_bucket_start(t);
		for (int ibu = start; ibu < start + POOL_SIZE; ibu++)
		{
			const AtomSet& s(_idx[ibu]);
			TYPE_INDEX_SHARED_LOCK(s);
			hset.insert(s.begin(), s.end());
		}
	}
}

// ================================================================

void TypeIndex::get_rootset_by_type(HandleSeq& hseq,
                                    Type type,
                                    bool subclass,
                                    const AtomSpace* cas) const
{
	if (not subclass and type < _offset_to_atom) return;

	// Get the initial size of the handles vector.
	size_t initial_size = hseq.size();

	// Determine the number of atoms we'll be adding.
	size_t size_of_append = size(type, subclass);

	// Now reserve size for the addition. This is faster for large
	// append iterations since appends to the list won't require new
	// allocations and copies whenever the allocated size is exceeded.
	hseq.reserve(initial_size + size_of_append);

	if (type >= _offset_to_atom)
	{
		int start = get_bucket_start(type);
		for (int ibu = start; ibu < start + POOL_SIZE; ibu++)
		{
			const AtomSet& s(_idx[ibu]);
			TYPE_INDEX_SHARED_LOCK(s);
			for (const Handle& h : s)
			{
				if (h->isIncomingSetEmpty(cas))
					hseq.push_back(h);
			}
		}
	}

	// Not subclassing? We are done!
	if (not subclass) return;

	Type tstar = std::max(type+1, _offset_to_atom);
	for (Type t = tstar; t<_num_types; t++)
	{
		if (not _nameserver.isA(t, type)) continue;

		int start = get_bucket_start(t);
		for (int ibu = start; ibu < start + POOL_SIZE; ibu++)
		{
			const AtomSet& s(_idx[ibu]);
			TYPE_INDEX_SHARED_LOCK(s);
			for (const Handle& h : s)
				if (h->isIncomingSetEmpty(cas))
					hseq.push_back(h);
		}
	}
}

// ================================================================