mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-09 04:59:31 -06:00
reference counting for concrete syntaxes
This commit is contained in:
@@ -106,7 +106,15 @@ typedef struct {
|
||||
|
||||
struct PGF_INTERNAL_DECL PgfConcr {
|
||||
size_t ref_count;
|
||||
Namespace<PgfFlag> cflags;
|
||||
size_t ref_count_ex;
|
||||
Namespace<PgfFlag> cflags;
|
||||
|
||||
// If there are references from the host language to this concrete,
|
||||
// then it is included in a double-linked list. If a process
|
||||
// dies without releasing the reference, it will be released by
|
||||
// the first process who have an exclusive access to the database.
|
||||
ref<PgfConcr> prev, next;
|
||||
|
||||
PgfText name;
|
||||
|
||||
static void release(ref<PgfConcr> pgf);
|
||||
|
||||
@@ -280,16 +280,18 @@ struct PGF_INTERNAL_DECL malloc_state
|
||||
/* The namespace of all persistant grammar revisions */
|
||||
Namespace<PgfPGF> revisions;
|
||||
|
||||
/* A reference to the first transient revision in
|
||||
/* A reference to the first transient revisions in
|
||||
* a double-linked list.
|
||||
*/
|
||||
ref<PgfPGF> transient_revisions;
|
||||
ref<PgfPGF> transient_revisions;
|
||||
ref<PgfConcr> transient_concr_revisions;
|
||||
};
|
||||
|
||||
PGF_INTERNAL
|
||||
PgfDB::PgfDB(const char* filepath, int flags, int mode) {
|
||||
size_t file_size;
|
||||
bool is_new = false;
|
||||
bool is_first = false;
|
||||
|
||||
fd = -1;
|
||||
ms = NULL;
|
||||
@@ -346,7 +348,7 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
|
||||
}
|
||||
|
||||
try {
|
||||
rwlock = ipc_new_file_rwlock(this->filepath);
|
||||
rwlock = ipc_new_file_rwlock(this->filepath, &is_first);
|
||||
} catch (pgf_systemerror e) {
|
||||
::free((void *) this->filepath);
|
||||
close(fd);
|
||||
@@ -362,14 +364,32 @@ PgfDB::PgfDB(const char* filepath, int flags, int mode) {
|
||||
throw pgf_error("Invalid file content");
|
||||
}
|
||||
|
||||
// We must make sure that left-over transient revisions are
|
||||
// released. This may happen if a client process was killed
|
||||
// or if the garbadge collector has not managed to run
|
||||
// pgf_release_revision() before the process ended.
|
||||
if (is_first) {
|
||||
// We must make sure that left-over transient revisions are
|
||||
// released. This may happen if a client process was killed
|
||||
// or if the garbadge collector has not managed to run
|
||||
// pgf_release_revision() before the process ended.
|
||||
|
||||
while (ms->transient_revisions != 0) {
|
||||
pgf_free_revision(this, ms->transient_revisions.as_object());
|
||||
}
|
||||
while (ms->transient_revisions != 0) {
|
||||
pgf_free_revision(this, ms->transient_revisions.as_object());
|
||||
ref<PgfPGF> pgf = ms->transient_revisions;
|
||||
ref<PgfPGF> next = pgf->next;
|
||||
PgfPGF::release(pgf);
|
||||
PgfDB::free(pgf);
|
||||
ms->transient_revisions = next;
|
||||
}
|
||||
|
||||
while (ms->transient_concr_revisions != 0) {
|
||||
ref<PgfConcr> concr = ms->transient_concr_revisions;
|
||||
ref<PgfConcr> next = concr->next;
|
||||
concr->ref_count -= concr->ref_count_ex;
|
||||
if (!concr->ref_count) {
|
||||
PgfConcr::release(concr);
|
||||
PgfDB::free(concr);
|
||||
}
|
||||
ms->transient_concr_revisions = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,6 +460,7 @@ void PgfDB::init_state(size_t size)
|
||||
|
||||
ms->revisions = 0;
|
||||
ms->transient_revisions = 0;
|
||||
ms->transient_concr_revisions = 0;
|
||||
}
|
||||
|
||||
/* Take a chunk off a bin list. */
|
||||
@@ -1044,23 +1065,6 @@ ref<PgfPGF> PgfDB::revision2pgf(PgfRevision revision)
|
||||
return pgf;
|
||||
}
|
||||
|
||||
PGF_INTERNAL
|
||||
ref<PgfConcr> PgfDB::revision2concr(PgfConcrRevision revision)
|
||||
{
|
||||
if (revision <= sizeof(*current_db->ms) || revision >= current_db->ms->top)
|
||||
throw pgf_error("Invalid revision");
|
||||
|
||||
mchunk *chunk = mem2chunk(ptr(current_db->ms,revision));
|
||||
if (chunksize(chunk) < sizeof(PgfConcr))
|
||||
throw pgf_error("Invalid revision");
|
||||
|
||||
ref<PgfConcr> concr = revision;
|
||||
if (chunksize(chunk) != request2size(sizeof(PgfConcr)+concr->name.size+1))
|
||||
throw pgf_error("Invalid revision");
|
||||
|
||||
return concr;
|
||||
}
|
||||
|
||||
PGF_INTERNAL
|
||||
bool PgfDB::is_persistant_revision(ref<PgfPGF> pgf)
|
||||
{
|
||||
@@ -1088,6 +1092,50 @@ void PgfDB::unlink_transient_revision(ref<PgfPGF> pgf)
|
||||
current_db->ms->transient_revisions = pgf->next;
|
||||
}
|
||||
|
||||
PGF_INTERNAL
|
||||
ref<PgfConcr> PgfDB::revision2concr(PgfConcrRevision revision)
|
||||
{
|
||||
if (revision <= sizeof(*current_db->ms) || revision >= current_db->ms->top)
|
||||
throw pgf_error("Invalid revision");
|
||||
|
||||
mchunk *chunk = mem2chunk(ptr(current_db->ms,revision));
|
||||
if (chunksize(chunk) < sizeof(PgfConcr))
|
||||
throw pgf_error("Invalid revision");
|
||||
|
||||
ref<PgfConcr> concr = revision;
|
||||
if (chunksize(chunk) != request2size(sizeof(PgfConcr)+concr->name.size+1))
|
||||
throw pgf_error("Invalid revision");
|
||||
|
||||
return concr;
|
||||
}
|
||||
|
||||
PGF_INTERNAL
|
||||
bool PgfDB::is_persistant_revision(ref<PgfConcr> concr)
|
||||
{
|
||||
return (concr->prev == 0 && concr->next == 0 &&
|
||||
current_db->ms->transient_concr_revisions != concr);
|
||||
}
|
||||
|
||||
PGF_INTERNAL
|
||||
void PgfDB::link_transient_revision(ref<PgfConcr> concr)
|
||||
{
|
||||
concr->next = current_db->ms->transient_concr_revisions;
|
||||
if (current_db->ms->transient_concr_revisions != 0)
|
||||
current_db->ms->transient_concr_revisions->prev = concr;
|
||||
current_db->ms->transient_concr_revisions = concr;
|
||||
}
|
||||
|
||||
PGF_INTERNAL
|
||||
void PgfDB::unlink_transient_revision(ref<PgfConcr> concr)
|
||||
{
|
||||
if (concr->next != 0)
|
||||
concr->next->prev = concr->prev;
|
||||
if (concr->prev != 0)
|
||||
concr->prev->next = concr->next;
|
||||
else if (current_db->ms->transient_concr_revisions == concr)
|
||||
current_db->ms->transient_concr_revisions = concr->next;
|
||||
}
|
||||
|
||||
PGF_INTERNAL
|
||||
void PgfDB::sync()
|
||||
{
|
||||
|
||||
@@ -84,12 +84,17 @@ public:
|
||||
|
||||
static PGF_INTERNAL_DECL ref<PgfPGF> get_revision(PgfText *name);
|
||||
static PGF_INTERNAL_DECL void set_revision(ref<PgfPGF> pgf);
|
||||
|
||||
static PGF_INTERNAL_DECL ref<PgfPGF> revision2pgf(PgfRevision revision);
|
||||
static PGF_INTERNAL_DECL ref<PgfConcr> revision2concr(PgfConcrRevision revision);
|
||||
static PGF_INTERNAL_DECL bool is_persistant_revision(ref<PgfPGF> pgf);
|
||||
static PGF_INTERNAL_DECL void link_transient_revision(ref<PgfPGF> pgf);
|
||||
static PGF_INTERNAL_DECL void unlink_transient_revision(ref<PgfPGF> pgf);
|
||||
|
||||
static PGF_INTERNAL_DECL ref<PgfConcr> revision2concr(PgfConcrRevision revision);
|
||||
static PGF_INTERNAL_DECL bool is_persistant_revision(ref<PgfConcr> concr);
|
||||
static PGF_INTERNAL_DECL void link_transient_revision(ref<PgfConcr> concr);
|
||||
static PGF_INTERNAL_DECL void unlink_transient_revision(ref<PgfConcr> concr);
|
||||
|
||||
static PGF_INTERNAL_DECL void sync();
|
||||
|
||||
private:
|
||||
|
||||
@@ -106,9 +106,11 @@ next:
|
||||
}
|
||||
|
||||
PGF_INTERNAL
|
||||
pthread_rwlock_t *ipc_new_file_rwlock(const char* file_path)
|
||||
pthread_rwlock_t *ipc_new_file_rwlock(const char* file_path,
|
||||
bool *is_first)
|
||||
{
|
||||
if (file_path == NULL) {
|
||||
*is_first = true;
|
||||
pthread_rwlock_t *rwlock = (pthread_rwlock_t *)
|
||||
malloc(sizeof(pthread_rwlock_t));
|
||||
if (pthread_rwlock_init(rwlock, NULL) != 0) {
|
||||
@@ -189,7 +191,11 @@ pthread_rwlock_t *ipc_new_file_rwlock(const char* file_path)
|
||||
entry = ptr(entry->next, lock_entry);
|
||||
}
|
||||
|
||||
*is_first = false;
|
||||
|
||||
if (entry == NULL) {
|
||||
*is_first = true;
|
||||
|
||||
if (locks->free_lock_entries) {
|
||||
entry = ptr(locks->free_lock_entries, lock_entry);
|
||||
locks->free_lock_entries = entry->next;
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
#define IPC_H
|
||||
|
||||
PGF_INTERNAL_DECL
|
||||
pthread_rwlock_t *ipc_new_file_rwlock(const char* file_path);
|
||||
pthread_rwlock_t *ipc_new_file_rwlock(const char* file_path,
|
||||
bool *is_first);
|
||||
|
||||
PGF_INTERNAL_DECL
|
||||
void ipc_release_file_rwlock(const char* file_path,
|
||||
|
||||
@@ -259,22 +259,17 @@ void pgf_free_revision(PgfDB *db, PgfRevision revision)
|
||||
PGF_API_DECL
|
||||
void pgf_free_concr_revision(PgfDB *db, PgfConcrRevision revision)
|
||||
{
|
||||
/* try {
|
||||
try {
|
||||
DB_scope scope(db, WRITER_SCOPE);
|
||||
ref<PgfPGF> pgf = PgfDB::revision2pgf(revision);
|
||||
ref<PgfConcr> concr = PgfDB::revision2concr(revision);
|
||||
|
||||
if (pgf->ref_count == 1 && PgfDB::is_persistant_revision(pgf)) {
|
||||
// Someone is trying to release the last reference count
|
||||
// to a persistant revision. Mostly likely this is an
|
||||
// error in the reference counting for one of the clients.
|
||||
// The best that we can do is to ignore the request.
|
||||
return;
|
||||
if (!(--concr->ref_count_ex)) {
|
||||
PgfDB::unlink_transient_revision(concr);
|
||||
}
|
||||
|
||||
if (!(--pgf->ref_count)) {
|
||||
PgfDB::unlink_transient_revision(pgf);
|
||||
PgfPGF::release(pgf);
|
||||
PgfDB::free(pgf);
|
||||
if (!(--concr->ref_count)) {
|
||||
PgfConcr::release(concr);
|
||||
PgfDB::free(concr);
|
||||
}
|
||||
|
||||
db->ref_count--;
|
||||
@@ -283,7 +278,7 @@ void pgf_free_concr_revision(PgfDB *db, PgfConcrRevision revision)
|
||||
}
|
||||
|
||||
if (!db->ref_count)
|
||||
delete db;*/
|
||||
delete db;
|
||||
}
|
||||
|
||||
PGF_API
|
||||
@@ -312,6 +307,24 @@ void pgf_iter_categories(PgfDB *db, PgfRevision revision,
|
||||
} PGF_API_END
|
||||
}
|
||||
|
||||
struct PgfItorConcrHelper : PgfItor
|
||||
{
|
||||
PgfDB *db;
|
||||
PgfItor *itor;
|
||||
};
|
||||
|
||||
static
|
||||
void iter_concretes_helper(PgfItor *itor, PgfText *key, object value,
|
||||
PgfExn *err)
|
||||
{
|
||||
PgfItorConcrHelper* helper = (PgfItorConcrHelper*) itor;
|
||||
ref<PgfConcr> concr = value;
|
||||
concr->ref_count++;
|
||||
concr->ref_count_ex++;
|
||||
helper->db->ref_count++;
|
||||
helper->itor->fn(helper->itor, key, value, err);
|
||||
}
|
||||
|
||||
PGF_API
|
||||
void pgf_iter_concretes(PgfDB *db, PgfRevision revision,
|
||||
PgfItor *itor, PgfExn *err)
|
||||
@@ -320,7 +333,12 @@ void pgf_iter_concretes(PgfDB *db, PgfRevision revision,
|
||||
DB_scope scope(db, READER_SCOPE);
|
||||
ref<PgfPGF> pgf = PgfDB::revision2pgf(revision);
|
||||
|
||||
namespace_iter(pgf->concretes, itor, err);
|
||||
PgfItorConcrHelper helper;
|
||||
helper.fn = iter_concretes_helper;
|
||||
helper.db = db;
|
||||
helper.itor = itor;
|
||||
|
||||
namespace_iter(pgf->concretes, &helper, err);
|
||||
} PGF_API_END
|
||||
}
|
||||
|
||||
@@ -433,7 +451,7 @@ void pgf_iter_functions(PgfDB *db, PgfRevision revision,
|
||||
} PGF_API_END
|
||||
}
|
||||
|
||||
struct PgfItorHelper : PgfItor
|
||||
struct PgfItorCatHelper : PgfItor
|
||||
{
|
||||
PgfText *cat;
|
||||
PgfItor *itor;
|
||||
@@ -443,7 +461,7 @@ static
|
||||
void iter_by_cat_helper(PgfItor *itor, PgfText *key, object value,
|
||||
PgfExn *err)
|
||||
{
|
||||
PgfItorHelper* helper = (PgfItorHelper*) itor;
|
||||
PgfItorCatHelper* helper = (PgfItorCatHelper*) itor;
|
||||
ref<PgfAbsFun> absfun = value;
|
||||
if (textcmp(helper->cat, &absfun->type->name) == 0)
|
||||
helper->itor->fn(helper->itor, key, value, err);
|
||||
@@ -457,7 +475,7 @@ void pgf_iter_functions_by_cat(PgfDB *db, PgfRevision revision,
|
||||
DB_scope scope(db, READER_SCOPE);
|
||||
ref<PgfPGF> pgf = PgfDB::revision2pgf(revision);
|
||||
|
||||
PgfItorHelper helper;
|
||||
PgfItorCatHelper helper;
|
||||
helper.fn = iter_by_cat_helper;
|
||||
helper.cat = cat;
|
||||
helper.itor = itor;
|
||||
@@ -855,14 +873,21 @@ PgfConcrRevision pgf_create_concrete(PgfDB *db, PgfRevision revision,
|
||||
throw pgf_error("The concrete syntax already exists");
|
||||
|
||||
concr = PgfDB::malloc<PgfConcr>(name->size+1);
|
||||
concr->ref_count = 1;
|
||||
concr->ref_count = 1;
|
||||
concr->ref_count_ex = 1;
|
||||
concr->cflags = 0;
|
||||
concr->prev = 0;
|
||||
concr->next = 0;
|
||||
memcpy(&concr->name, name, sizeof(PgfText)+name->size+1);
|
||||
|
||||
PgfDB::link_transient_revision(concrter);
|
||||
|
||||
Namespace<PgfConcr> concrs =
|
||||
namespace_insert(pgf->concretes, concr);
|
||||
namespace_release(pgf->concretes);
|
||||
pgf->concretes = concrs;
|
||||
|
||||
db->ref_count++;
|
||||
return concr.as_object();
|
||||
} PGF_API_END
|
||||
return 0;
|
||||
@@ -884,14 +909,24 @@ PgfConcrRevision pgf_clone_concrete(PgfDB *db, PgfRevision revision,
|
||||
throw pgf_error("Unknown concrete syntax");
|
||||
|
||||
ref<PgfConcr> clone = PgfDB::malloc<PgfConcr>(name->size+1);
|
||||
clone->ref_count = 1;
|
||||
clone->ref_count = 1;
|
||||
clone->ref_count_ex = 1;
|
||||
clone->cflags = concr->cflags;
|
||||
clone->prev = 0;
|
||||
clone->next = 0;
|
||||
memcpy(&clone->name, name, sizeof(PgfText)+name->size+1);
|
||||
|
||||
if (clone->cflags != 0)
|
||||
Node<PgfFlag>::add_node_ref(clone->cflags);
|
||||
|
||||
PgfDB::link_transient_revision(clone);
|
||||
|
||||
Namespace<PgfConcr> concrs =
|
||||
namespace_insert(pgf->concretes, clone);
|
||||
namespace_release(pgf->concretes);
|
||||
pgf->concretes = concrs;
|
||||
|
||||
db->ref_count++;
|
||||
return clone.as_object();
|
||||
} PGF_API_END
|
||||
return 0;
|
||||
|
||||
@@ -434,8 +434,11 @@ void PgfReader::read_abstract(ref<PgfAbstr> abstract)
|
||||
ref<PgfConcr> PgfReader::read_concrete()
|
||||
{
|
||||
ref<PgfConcr> concr = read_name(&PgfConcr::name);
|
||||
concr->ref_count = 1;
|
||||
concr->ref_count = 1;
|
||||
concr->ref_count_ex = 0;
|
||||
concr->cflags = read_namespace<PgfFlag>(&PgfReader::read_flag);
|
||||
concr->prev = 0;
|
||||
concr->next = 0;
|
||||
return concr;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user