00001 #ifndef MCS_LOCK_H 00002 #define MCS_LOCK_H 00003 /* -*- mode:C++; c-basic-offset:4 -*- 00004 Shore-MT -- Multi-threaded port of the SHORE storage manager 00005 00006 Copyright (c) 2007-2009 00007 Data Intensive Applications and Systems Labaratory (DIAS) 00008 Ecole Polytechnique Federale de Lausanne 00009 00010 All Rights Reserved. 00011 00012 Permission to use, copy, modify and distribute this software and 00013 its documentation is hereby granted, provided that both the 00014 copyright notice and this permission notice appear in all copies of 00015 the software, derivative works or modified versions, and any 00016 portions thereof, and that both notices appear in supporting 00017 documentation. 00018 00019 This code is distributed in the hope that it will be useful, but 00020 WITHOUT ANY WARRANTY; without even the implied warranty of 00021 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS 00022 DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER 00023 RESULTING FROM THE USE OF THIS SOFTWARE. 00024 */ 00025 00026 // -*- mode:c++; c-basic-offset:4 -*- 00027 /*<std-header orig-src='shore' incl-file-exclusion='MCS_LOCK_H'> 00028 00029 $Id: mcs_lock.h,v 1.7 2010/11/08 15:07:23 nhall Exp $ 00030 00031 SHORE -- Scalable Heterogeneous Object REpository 00032 00033 Copyright (c) 1994-99 Computer Sciences Department, University of 00034 Wisconsin -- Madison 00035 All Rights Reserved. 00036 00037 Permission to use, copy, modify and distribute this software and its 00038 documentation is hereby granted, provided that both the copyright 00039 notice and this permission notice appear in all copies of the 00040 software, derivative works or modified versions, and any portions 00041 thereof, and that both notices appear in supporting documentation. 00042 00043 THE AUTHORS AND THE COMPUTER SCIENCES DEPARTMENT OF THE UNIVERSITY 00044 OF WISCONSIN - MADISON ALLOW FREE USE OF THIS SOFTWARE IN ITS 00045 "AS IS" CONDITION, AND THEY DISCLAIM ANY LIABILITY OF ANY KIND 00046 FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 00047 00048 This software was developed with support by the Advanced Research 00049 Project Agency, ARPA order number 018 (formerly 8230), monitored by 00050 the U.S. Army Research Laboratory under contract DAAB07-91-C-Q518. 00051 Further funding for this work was provided by DARPA through 00052 Rome Research Laboratory Contract No. F30602-97-2-0247. 00053 00054 */ 00055 00056 /* -- do not edit anything above this line -- </std-header>*/ 00057 00058 /**\cond skip */ 00059 00060 /**\brief An MCS queuing spinlock. 00061 * 00062 * Useful for short, contended critical sections. 00063 * If contention is expected to be rare, use a 00064 * tatas_lock; 00065 * if critical sections are long, use pthread_mutex_t so 00066 * the thread can block instead of spinning. 00067 * 00068 * Tradeoffs are: 00069 - test-and-test-and-set locks: low-overhead but not scalable 00070 - queue-based locks: higher overhead but scalable 00071 - pthread mutexes : high overhead and blocks, but frees up cpu for other threads 00072 */ 00073 struct mcs_lock { 00074 struct qnode; 00075 typedef qnode volatile* qnode_ptr; 00076 struct qnode { 00077 qnode_ptr _next; 00078 bool _waiting; 00079 // qnode() : _next(NULL), _waiting(false) { } // non-POD, alas 00080 }; 00081 struct ext_qnode { 00082 qnode _node; 00083 mcs_lock* _held; 00084 operator qnode*() { return &_node; } 00085 }; 00086 #define MCS_EXT_QNODE_INITIALIZER {{NULL,false},NULL} 00087 #define MCS_EXT_QNODE_INITIALIZE(x) \ 00088 { (x)._node._next = NULL; (x)._node._waiting = false; (x)._held = NULL; } 00089 qnode_ptr volatile _tail; 00090 mcs_lock() : _tail(NULL) { } 00091 00092 /* This spinning occurs whenever there are critical sections ahead 00093 of us. 00094 00095 CC mangles this as __1cImcs_lockPspin_on_waiting6Mpon0AFqnode__v_ 00096 */ 00097 void spin_on_waiting(qnode_ptr me) { 00098 while(me->_waiting); 00099 } 00100 /* Only acquire the lock if it is free... 00101 */ 00102 bool attempt(ext_qnode* me) { 00103 if(attempt((qnode_ptr) me)) { 00104 me->_held = this; 00105 return true; 00106 } 00107 return false; 00108 } 00109 bool attempt(qnode_ptr me) { 00110 me->_next = NULL; 00111 me->_waiting = true; 00112 membar_producer(); 00113 qnode_ptr pred = (qnode_ptr) atomic_cas_ptr(&_tail, 0, (void*) me); 00114 // lock held? 00115 if(pred) 00116 return false; 00117 membar_enter(); 00118 return true; 00119 } 00120 // return true if the lock was free 00121 void* acquire(ext_qnode* me) { 00122 me->_held = this; 00123 return acquire((qnode*) me); 00124 } 00125 void* acquire(qnode_ptr me) { 00126 me->_next = NULL; 00127 me->_waiting = true; 00128 membar_producer(); 00129 qnode_ptr pred = (qnode_ptr) atomic_swap_ptr(&_tail, (void*) me); 00130 if(pred) { 00131 pred->_next = me; 00132 spin_on_waiting(me); 00133 } 00134 membar_enter(); 00135 return (void*) pred; 00136 } 00137 00138 /* This spinning only occurs when we are at _tail and catch a 00139 thread trying to enqueue itself. 00140 00141 CC mangles this as __1cImcs_lockMspin_on_next6Mpon0AFqnode__3_ 00142 */ 00143 qnode_ptr spin_on_next(qnode_ptr me) { 00144 qnode_ptr next; 00145 while(!(next=me->_next)); 00146 return next; 00147 } 00148 void release(ext_qnode *me) { 00149 w_assert1(is_mine(me)); 00150 me->_held = 0; release((qnode*) me); 00151 } 00152 void release(ext_qnode &me) { w_assert1(is_mine(&me)); release(&me); } 00153 void release(qnode &me) { release(&me); } 00154 void release(qnode_ptr me) { 00155 membar_exit(); 00156 00157 qnode_ptr next; 00158 if(!(next=me->_next)) { 00159 if(me == _tail && me == (qnode_ptr) 00160 atomic_cas_ptr(&_tail, (void*) me, NULL)) 00161 return; 00162 next = spin_on_next(me); 00163 } 00164 next->_waiting = false; 00165 } 00166 // bool is_mine(qnode_ptr me) { return me->_held == this; } 00167 bool is_mine(ext_qnode* me) { return me->_held == this; } 00168 }; 00169 /**\endcond skip */ 00170 #endif 00171