//-< OBJECT.CXX >----------------------------------------------------*--------* // GOODS Version 1.0 (c) 1997 GARRET * ? * // (Generic Object Oriented Database System) * /\| * // * / \ * // Created: 7-Jan-97 K.A. Knizhnik * / [] \ * // Last update: 26-Apr-97 K.A. Knizhnik * GARRET * //-------------------------------------------------------------------*--------* // Base class for all GOODS classes //-------------------------------------------------------------------*--------* #include "goods.h" BEGIN_GOODS_NAMESPACE // // Object header index management // #define INIT_INDEX_TABLE_SIZE (64*1024) hnd_t object_handle::hash_table[OBJECT_HANDLE_HASH_TABLE_SIZE]; dnm_object_pool object_handle::handle_pool(sizeof(object_handle), False); inline unsigned object_handle::hash_function(obj_storage* storage, opid_t opid) { unsigned sid = storage->id; return (opid ^ (opid >> 8) ^ (opid >> 16) ^ (opid >> 24) ^ (sid << 8)) % OBJECT_HANDLE_HASH_TABLE_SIZE; } hnd_t object_handle::find(obj_storage* storage, opid_t opid) { hnd_t hnd = hash_table[hash_function(storage, opid)]; while (hnd != 0) { if (hnd->opid == opid && hnd->storage == storage) { return hnd; } hnd = hnd->next; } return hnd; } hnd_t object_handle::allocate(object* obj, obj_storage* storage) { object_monitor::lock_global(); hnd_t hnd = (hnd_t)handle_pool.alloc(); hnd->storage = storage; hnd->obj = obj; hnd->access = 0; hnd->opid = 0; object_monitor::unlock_global(); return hnd; } void object_handle::deallocate(hnd_t hnd) { internal_assert(hnd->access == 0); if (IS_VALID_OBJECT(hnd->obj)) { if (!(hnd->obj->state & (object_header::persistent|object_header::removed))) { // // Destructor of 'object' will call 'deallocate' methos // once again but with obj == THROWN_OBJECT or obj == NULL // hnd->obj->remove_object(); } return; } else if (hnd->obj == THROWN_OBJECT) { internal_assert(hnd->opid != 0); hnd->storage->forget_object(hnd->opid); } if (hnd->opid != 0) { deassign_persistent_oid(hnd); } handle_pool.dealloc(hnd); } void object_handle::remove_from_storage(hnd_t hnd) { hnd->storage->deallocate(hnd->opid); deassign_persistent_oid(hnd); } void object_handle::assign_persistent_oid(hnd_t hnd, opid_t opid) { hnd->opid = opid; hnd_t* chain = &hash_table[hash_function(hnd->storage, opid)]; hnd->next = *chain; *chain = hnd; hnd->storage->add_reference(); } void object_handle::deassign_persistent_oid(hnd_t hnd) { hnd_t* chain = &hash_table[hash_function(hnd->storage, hnd->opid)]; while (*chain != hnd) { chain = &(*chain)->next; } *chain = hnd->next; hnd->opid = 0; hnd->storage->remove_reference(); } #ifdef _DEBUG /*****************************************************************************\ dump - dump this handle's object class type & memory address ------------------------------------------------------------------------------- This function prints this handle's object class type & memory address, as well as the number of references to this object, the object's storage and object ID. \*****************************************************************************/ void object_handle::dump(void) { char storageID[20]; const char* type; if (obj != NULL) { type = obj->cls.name; } else { type = "n/a"; } if (storage != NULL) { sprintf(storageID, "%04lx", (long)storage->id); } else { sprintf(storageID, "none"); } console::output("Object %s:%08lx at (%08lx) of class \"%s\" " "has %ld references.\n", storageID, opid, (nat4)obj, type, access); } /*****************************************************************************\ dumpCachedDBhandles - print class type & memory address of each handle's object ------------------------------------------------------------------------------- This function prints the class type, reference count, and memory address of each cached database object referenced by an object handle. The output is sent to the default console output. \*****************************************************************************/ void object_handle::dumpCachedDBhandles(void) { hnd_t hnd; nat4 i; for (i = 0; i < OBJECT_HANDLE_HASH_TABLE_SIZE; i++) { hnd = hash_table[i]; while (hnd != NULL) { hnd->dump(); hnd = hnd->next; } } } #endif void object_handle::make_persistent(hnd_t hnd, obj_storage* parent_storage) { object* obj = hnd->obj; if (hnd->storage != NULL) { assert(hnd->storage->db == parent_storage->db); } else { hnd->storage = parent_storage; } class_descriptor* desc = &obj->cls; obj->cpid = hnd->storage->get_cpid_by_descriptor(desc); opid_t opid = hnd->storage->allocate(obj->cpid, desc->packed_size((char*)obj, obj->size), (desc->class_attr & class_descriptor::cls_aligned) != 0); assign_persistent_oid(hnd, opid); } hnd_t object_handle::create_persistent_reference(obj_storage* storage, opid_t opid, int n_refs) { hnd_t hnd = find(storage, opid); if (hnd == 0) { hnd = allocate(NULL, storage); assign_persistent_oid(hnd, opid); } hnd->access += n_refs; return hnd; } // // Object control methods // boolean object_header::on_write_conflict() { console::error("Write conflict for object %x of class \"%s\"\n", hnd, ((object*)this)->cls.name); return False; } boolean object_header::on_lock_failed(lck_t) { return False; } void object_header::on_loading() {} void object_header::remove_object() { state |= removed; delete this; } object_header::~object_header() {} // // Object methods // metaobject* object::default_mop; object* object::become(object* new_obj) { object_monitor::lock_global(); assert(new_obj->state == 0 && new_obj->n_invokations == 0); hnd_t new_hnd = new_obj->hnd; new_obj->monitor = monitor; new_obj->hnd = hnd; new_obj->next = next; new_obj->prev = prev; new_obj->state = state; new_obj->n_invokations = n_invokations; hnd->obj = new_obj; state = 0; monitor = 0; n_invokations = 0; next = prev = 0; if (new_hnd != 0 && new_hnd != hnd) { // // Swap objects // internal_assert(new_hnd->obj == new_obj); hnd = new_hnd; hnd->obj = this; if (hnd->access == 0) { remove_object(); } } else { hnd = 0; remove_object(); } object_monitor::unlock_global(); return new_obj; } object* object::load_last_version() { object_monitor::lock_global(); hnd_t copy_hnd = object_handle::allocate(NULL); copy_hnd->storage = hnd->storage; copy_hnd->opid = hnd->opid; copy_hnd->obj = COPIED_OBJECT; hnd->storage->load(copy_hnd, lof_copy); object_monitor::unlock_global(); return copy_hnd->obj; } void object::begin_transaction() { object_monitor::lock_global(); mop->begin_transaction(); object_monitor::unlock_global(); } void object::abort_transaction() { object_monitor::lock_global(); mop->abort_transaction(get_database()); object_monitor::unlock_global(); } void object::end_transaction() { object_monitor::lock_global(); mop->end_transaction(); object_monitor::unlock_global(); } boolean object::is_abstract_root() const { return False; } void object::signal_on_modification(event& e) const { object_monitor::lock_global(); mop->signal_on_modification(hnd, e); object_monitor::unlock_global(); } void object::cluster_with(object_reference const& r) const { assert(hnd != 0); assert(hnd->opid == 0); // object not yet persistent hnd->storage = r.get_handle()->storage; } void object::attach_to_storage(database const* db, int sid) const { assert(hnd != 0); assert(hnd->opid == 0); // object not yet persistent assert(sid < db->n_storages); hnd->storage = db->storages[sid]; } void object::wait() const { object_monitor::lock_global(); mop->wait(hnd); object_monitor::unlock_global(); } boolean object::wait(time_t timeout) const { object_monitor::lock_global(); boolean result = mop->wait(hnd, timeout); object_monitor::unlock_global(); return result; } void object::notify() const { object_monitor::lock_global(); mop->notify(hnd); object_monitor::unlock_global(); } database const* object::get_database() const { return hnd->storage ? hnd->storage->db : (database const*)0; } object::~object() { object_monitor::lock_global(); if (cls.n_varying_references > 0) { cls.destroy_varying_part_references(this); } if (hnd != 0) { // // We can remove object when there are no references to it or it is // persistent object which can be loaded on demand from database // assert(n_invokations == 0 && (hnd->access == 0 || hnd->opid != 0)); mop->destroy(hnd); if (state & persistent) { hnd->obj = THROWN_OBJECT; if (hnd->access != 0) { hnd->storage->throw_object(hnd->opid); } } else { hnd->obj = NULL; } if (hnd->access == 0) { object_handle::deallocate(hnd); } } object_monitor::unlock_global(); } #ifndef __GOODS_MFCDLL class_descriptor& classof(object const*) { return object::self_class; } #endif field_descriptor& object::describe_components() { return NO_FIELDS; } object* object::constructor(hnd_t hnd, class_descriptor& desc, size_t) { return NEW object(hnd, desc, 0); } class_descriptor object::self_class("object", sizeof(object), &optimistic_scheme, &object::constructor, NULL); boolean storage_root::is_abstract_root() const { return True; } field_descriptor& storage_root::describe_components() { return NO_FIELDS; } REGISTER(storage_root, object, pessimistic_exclusive_scheme); END_GOODS_NAMESPACE