From owner-freebsd-hackers@FreeBSD.ORG Tue Aug 2 22:22:10 2005 Return-Path: X-Original-To: freebsd-hackers@freebsd.org Delivered-To: freebsd-hackers@freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 083D016A41F for ; Tue, 2 Aug 2005 22:22:10 +0000 (GMT) (envelope-from hselasky@c2i.net) Received: from swip.net (mailfe09.tele2.se [212.247.155.1]) by mx1.FreeBSD.org (Postfix) with ESMTP id 7B31E43D53 for ; Tue, 2 Aug 2005 22:22:09 +0000 (GMT) (envelope-from hselasky@c2i.net) X-T2-Posting-ID: gvlK0tOCzrqh9CPROFOFPw== Received: from mp-217-203-26.daxnet.no ([193.217.203.26] verified) by mailfe09.swip.net (CommuniGate Pro SMTP 4.3.4) with ESMTP id 223986074 for freebsd-hackers@freebsd.org; Wed, 03 Aug 2005 00:22:06 +0200 From: Hans Petter Selasky To: freebsd-hackers@freebsd.org Date: Wed, 3 Aug 2005 00:23:04 +0200 User-Agent: KMail/1.7 MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Content-Disposition: inline Message-Id: <200508030023.04748.hselasky@c2i.net> Subject: How to do proper locking X-BeenThere: freebsd-hackers@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list Reply-To: hselasky@c2i.net List-Id: Technical Discussions relating to FreeBSD List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 02 Aug 2005 22:22:10 -0000 Hi, I am looking for a safe way to access structures that can be freed. The solution I am looking for must not: - hinder scaleability - lead to use of a single lock - lead to lock order reversal Here is the solution I have landed on so far: First I plan to make a reference count manager that has the following extern functions: u_int32_t *ref_alloc(); // will allocate a reference count void ref_free(u_int32_t *); // will free a reference count and increment it. // Note that the pointer passed to this function // is still valid after its call u_int32_t ref_atomic_read(u_int32_t *); // will read the value // of a reference count void ref_atomic_increment(u_int32_t *); // will increment the value // of a reference count Assume that we have the following structure: struct my_struct { struct mtx *p_mtx; u_int32_t *p_ref; u_int8_t my_data[256]; }; At some point this structure is allocated: static struct my_struct *ptr; mtx_lock(lock_A); if(ptr == NULL) { ptr = malloc(...); if(ptr) { ptr->p_ref = ref_alloc(); ptr->p_mtx = mtx_alloc(); } } mtx_unlock(lock_A); At some other point it is freed: mtx_lock(lock_A); ptr_copy = ptr; ptr = NULL; mtx_unlock(lock_A); if(ptr_copy) { mtx_lock(ptr_copy->p_mtx); // we want to hold this lock // to block the callback while // incrementing refcount ref_free(ptr_copy->p_ref); // will increment the refcount mtx_unlock(ptr_copy->p_mtx); mtx_free(ptr_copy->p_mtx); // Note: mutex is still valid after this! free(ptr_copy); } Then at the last point we want to access this structure, but we don't want to hold "lock_A", but rather "ptr->p_mtx", to increase performance. FIRST_PART: mtx_lock(lock_A); if(ptr) { p_ref_copy = ptr->p_ref; ref_value = ref_atomic_read(ptr->p_ref); p_mtx_copy = ptr->p_mtx; } else { p_ref_copy = NULL; } mtx_unlock(lock_A); SECOND_PART: if(p_ref_copy) { mtx_lock(p_mtx_copy); if(ref_value == ref_atomic_read(p_ref_copy)) { /* this structure is still allocated */ CALLBACK_CODE_HERE: } else { /* this structure has been freed */ } mtx_unlock(p_mtx_copy); } To access a memory structure safely, one needs three parameters, according to my theory: "p_ref_copy", "ref_value", "p_mtx_copy". Then I thought that one might prestore these, so that the "FIRST_PART" can be skipped, left with only the "SECOND_PART". Then I thought that the "SECOND_PART" could be implemented by existing callbacks, so that we stay out of trouble. Any comments ? (Hence todays computers are so fast, one might want to use a 64-bit reference count. So after some billion years my model will fail, but one will probably reboot long before that :-) --HPS