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