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.4 2010/06/23 23:43:43 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) { } 00080 }; 00081 struct ext_qnode : qnode { 00082 mcs_lock* _held; 00083 }; 00084 qnode_ptr volatile _tail; 00085 mcs_lock() : _tail(NULL) { } 00086 00087 /* This spinning occurs whenever there are critical sections ahead 00088 of us. 00089 00090 CC mangles this as __1cImcs_lockPspin_on_waiting6Mpon0AFqnode__v_ 00091 */ 00092 void spin_on_waiting(qnode_ptr me) { 00093 while(me->_waiting); 00094 } 00095 /* Only acquire the lock if it is free... 00096 */ 00097 bool attempt(ext_qnode* me) { 00098 if(attempt((qnode_ptr) me)) { 00099 me->_held = this; 00100 return true; 00101 } 00102 return false; 00103 } 00104 bool attempt(qnode_ptr me) { 00105 me->_next = NULL; 00106 me->_waiting = true; 00107 membar_producer(); 00108 qnode_ptr pred = (qnode_ptr) atomic_cas_ptr(&_tail, 0, (void*) me); 00109 // lock held? 00110 if(pred) 00111 return false; 00112 membar_enter(); 00113 return true; 00114 } 00115 // return true if the lock was free 00116 void* acquire(ext_qnode* me) { 00117 me->_held = this; 00118 return acquire((qnode*) me); 00119 } 00120 void* acquire(qnode_ptr me) { 00121 me->_next = NULL; 00122 me->_waiting = true; 00123 membar_producer(); 00124 qnode_ptr pred = (qnode_ptr) atomic_swap_ptr(&_tail, (void*) me); 00125 if(pred) { 00126 pred->_next = me; 00127 spin_on_waiting(me); 00128 } 00129 membar_enter(); 00130 return (void*) pred; 00131 } 00132 00133 /* This spinning only occurs when we are at _tail and catch a 00134 thread trying to enqueue itself. 00135 00136 CC mangles this as __1cImcs_lockMspin_on_next6Mpon0AFqnode__3_ 00137 */ 00138 qnode_ptr spin_on_next(qnode_ptr me) { 00139 qnode_ptr next; 00140 while(!(next=me->_next)); 00141 return next; 00142 } 00143 void release(ext_qnode *me) { 00144 w_assert1(is_mine(me)); 00145 me->_held = 0; release((qnode*) me); 00146 } 00147 void release(ext_qnode &me) { release(&me); } 00148 void release(qnode &me) { release(&me); } 00149 void release(qnode_ptr me) { 00150 w_assert1(is_mine(me)); 00151 membar_exit(); 00152 00153 qnode_ptr next; 00154 if(!(next=me->_next)) { 00155 if(me == _tail && me == (qnode_ptr) 00156 atomic_cas_ptr(&_tail, (void*) me, NULL)) 00157 return; 00158 next = spin_on_next(me); 00159 } 00160 next->_waiting = false; 00161 } 00162 bool is_mine(qnode_ptr me) { return me._held == this; } 00163 bool is_mine(ext_qnode* me) { return me->_held == this; } 00164 }; 00165 /**\endcond skip */ 00166 #endif 00167