reference counting for concrete syntaxes

This commit is contained in:
krangelov
2021-11-03 10:48:20 +01:00
parent 2320c6b3b0
commit 309a16d471
7 changed files with 157 additions and 51 deletions

View File

@@ -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);

View File

@@ -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()
{

View File

@@ -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:

View File

@@ -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;

View File

@@ -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,

View File

@@ -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;

View File

@@ -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;
}