diff --git a/src/runtime/c/pgf/data.h b/src/runtime/c/pgf/data.h index cdc375934..84ece00ba 100644 --- a/src/runtime/c/pgf/data.h +++ b/src/runtime/c/pgf/data.h @@ -106,7 +106,15 @@ typedef struct { struct PGF_INTERNAL_DECL PgfConcr { size_t ref_count; - Namespace cflags; + size_t ref_count_ex; + Namespace 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 prev, next; + PgfText name; static void release(ref pgf); diff --git a/src/runtime/c/pgf/db.cxx b/src/runtime/c/pgf/db.cxx index 7920a1a19..1ea4d5e31 100644 --- a/src/runtime/c/pgf/db.cxx +++ b/src/runtime/c/pgf/db.cxx @@ -280,16 +280,18 @@ struct PGF_INTERNAL_DECL malloc_state /* The namespace of all persistant grammar revisions */ Namespace revisions; - /* A reference to the first transient revision in + /* A reference to the first transient revisions in * a double-linked list. */ - ref transient_revisions; + ref transient_revisions; + ref 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 pgf = ms->transient_revisions; + ref next = pgf->next; + PgfPGF::release(pgf); + PgfDB::free(pgf); + ms->transient_revisions = next; + } + + while (ms->transient_concr_revisions != 0) { + ref concr = ms->transient_concr_revisions; + ref 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 PgfDB::revision2pgf(PgfRevision revision) return pgf; } -PGF_INTERNAL -ref 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 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 pgf) { @@ -1088,6 +1092,50 @@ void PgfDB::unlink_transient_revision(ref pgf) current_db->ms->transient_revisions = pgf->next; } +PGF_INTERNAL +ref 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 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 concr) +{ + return (concr->prev == 0 && concr->next == 0 && + current_db->ms->transient_concr_revisions != concr); +} + +PGF_INTERNAL +void PgfDB::link_transient_revision(ref 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 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() { diff --git a/src/runtime/c/pgf/db.h b/src/runtime/c/pgf/db.h index 758bdc8de..13bb6f7d0 100644 --- a/src/runtime/c/pgf/db.h +++ b/src/runtime/c/pgf/db.h @@ -84,12 +84,17 @@ public: static PGF_INTERNAL_DECL ref get_revision(PgfText *name); static PGF_INTERNAL_DECL void set_revision(ref pgf); + static PGF_INTERNAL_DECL ref revision2pgf(PgfRevision revision); - static PGF_INTERNAL_DECL ref revision2concr(PgfConcrRevision revision); static PGF_INTERNAL_DECL bool is_persistant_revision(ref pgf); static PGF_INTERNAL_DECL void link_transient_revision(ref pgf); static PGF_INTERNAL_DECL void unlink_transient_revision(ref pgf); + static PGF_INTERNAL_DECL ref revision2concr(PgfConcrRevision revision); + static PGF_INTERNAL_DECL bool is_persistant_revision(ref concr); + static PGF_INTERNAL_DECL void link_transient_revision(ref concr); + static PGF_INTERNAL_DECL void unlink_transient_revision(ref concr); + static PGF_INTERNAL_DECL void sync(); private: diff --git a/src/runtime/c/pgf/ipc.cxx b/src/runtime/c/pgf/ipc.cxx index bfa8881b2..8a55154e4 100644 --- a/src/runtime/c/pgf/ipc.cxx +++ b/src/runtime/c/pgf/ipc.cxx @@ -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; diff --git a/src/runtime/c/pgf/ipc.h b/src/runtime/c/pgf/ipc.h index 70fe37b6b..6afa34ff4 100644 --- a/src/runtime/c/pgf/ipc.h +++ b/src/runtime/c/pgf/ipc.h @@ -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, diff --git a/src/runtime/c/pgf/pgf.cxx b/src/runtime/c/pgf/pgf.cxx index 9595b30e0..0444917f4 100644 --- a/src/runtime/c/pgf/pgf.cxx +++ b/src/runtime/c/pgf/pgf.cxx @@ -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 pgf = PgfDB::revision2pgf(revision); + ref 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 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 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 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 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(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 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 clone = PgfDB::malloc(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::add_node_ref(clone->cflags); + + PgfDB::link_transient_revision(clone); + Namespace 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; diff --git a/src/runtime/c/pgf/reader.cxx b/src/runtime/c/pgf/reader.cxx index ef3a8ea6c..5e892ef3e 100644 --- a/src/runtime/c/pgf/reader.cxx +++ b/src/runtime/c/pgf/reader.cxx @@ -434,8 +434,11 @@ void PgfReader::read_abstract(ref abstract) ref PgfReader::read_concrete() { ref concr = read_name(&PgfConcr::name); - concr->ref_count = 1; + concr->ref_count = 1; + concr->ref_count_ex = 0; concr->cflags = read_namespace(&PgfReader::read_flag); + concr->prev = 0; + concr->next = 0; return concr; }