<commit_before>/*
 * SchemeSmobAS.c
 *
 * Scheme small objects (SMOBS) for atom spaces.
 *
 * Copyright (c) 2008,2009,2014 <NAME> <<EMAIL>>
 */

#include <cstddef>
#include <libguile.h>

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/guile/SchemeSmob.h>
#include <opencog/util/oc_assert.h>

using namespace opencog;

// Need a lock to protect the map, since multiple threads may be trying
// to update this map.  The map contains a use-count for the number of
// threads that are currently using this atomspace as the current
// atomspace.  When the count drops to zero, the atomspace will be
// reaped if the guile GC finds that the number of SCM references
// has also dropped to zero.

std::mutex SchemeSmob::as_mtx;
std::map<AtomSpace*, int> SchemeSmob::deleteable_as;

/* ============================================================== */

std::string SchemeSmob::as_to_string(const AtomSpace *as)
{
#define BUFLEN 120
	char buff[BUFLEN];

	snprintf(buff, BUFLEN, "#<atomspace %p>", as);
	return buff;
}

/* ============================================================== */
/**
 * Create SCM object wrapping the atomspace.
 * Guile will try to memory-manage it; the deleteable_as will be
 * used to track foreign vs. internal references.
 */
SCM SchemeSmob::make_as(AtomSpace *as)
{
	SCM smob;
	SCM_NEWSMOB (smob, cog_misc_tag, as);
	SCM_SET_SMOB_FLAGS(smob, COG_AS);
	return smob;
}

/* ============================================================== */

/**
 * The only place this is called from is the guile garbage collector!
 * Its called when guile thinks it has no pointers to the given
 * atomspace; thus, we should free it. In fact, we only decrement the
 * use count for it; XXX but under what circumstance would that not be
 * zero? If its racing with another thread ?? I'm confused.
 *
 * Note that if the atomspace was given to us externally, e.g. by the
 * cogserver, then it will not even have a use-count (and thus, it's
 * never deleted here.)
 */
void SchemeSmob::release_as (AtomSpace *as)
{
	std::unique_lock<std::mutex> lck(as_mtx);
	auto has = deleteable_as.find(as);
	if (deleteable_as.end() == has) return;

	if (0 == deleteable_as[as])
	{
		AtomSpace* env = as->get_environ();
		deleteable_as.erase(has);
		lck.unlock();
		delete as;

		// (Recursively) decrement the use count on the parent.
		// We had incremented it earlier, in `ss_new_as`, when
		// creating it.
		if (env and deleteable_as.end() != deleteable_as.find(env))
		{
			--deleteable_as[env];
			if (0 == deleteable_as[env]) release_as(env);
		}
	}
}

/* ============================================================== */
/**
 * Create a new atom space.  The parent argument might not
 * be present -- its optional.
 */
SCM SchemeSmob::ss_new_as (SCM s)
{
	AtomSpace *parent = ss_to_atomspace(s);

	AtomSpace *as = new AtomSpace(parent);

	scm_gc_register_allocation(sizeof(*as));

	// Only the internally-created atomspaces are trackable.
	std::lock_guard<std::mutex> lck(as_mtx);<commit_msg>Bug-fix: do not just delete because count is zero!<commit_after>/*
 * SchemeSmobAS.c
 *
 * Scheme small objects (SMOBS) for atom spaces.
 *
 * Copyright (c) 2008,2009,2014 <NAME> <<EMAIL>>
 */

#include <cstddef>
#include <libguile.h>

#include <opencog/atomspace/AtomSpace.h>
#include <opencog/guile/SchemeSmob.h>
#include <opencog/util/oc_assert.h>

using namespace opencog;

// Need a lock to protect the map, since multiple threads may be trying
// to update this map.  The map contains a use-count for the number of
// threads that are currently using this atomspace as the current
// atomspace.  When the count drops to zero, the atomspace will be
// reaped if the guile GC finds that the number of SCM references
// has also dropped to zero.

std::mutex SchemeSmob::as_mtx;
std::map<AtomSpace*, int> SchemeSmob::deleteable_as;

/* ============================================================== */

std::string SchemeSmob::as_to_string(const AtomSpace *as)
{
#define BUFLEN 120
	char buff[BUFLEN];

	snprintf(buff, BUFLEN, "#<atomspace %p>", as);
	return buff;
}

/* ============================================================== */
/**
 * Create SCM object wrapping the atomspace.
 * Guile will try to memory-manage it; the deleteable_as will be
 * used to track foreign vs. internal references.
 */
SCM SchemeSmob::make_as(AtomSpace *as)
{
	SCM smob;
	SCM_NEWSMOB (smob, cog_misc_tag, as);
	SCM_SET_SMOB_FLAGS(smob, COG_AS);
	return smob;
}

/* ============================================================== */

/**
 * The only place this is called from is the guile garbage collector!
 * Its called when guile thinks it has no pointers to the given
 * atomspace; thus, we should free it. In fact, we only decrement the
 * use count for it; XXX but under what circumstance would that not be
 * zero? If its racing with another thread ?? I'm confused.
 *
 * Note that if the atomspace was given to us externally, e.g. by the
 * cogserver, then it will not even have a use-count (and thus, it's
 * never deleted here.)
 */
void SchemeSmob::release_as (AtomSpace *as)
{
	std::unique_lock<std::mutex> lck(as_mtx);
	auto has = deleteable_as.find(as);
	if (deleteable_as.end() == has) return;

	if (0 == deleteable_as[as])
	{
		AtomSpace* env = as->get_environ();
		deleteable_as.erase(has);
		lck.unlock();
		delete as;

		// (Recursively) decrement the use count on the parent.
		// We had incremented it earlier, in `ss_new_as`, when
		// creating it. Do not delete it, unless the guile gc
		// asks us to.
		if (env and deleteable_as.end() != deleteable_as.find(env))
		{
			if (0 < deleteable_as[env])
				--deleteable_as[env];
		}
	}
}

/* ============================================================== */
/**
 * Create a new atom space.  The parent argument might not
 * be present -- its optional.
 */
SCM SchemeSmob::ss_new_as (SCM s)
{
	AtomSpace *parent = ss_to_atomspace(s);

	AtomSpace *as = new AtomSpace(parent);

	scm_gc_register_allocation(sizeof(*as));

	// Only the internally-created atomspaces are trackable.
	std::lock_guard<std::mutex> lck(as_mtx);
{'fraction_non_alphanumeric': 0.19090759347718664, 'fraction_numerical': 0.005600395322022731, 'mean_word_length': 4.359223300970874, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '944335', 'n_tokens_mistral': 2088, 'n_tokens_neox': 1960, 'n_words': 655}