Changeset 563 for trunk/kernel/libk/remote_rwlock.c
- Timestamp:
- Oct 4, 2018, 11:16:13 PM (6 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/kernel/libk/remote_rwlock.c
r457 r563 31 31 #include <remote_rwlock.h> 32 32 33 /////////////////////////////////////////// 34 void remote_rwlock_init( xptr_t lock_xp ) 33 ////////////////////////////////////////////////////////////////////////////// 34 // Extern global variables 35 ////////////////////////////////////////////////////////////////////////////// 36 37 extern char * lock_type_str[]; // allocated in kernel_init.c 38 39 40 ////////////////////////////////////////// 41 void remote_rwlock_init( xptr_t lock_xp, 42 uint32_t type ) 35 43 { 36 44 remote_rwlock_t * lock_ptr = GET_PTR( lock_xp ); 37 45 cxy_t lock_cxy = GET_CXY( lock_xp ); 38 46 39 hal_remote_sw ( XPTR( lock_cxy , &lock_ptr->ticket ) , 0 ); 40 hal_remote_sw ( XPTR( lock_cxy , &lock_ptr->current ) , 0 ); 41 hal_remote_sw ( XPTR( lock_cxy , &lock_ptr->count ) , 0 ); 42 43 #if DEBUG_REMOTE_RWLOCKS 44 hal_remote_swd( XPTR( lock_cxy , &lock_ptr->owner ) , XPTR_NULL ); 45 xlist_entry_init( XPTR( lock_cxy , &lock_ptr->list ) ); 46 #endif 47 48 } 49 50 ////////////////////////////////////////////// 51 void remote_rwlock_rd_lock( xptr_t lock_xp ) 47 hal_remote_s32 ( XPTR( lock_cxy , &lock_ptr->taken ) , 0 ); 48 hal_remote_s32 ( XPTR( lock_cxy , &lock_ptr->count ) , 0 ); 49 50 xlist_root_init( XPTR( lock_cxy , &lock_ptr->rd_xroot ) ); 51 xlist_root_init( XPTR( lock_cxy , &lock_ptr->wr_xroot ) ); 52 53 remote_busylock_init( XPTR( lock_cxy , &lock_ptr->lock ) , type ); 54 } 55 56 /////////////////////////////////////////////// 57 void remote_rwlock_rd_acquire( xptr_t lock_xp ) 52 58 { 53 reg_t mode; 54 uint32_t ticket; 59 thread_t * this = CURRENT_THREAD; 60 61 // check calling thread can yield 62 thread_assert_can_yield( this , __FUNCTION__ ); 55 63 56 64 // get cluster and local pointer on remote_rwlock … … 58 66 cxy_t lock_cxy = GET_CXY( lock_xp ); 59 67 60 // get local pointer on local thread 61 thread_t * thread_ptr = CURRENT_THREAD; 62 63 // extended pointers on ticket, current, count 64 xptr_t ticket_xp = XPTR( lock_cxy , &lock_ptr->ticket ); 65 xptr_t current_xp = XPTR( lock_cxy , &lock_ptr->current ); 66 xptr_t count_xp = XPTR( lock_cxy , &lock_ptr->count ); 67 68 // disable interrupts 69 hal_disable_irq( &mode ); 70 71 // get next free ticket 72 ticket = hal_remote_atomic_add( ticket_xp , 1 ); 73 74 // busy waiting loop to take the lock 75 while( ticket != hal_remote_lw( current_xp ) ) 76 { 77 hal_fixed_delay( CONFIG_RWLOCK_DELAY ); 78 } 79 80 ////////// From here we have the lock //////////// 81 82 // increment count 68 // build useful extended pointers 69 xptr_t busylock_xp = XPTR( lock_cxy , &lock_ptr->lock ); 70 xptr_t taken_xp = XPTR( lock_cxy , &lock_ptr->taken ); 71 xptr_t count_xp = XPTR( lock_cxy , &lock_ptr->count ); 72 xptr_t rd_root_xp = XPTR( lock_cxy , &lock_ptr->rd_xroot ); 73 74 // get busylock 75 remote_busylock_acquire( busylock_xp ); 76 77 // block and deschedule if lock taken 78 while( hal_remote_l32( taken_xp ) ) 79 { 80 81 #if DEBUG_RWLOCK 82 if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) 83 { 84 uint32_t type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->lock.type ) ); 85 printk("\n[DBG] %s : thread %x (%s) READ BLOCK on rwlock %s [%x,%x] / cycle %d\n", 86 __FUNCTION__, this->trdid, thread_type_str(this->type), 87 lock_type_str[type], lock_cxy, lock_ptr, (uint32_t)hal_get_cycles() ); 88 } 89 #endif 90 // get pointer on calling thread 91 thread_t * this = CURRENT_THREAD; 92 93 // register reader thread in waiting queue 94 xlist_add_last( rd_root_xp , XPTR( local_cxy , &this->wait_xlist ) ); 95 96 // block reader thread 97 thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_LOCK ); 98 99 // release busylock 100 remote_busylock_release( busylock_xp ); 101 102 // deschedule 103 sched_yield("reader wait remote_rwlock"); 104 105 // get busylock 106 remote_busylock_acquire( busylock_xp ); 107 } 108 109 #if DEBUG_RWLOCK 110 if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) 111 { 112 uint32_t type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->lock.type ) ); 113 printk("\n[DBG] %s : thread %x (%s) READ ACQUIRE on rwlock %s [%x,%x] / cycle %d\n", 114 __FUNCTION__, this->trdid, thread_type_str(this->type), 115 lock_type_str[type], lock_cxy, lock_ptr, (uint32_t)hal_get_cycles() ); 116 } 117 #endif 118 119 // increment number of readers 83 120 hal_remote_atomic_add( count_xp , 1 ); 84 121 85 // increment thread.remote_locks 86 thread_ptr->remote_locks++; 87 88 #if DEBUG_REMOTE_RWLOCKS 89 xlist_add_first( XPTR( local_cxy , &thread_ptr->xlocks_root ) , 90 XPTR( lock_cxy , &lock_ptr->list ) ); 91 #endif 92 93 // sync 94 hal_fence(); 95 96 // release lock to allow several simultaneous readers 97 hal_remote_atomic_add( current_xp , 1 ); 98 99 // enable interrupts 100 hal_restore_irq( mode ); 101 102 } // end remote_rwlock_rd_lock() 103 104 //////////////////////////////////////////////// 105 void remote_rwlock_rd_unlock( xptr_t lock_xp ) 106 { 107 reg_t mode; 122 // release busylock 123 remote_busylock_release( busylock_xp ); 124 125 } // end remote_rwlock_rd_acquire() 126 127 /////////////////////////////////////////////// 128 void remote_rwlock_wr_acquire( xptr_t lock_xp ) 129 { 130 thread_t * this = CURRENT_THREAD; 131 132 // check calling thread can yield 133 thread_assert_can_yield( this , __FUNCTION__ ); 108 134 109 135 // get cluster and local pointer on remote_rwlock … … 111 137 cxy_t lock_cxy = GET_CXY( lock_xp ); 112 138 113 // get cluster and local pointer on local thread 114 thread_t * thread_ptr = CURRENT_THREAD; 115 116 // extended pointers on lock->count 117 xptr_t count_xp = XPTR( lock_cxy , &lock_ptr->count ); 118 119 // disable interrupts 120 hal_disable_irq( &mode ); 121 122 // decrement count 123 hal_remote_atomic_add( count_xp , -1 ); 124 125 // decrement thread.remote_locks 126 thread_ptr->remote_locks--; 127 128 #if DEBUG_REMOTE_RWLOCKS 129 xlist_unlink( XPTR( lock_cxy , &lock_ptr->list ) ); 130 #endif 131 132 // enable interrupts 133 hal_restore_irq( mode ); 134 135 // deschedule if pending request 136 thread_check_sched(); 137 138 } // end remote_rwlock_rd_unlock() 139 140 ////////////////////////////////////////////// 141 void remote_rwlock_wr_lock( xptr_t lock_xp ) 142 { 143 reg_t mode; 144 uint32_t ticket; 139 // build useful extended pointers 140 xptr_t busylock_xp = XPTR( lock_cxy , &lock_ptr->lock ); 141 xptr_t taken_xp = XPTR( lock_cxy , &lock_ptr->taken ); 142 xptr_t count_xp = XPTR( lock_cxy , &lock_ptr->count ); 143 xptr_t wr_root_xp = XPTR( lock_cxy , &lock_ptr->wr_xroot ); 144 145 // get busylock 146 remote_busylock_acquire( busylock_xp ); 147 148 // block and deschedule if lock already taken or current readers 149 while( hal_remote_l32( taken_xp ) || hal_remote_l32( count_xp ) ) 150 { 151 152 #if DEBUG_RWLOCK 153 if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) 154 { 155 uint32_t type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->lock.type ) ); 156 printk("\n[DBG] %s : thread %x (%s) WRITE BLOCK on rwlock %s [%x,%x] / cycle %d\n", 157 __FUNCTION__, this->trdid, thread_type_str(this->type), 158 lock_type_str[type], lock_cxy, lock_ptr, (uint32_t)hal_get_cycles() ); 159 } 160 #endif 161 // get local pointer on calling thread 162 thread_t * this = CURRENT_THREAD; 163 164 // register writer thread in waiting queue 165 xlist_add_last( wr_root_xp , XPTR( local_cxy , &this->wait_xlist ) ); 166 167 // block writer thread 168 thread_block( XPTR( local_cxy , this ) , THREAD_BLOCKED_LOCK ); 169 170 // release busylock 171 remote_busylock_release( busylock_xp ); 172 173 // deschedule 174 sched_yield("writer wait remote_rwlock"); 175 176 // get busylock 177 remote_busylock_acquire( busylock_xp ); 178 } 179 180 #if DEBUG_RWLOCK 181 if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) 182 { 183 uint32_t type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->lock.type ) ); 184 printk("\n[DBG] %s : thread %x (%s) WRITE ACQUIRE on rwlock %s [%x,%x] / cycle %d\n", 185 __FUNCTION__, this->trdid, thread_type_str(this->type), 186 lock_type_str[type], lock_cxy, lock_ptr, (uint32_t)hal_get_cycles() ); 187 } 188 #endif 189 190 // take rwlock 191 hal_remote_s32( taken_xp , 1 ); 192 193 // release busylock 194 remote_busylock_release( busylock_xp ); 195 196 } // end remote_rwlock_wr_acquire() 197 198 199 /////////////////////////////////////////////// 200 void remote_rwlock_rd_release( xptr_t lock_xp ) 201 { 202 // memory barrier before lock release 203 hal_fence(); 145 204 146 205 // get cluster and local pointer on remote_rwlock … … 148 207 cxy_t lock_cxy = GET_CXY( lock_xp ); 149 208 150 // get local pointer on local thread 151 thread_t * thread_ptr = CURRENT_THREAD; 152 153 // compute extended pointers on lock->ticket, lock->owner 154 xptr_t ticket_xp = XPTR( lock_cxy , &lock_ptr->ticket ); 155 xptr_t count_xp = XPTR( lock_cxy , &lock_ptr->count ); 156 xptr_t current_xp = XPTR( lock_cxy , &lock_ptr->current ); 157 158 // disable interrupts 159 hal_disable_irq( &mode ); 160 161 // get next free ticket 162 ticket = hal_remote_atomic_add( ticket_xp , 1 ); 163 164 // loop to take the lock 165 while( ticket != hal_remote_lw( current_xp ) ) 166 { 167 hal_fixed_delay( CONFIG_RWLOCK_DELAY ); 168 } 169 170 ////////// From here we have the lock //////////// 171 172 // wait completion of read accesses 173 while( hal_remote_lw( count_xp ) != 0 ) 174 { 175 hal_fixed_delay( CONFIG_RWLOCK_DELAY ); 176 } 177 178 #if DEBUG_REMOTE_RWLOCKS 179 hal_remote_swd( XPTR( lock_cxy , &lock_ptr->owner ) , 180 XPTR( local_cxy , thread_ptr ) ); 181 xlist_add_first( XPTR( local_cxy , &thread_ptr->xlocks_root ) , 182 XPTR( lock_cxy , &lock_ptr->list ) ); 183 #endif 184 185 // increment thread.remote_locks 186 thread_ptr->remote_locks++; 187 188 // enable interrupts 189 hal_restore_irq( mode ); 190 191 } // end remote_rwlock_wr_lock() 192 193 ////////////////////////////////////////////// 194 void remote_rwlock_wr_unlock( xptr_t lock_xp ) 195 { 196 reg_t mode; 209 // build useful extended pointers 210 xptr_t busylock_xp = XPTR( lock_cxy , &lock_ptr->lock ); 211 xptr_t count_xp = XPTR( lock_cxy , &lock_ptr->count ); 212 xptr_t rd_root_xp = XPTR( lock_cxy , &lock_ptr->rd_xroot ); 213 xptr_t wr_root_xp = XPTR( lock_cxy , &lock_ptr->wr_xroot ); 214 215 // get busylock 216 remote_busylock_acquire( busylock_xp ); 217 218 #if DEBUG_RWLOCK 219 if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) 220 { 221 thread_t * this = CURRENT_THREAD; 222 uint32_t type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->lock.type ) ); 223 printk("\n[DBG] %s : thread %x (%s) READ RELEASE on rwlock %s [%x,%x] / cycle %d\n", 224 __FUNCTION__, this->trdid, thread_type_str(this->type), 225 lock_type_str[type], lock_cxy, lock_ptr, (uint32_t)hal_get_cycles() ); 226 } 227 #endif 228 229 // decrement number of readers 230 hal_remote_atomic_add( count_xp , -1 ); 231 232 // release first writer in waiting queue if no current readers 233 // and writers waiting queue non empty 234 if( (hal_remote_l32( count_xp ) == 0) && (xlist_is_empty( wr_root_xp ) == false) ) 235 { 236 // get first writer thread 237 xptr_t thread_xp = XLIST_FIRST( wr_root_xp , thread_t, wait_xlist ); 238 cxy_t thread_cxy = GET_CXY( thread_xp ); 239 thread_t * thread_ptr = GET_PTR( thread_xp ); 240 241 // remove this waiting thread from waiting list 242 xlist_unlink( XPTR( thread_cxy , &thread_ptr->wait_xlist ) ); 243 244 // unblock this waiting thread 245 thread_unblock( thread_xp , THREAD_BLOCKED_LOCK ); 246 247 #if DEBUG_RWLOCK 248 if( (uint32_t)hal_get_cycles() > DEBUG_RWLOCK ) 249 { 250 thread_t * this = CURRENT_THREAD; 251 uint32_t lock_type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->lock.type ) ); 252 trdid_t trdid = hal_remote_l32( XPTR( thread_cxy , &thread_ptr->trdid ) ); 253 uint32_t thread_type = hal_remote_lpt( XPTR( thread_cxy , &thread_ptr->type ) ); 254 printk("\n[DBG] %s : thread %x (%s) UNBLOCK thread %x (%s)" 255 " / rwlock %s [%x,%x] / cycle %d\n", 256 __FUNCTION__, this->trdid, thread_type_str(this->type), trdid, thread_type_str(thread_type), 257 lock_type_str[lock_type], lock_cxy, lock_ptr, (uint32_t)hal_get_cycles() ); 258 } 259 #endif 260 261 } 262 263 // release all readers in waiting queue if writers waiting queue empty 264 // and readers waiting queue non empty 265 else if( xlist_is_empty( wr_root_xp ) && (xlist_is_empty( rd_root_xp ) == false) ) 266 { 267 while( xlist_is_empty( rd_root_xp ) == false ) 268 { 269 // get first writer thread 270 xptr_t thread_xp = XLIST_FIRST( wr_root_xp , thread_t, wait_xlist ); 271 cxy_t thread_cxy = GET_CXY( thread_xp ); 272 thread_t * thread_ptr = GET_PTR( thread_xp ); 273 274 // remove this waiting thread from waiting list 275 xlist_unlink( XPTR( thread_cxy , &thread_ptr->wait_xlist ) ); 276 277 // unblock this waiting thread 278 thread_unblock( thread_xp , THREAD_BLOCKED_LOCK ); 279 280 #if DEBUG_RWLOCK 281 if( (uint32_t)hal_get_cycles() > DEBUG_RWLOCK ) 282 { 283 thread_t * this = CURRENT_THREAD; 284 uint32_t lock_type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->lock.type ) ); 285 trdid_t trdid = hal_remote_l32( XPTR( thread_cxy , &thread_ptr->trdid ) ); 286 uint32_t thread_type = hal_remote_lpt( XPTR( thread_cxy , &thread_ptr->type ) ); 287 printk("\n[DBG] %s : thread %x (%s) UNBLOCK thread %x (%s)" 288 " / rwlock %s [%x,%x] / cycle %d\n", 289 __FUNCTION__, this->trdid, thread_type_str(this->type), trdid, thread_type_str(thread_type), 290 lock_type_str[lock_type], lock_cxy, lock_ptr, (uint32_t)hal_get_cycles() ); 291 } 292 #endif 293 294 } 295 } 296 297 // release busylock 298 remote_busylock_release( busylock_xp ); 299 300 } // end remote_rwlock_rd_release() 301 302 /////////////////////////////////////////////// 303 void remote_rwlock_wr_release( xptr_t lock_xp ) 304 { 305 // memory barrier before lock release 306 hal_fence(); 197 307 198 308 // get cluster and local pointer on remote_rwlock … … 200 310 cxy_t lock_cxy = GET_CXY( lock_xp ); 201 311 202 // get cluster and local pointer on local thread 203 thread_t * thread_ptr = CURRENT_THREAD; 204 205 // compute extended pointer on lock->ticket 206 xptr_t current_xp = XPTR( lock_cxy , &lock_ptr->current ); 207 208 // disable interrupts 209 hal_disable_irq( &mode ); 210 211 #if CONFIG_LOCKS_OWNER 212 hal_remote_swd( XPTR( lock_cxy , &lock_ptr->owner ) , XPTR_NULL ); 213 xlist_unlink( XPTR( lock_cxy , &lock_ptr->list ) ); 214 #endif 215 216 // release lock 217 hal_remote_atomic_add( current_xp , 1 ); 218 219 // decrement thread.remote_locks 220 thread_ptr->remote_locks--; 221 222 // enable interrupts 223 hal_restore_irq( mode ); 224 225 // deschedule if pending request 226 thread_check_sched(); 227 228 } // end remote_rwlock_wr_unlock() 229 230 /////////////////////////////////////////// 231 void remote_rwlock_print( xptr_t lock_xp, 232 char * comment ) 233 { 234 uint32_t ticket; // first free ticket index 235 uint32_t current; // ticket index of current owner 236 uint32_t count; // current number of reader threads 237 238 // get cluster and local pointer on remote_rwlock 239 remote_rwlock_t * lock_ptr = GET_PTR( lock_xp ); 240 cxy_t lock_cxy = GET_CXY( lock_xp ); 241 242 ticket = hal_remote_lw ( XPTR( lock_cxy , &lock_ptr->ticket ) ); 243 current = hal_remote_lw ( XPTR( lock_cxy , &lock_ptr->current ) ); 244 count = hal_remote_lw ( XPTR( lock_cxy , &lock_ptr->count ) ); 245 246 printk("\n*** rwlock <%l> %s : ticket = %d / current = %d / count = %d\n", 247 lock_xp , comment , ticket , current , count ); 248 249 } // end remote_rwlock_print() 250 312 // build useful extended pointers 313 xptr_t busylock_xp = XPTR( lock_cxy , &lock_ptr->lock ); 314 xptr_t taken_xp = XPTR( lock_cxy , &lock_ptr->taken ); 315 xptr_t rd_root_xp = XPTR( lock_cxy , &lock_ptr->rd_xroot ); 316 xptr_t wr_root_xp = XPTR( lock_cxy , &lock_ptr->wr_xroot ); 317 318 // get busylock 319 remote_busylock_acquire( busylock_xp ); 320 321 #if DEBUG_RWLOCK 322 if( DEBUG_RWLOCK < (uint32_t)hal_get_cycles() ) 323 { 324 thread_t * this = CURRENT_THREAD; 325 uint32_t type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->lock.type ) ); 326 printk("\n[DBG] %s : thread %x (%s) WRITE RELEASE on rwlock %s [%x,%x] / cycle %d\n", 327 __FUNCTION__, this->trdid, thread_type_str(this->type), 328 lock_type_str[type], lock_cxy, lock_ptr, (uint32_t)hal_get_cycles() ); 329 } 330 #endif 331 332 // release rwlock 333 hal_remote_s32( taken_xp , 0 ); 334 335 // unblock first waiting writer thread if writers waiting queue non empty 336 if( xlist_is_empty( wr_root_xp ) == false ) 337 { 338 // get first writer thread 339 xptr_t thread_xp = XLIST_FIRST( wr_root_xp , thread_t, wait_xlist ); 340 cxy_t thread_cxy = GET_CXY( thread_xp ); 341 thread_t * thread_ptr = GET_PTR( thread_xp ); 342 343 // remove this waiting thread from waiting list 344 xlist_unlink( XPTR( thread_cxy , &thread_ptr->wait_xlist ) ); 345 346 // unblock this waiting thread 347 thread_unblock( thread_xp , THREAD_BLOCKED_LOCK ); 348 349 #if DEBUG_RWLOCK 350 if( (uint32_t)hal_get_cycles() > DEBUG_RWLOCK ) 351 { 352 thread_t * this = CURRENT_THREAD; 353 uint32_t lock_type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->lock.type ) ); 354 trdid_t trdid = hal_remote_l32( XPTR( thread_cxy , &thread_ptr->trdid ) ); 355 uint32_t thread_type = hal_remote_lpt( XPTR( thread_cxy , &thread_ptr->type ) ); 356 printk("\n[DBG] %s : thread %x (%s) UNBLOCK thread %x (%s)" 357 " / rwlock %s [%x,%x] / cycle %d\n", 358 __FUNCTION__, this->trdid, thread_type_str(this->type), trdid, thread_type_str(thread_type), 359 lock_type_str[lock_type], lock_cxy, lock_ptr, (uint32_t)hal_get_cycles() ); 360 } 361 #endif 362 363 } 364 365 // check readers waiting queue and unblock all if writers waiting queue empty 366 else 367 { 368 while( xlist_is_empty( rd_root_xp ) == false ) 369 { 370 // get first writer thread 371 xptr_t thread_xp = XLIST_FIRST( rd_root_xp , thread_t, wait_xlist ); 372 cxy_t thread_cxy = GET_CXY( thread_xp ); 373 thread_t * thread_ptr = GET_PTR( thread_xp ); 374 375 // remove this waiting thread from waiting list 376 xlist_unlink( XPTR( thread_cxy , &thread_ptr->wait_xlist ) ); 377 378 // unblock this waiting thread 379 thread_unblock( thread_xp , THREAD_BLOCKED_LOCK ); 380 381 #if DEBUG_RWLOCK 382 if( (uint32_t)hal_get_cycles() > DEBUG_RWLOCK ) 383 { 384 thread_t * this = CURRENT_THREAD; 385 uint32_t lock_type = hal_remote_l32( XPTR( lock_cxy , &lock_ptr->lock.type ) ); 386 trdid_t trdid = hal_remote_l32( XPTR( thread_cxy , &thread_ptr->trdid ) ); 387 uint32_t thread_type = hal_remote_lpt( XPTR( thread_cxy , &thread_ptr->type ) ); 388 printk("\n[DBG] %s : thread %x (%s) UNBLOCK thread %x (%s)" 389 " / rwlock %s [%x,%x] / cycle %d\n", 390 __FUNCTION__, this->trdid, thread_type_str(this->type), trdid, thread_type_str(thread_type), 391 lock_type_str[lock_type], lock_cxy, lock_ptr, (uint32_t)hal_get_cycles() ); 392 } 393 #endif 394 395 } 396 } 397 398 // release busylock 399 remote_busylock_release( busylock_xp ); 400 401 } // end remote_rwlock_wr_release() 402 403 404
Note: See TracChangeset
for help on using the changeset viewer.