1 /* ccapi/server/ccs_lock_state.c */ 2 /* 3 * Copyright 2006, 2007 Massachusetts Institute of Technology. 4 * All Rights Reserved. 5 * 6 * Export of this software from the United States of America may 7 * require a specific license from the United States Government. 8 * It is the responsibility of any person or organization contemplating 9 * export to obtain such a license before exporting. 10 * 11 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 12 * distribute this software and its documentation for any purpose and 13 * without fee is hereby granted, provided that the above copyright 14 * notice appear in all copies and that both that copyright notice and 15 * this permission notice appear in supporting documentation, and that 16 * the name of M.I.T. not be used in advertising or publicity pertaining 17 * to distribution of the software without specific, written prior 18 * permission. Furthermore if you modify this software you must label 19 * your software as modified software and not distribute it in such a 20 * fashion that it might be confused with the original M.I.T. software. 21 * M.I.T. makes no representations about the suitability of 22 * this software for any purpose. It is provided "as is" without express 23 * or implied warranty. 24 */ 25 26 #include "ccs_common.h" 27 28 struct ccs_lock_state_d { 29 cc_int32 invalid_object_err; 30 cc_int32 pending_lock_err; 31 cc_int32 no_lock_err; 32 ccs_lock_array_t locks; 33 cc_uint64 first_pending_lock_index; 34 }; 35 36 struct ccs_lock_state_d ccs_lock_state_initializer = { 1, 1, 1, NULL, 0 }; 37 38 /* ------------------------------------------------------------------------ */ 39 40 cc_int32 ccs_lock_state_new (ccs_lock_state_t *out_lock_state, 41 cc_int32 in_invalid_object_err, 42 cc_int32 in_pending_lock_err, 43 cc_int32 in_no_lock_err) 44 { 45 cc_int32 err = ccNoError; 46 ccs_lock_state_t lock_state = NULL; 47 48 if (!out_lock_state) { err = cci_check_error (ccErrBadParam); } 49 50 if (!err) { 51 lock_state = malloc (sizeof (*lock_state)); 52 if (lock_state) { 53 *lock_state = ccs_lock_state_initializer; 54 } else { 55 err = cci_check_error (ccErrNoMem); 56 } 57 } 58 59 if (!err) { 60 err = ccs_lock_array_new (&lock_state->locks); 61 } 62 63 if (!err) { 64 lock_state->invalid_object_err = in_invalid_object_err; 65 lock_state->pending_lock_err = in_pending_lock_err; 66 lock_state->no_lock_err = in_no_lock_err; 67 68 *out_lock_state = lock_state; 69 lock_state = NULL; 70 } 71 72 ccs_lock_state_release (lock_state); 73 74 return cci_check_error (err); 75 } 76 77 /* ------------------------------------------------------------------------ */ 78 79 cc_int32 ccs_lock_state_release (ccs_lock_state_t io_lock_state) 80 { 81 cc_int32 err = ccNoError; 82 83 if (!err && io_lock_state) { 84 ccs_lock_array_release (io_lock_state->locks); 85 free (io_lock_state); 86 } 87 88 return cci_check_error (err); 89 } 90 91 #ifdef TARGET_OS_MAC 92 #pragma mark - 93 #endif 94 95 /* ------------------------------------------------------------------------ */ 96 97 static cc_int32 ccs_lock_status_add_pending_lock (ccs_lock_state_t io_lock_state, 98 ccs_pipe_t in_client_pipe, 99 ccs_pipe_t in_reply_pipe, 100 cc_uint32 in_lock_type, 101 cc_uint64 *out_lock_index) 102 { 103 cc_int32 err = ccNoError; 104 ccs_lock_t lock = NULL; 105 106 if (!io_lock_state ) { err = cci_check_error (ccErrBadParam); } 107 if (!ccs_pipe_valid (in_client_pipe)) { err = cci_check_error (ccErrBadParam); } 108 if (!ccs_pipe_valid (in_reply_pipe) ) { err = cci_check_error (ccErrBadParam); } 109 110 if (!err) { 111 err = ccs_lock_new (&lock, in_lock_type, 112 io_lock_state->invalid_object_err, 113 in_client_pipe, in_reply_pipe, 114 io_lock_state); 115 } 116 117 if (!err) { 118 err = ccs_lock_array_insert (io_lock_state->locks, lock, 119 ccs_lock_array_count (io_lock_state->locks)); 120 if (!err) { lock = NULL; /* take ownership */ } 121 } 122 123 if (!err) { 124 *out_lock_index = ccs_lock_array_count (io_lock_state->locks) - 1; 125 } 126 127 ccs_lock_release (lock); 128 129 return cci_check_error (err); 130 } 131 132 /* ------------------------------------------------------------------------ */ 133 134 static cc_int32 ccs_lock_status_remove_lock (ccs_lock_state_t io_lock_state, 135 cc_uint64 in_lock_index) 136 { 137 cc_int32 err = ccNoError; 138 139 if (!io_lock_state) { err = cci_check_error (ccErrBadParam); } 140 141 if (!err) { 142 err = ccs_lock_array_remove (io_lock_state->locks, in_lock_index); 143 144 if (!err && in_lock_index < io_lock_state->first_pending_lock_index) { 145 io_lock_state->first_pending_lock_index--; 146 } 147 } 148 149 return cci_check_error (err); 150 } 151 152 /* ------------------------------------------------------------------------ */ 153 154 static cc_int32 ccs_lock_status_grant_lock (ccs_lock_state_t io_lock_state, 155 cc_uint64 in_pending_lock_index) 156 { 157 cc_int32 err = ccNoError; 158 ccs_lock_t pending_lock = NULL; 159 cc_uint32 type = 0; 160 161 if (!io_lock_state) { err = cci_check_error (ccErrBadParam); } 162 163 if (!err) { 164 pending_lock = ccs_lock_array_object_at_index (io_lock_state->locks, 165 in_pending_lock_index); 166 if (!pending_lock || in_pending_lock_index < io_lock_state->first_pending_lock_index) { 167 err = cci_check_error (ccErrBadParam); 168 } 169 } 170 171 if (!err) { 172 err = ccs_lock_type (pending_lock, &type); 173 } 174 175 if (!err && (type == cc_lock_upgrade || type == cc_lock_downgrade)) { 176 /* lock upgrades or downgrades. Find the old lock and remove it. */ 177 ccs_pipe_t pending_client_pipe = CCS_PIPE_NULL; 178 179 err = ccs_lock_client_pipe (pending_lock, &pending_client_pipe); 180 181 if (!err) { 182 cc_uint64 i; 183 184 for (i = 0; !err && i < io_lock_state->first_pending_lock_index; i++) { 185 ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i); 186 cc_uint32 is_lock_for_client = 0; 187 188 err = ccs_lock_is_for_client_pipe (lock, pending_client_pipe, &is_lock_for_client); 189 190 if (!err && is_lock_for_client) { 191 cci_debug_printf ("%s: Removing old lock %p at index %d to replace with pending lock %p.", 192 __FUNCTION__, lock, (int) i, pending_lock); 193 err = ccs_lock_status_remove_lock (io_lock_state, i); 194 if (!err) { i--; in_pending_lock_index--; /* We removed one so back up an index */ } 195 break; 196 } 197 } 198 } 199 } 200 201 if (!err) { 202 cc_uint64 new_lock_index = 0; 203 204 err = ccs_lock_array_move (io_lock_state->locks, 205 in_pending_lock_index, 206 io_lock_state->first_pending_lock_index, 207 &new_lock_index); 208 if (!err) { io_lock_state->first_pending_lock_index++; } 209 } 210 211 if (!err) { 212 err = ccs_lock_grant_lock (pending_lock); 213 } 214 215 return cci_check_error (err); 216 } 217 218 #ifdef TARGET_OS_MAC 219 #pragma mark - 220 #endif 221 222 /* ------------------------------------------------------------------------ */ 223 224 static cc_int32 ccs_lock_state_check_pending_lock (ccs_lock_state_t io_lock_state, 225 ccs_pipe_t in_pending_lock_client_pipe, 226 cc_uint32 in_pending_lock_type, 227 cc_uint32 *out_grant_lock) 228 { 229 cc_int32 err = ccNoError; 230 cc_uint32 is_write_locked = 0; 231 cc_uint32 client_has_lock = 0; 232 cc_uint32 other_clients_have_locks = 0; 233 cc_uint32 client_lock_type = 0; 234 cc_uint64 client_lock_index = 0; 235 cc_uint32 grant_lock = 0; 236 237 if (!io_lock_state ) { err = cci_check_error (ccErrBadParam); } 238 if (!ccs_pipe_valid (in_pending_lock_client_pipe)) { err = cci_check_error (ccErrBadParam); } 239 if (!out_grant_lock ) { err = cci_check_error (ccErrBadParam); } 240 241 if (!err) { 242 cc_uint64 i; 243 cc_uint64 lock_count = io_lock_state->first_pending_lock_index; 244 245 for (i = 0; !err && i < lock_count; i++) { 246 ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i); 247 cc_uint32 lock_type = 0; 248 cc_uint32 lock_is_for_client = 0; 249 250 err = ccs_lock_type (lock, &lock_type); 251 252 if (!err) { 253 err = ccs_lock_is_for_client_pipe (lock, in_pending_lock_client_pipe, 254 &lock_is_for_client); 255 } 256 257 if (!err) { 258 if (lock_type == cc_lock_write || lock_type == cc_lock_upgrade) { 259 is_write_locked = 1; 260 } 261 262 if (!lock_is_for_client) { 263 other_clients_have_locks = 1; 264 265 } else if (!client_has_lock) { /* only record type of 1st lock */ 266 client_has_lock = 1; 267 client_lock_type = lock_type; 268 client_lock_index = i; 269 } 270 } 271 } 272 } 273 274 if (!err) { 275 cc_uint64 lock_count = io_lock_state->first_pending_lock_index; 276 277 if (in_pending_lock_type == cc_lock_write) { 278 if (client_has_lock) { 279 err = cci_check_error (ccErrBadLockType); 280 } else { 281 grant_lock = (lock_count == 0); 282 } 283 284 } else if (in_pending_lock_type == cc_lock_read) { 285 if (client_has_lock) { 286 err = cci_check_error (ccErrBadLockType); 287 } else { 288 grant_lock = !is_write_locked; 289 } 290 291 } else if (in_pending_lock_type == cc_lock_upgrade) { 292 if (!client_has_lock || (client_lock_type != cc_lock_read && 293 client_lock_type != cc_lock_downgrade)) { 294 err = cci_check_error (ccErrBadLockType); 295 } else { 296 /* don't grant if other clients have read locks */ 297 grant_lock = !other_clients_have_locks; 298 } 299 300 } else if (in_pending_lock_type == cc_lock_downgrade) { 301 if (!client_has_lock || (client_lock_type != cc_lock_write && 302 client_lock_type != cc_lock_upgrade)) { 303 err = cci_check_error (ccErrBadLockType); 304 } else { 305 /* downgrades can never block */ 306 grant_lock = 1; 307 } 308 } else { 309 err = cci_check_error (ccErrBadLockType); 310 } 311 } 312 313 if (!err) { 314 *out_grant_lock = grant_lock; 315 } 316 317 return cci_check_error (err); 318 } 319 320 /* ------------------------------------------------------------------------ */ 321 322 static cc_int32 ccs_lock_status_try_to_grant_pending_locks (ccs_lock_state_t io_lock_state) 323 { 324 cc_int32 err = ccNoError; 325 cc_uint32 done = 0; 326 327 if (!io_lock_state) { err = cci_check_error (ccErrBadParam); } 328 329 /* Look at the pending locks and see if we can grant them. 330 * Note that downgrade locks mean we must check all pending locks each pass 331 * since a downgrade lock might be last in the list. */ 332 333 while (!err && !done) { 334 cc_uint64 i; 335 cc_uint64 count = ccs_lock_array_count (io_lock_state->locks); 336 cc_uint32 granted_lock = 0; 337 338 for (i = io_lock_state->first_pending_lock_index; !err && i < count; i++) { 339 ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i); 340 cc_uint32 lock_type = 0; 341 ccs_pipe_t client_pipe = CCS_PIPE_NULL; 342 cc_uint32 can_grant_lock_now = 0; 343 344 err = ccs_lock_client_pipe (lock, &client_pipe); 345 346 if (!err) { 347 err = ccs_lock_type (lock, &lock_type); 348 } 349 350 if (!err) { 351 err = ccs_lock_state_check_pending_lock (io_lock_state, client_pipe, 352 lock_type, &can_grant_lock_now); 353 } 354 355 if (!err && can_grant_lock_now) { 356 err = ccs_lock_status_grant_lock (io_lock_state, i); 357 if (!err) { granted_lock = 1; } 358 } 359 } 360 361 if (!err && !granted_lock) { 362 /* we walked over all the locks and couldn't grant any of them */ 363 done = 1; 364 } 365 } 366 367 return cci_check_error (err); 368 } 369 370 #ifdef TARGET_OS_MAC 371 #pragma mark - 372 #endif 373 374 /* ------------------------------------------------------------------------ */ 375 376 cc_int32 ccs_lock_state_add (ccs_lock_state_t io_lock_state, 377 ccs_pipe_t in_client_pipe, 378 ccs_pipe_t in_reply_pipe, 379 cc_uint32 in_lock_type, 380 cc_uint32 in_block, 381 cc_uint32 *out_will_send_reply) 382 { 383 cc_int32 err = ccNoError; 384 cc_uint32 can_grant_lock_now = 0; 385 386 if (!io_lock_state ) { err = cci_check_error (ccErrBadParam); } 387 if (!ccs_pipe_valid (in_client_pipe)) { err = cci_check_error (ccErrBadParam); } 388 if (!ccs_pipe_valid (in_reply_pipe) ) { err = cci_check_error (ccErrBadParam); } 389 if (!out_will_send_reply ) { err = cci_check_error (ccErrBadParam); } 390 391 if (!err) { 392 /* Sanity check: if there are any pending locks for this client 393 * the client must have timed out waiting for our reply. Remove any 394 * existing pending locks for the client. */ 395 cc_uint64 i; 396 397 for (i = io_lock_state->first_pending_lock_index; !err && i < ccs_lock_array_count (io_lock_state->locks); i++) { 398 ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i); 399 cc_uint32 has_pending_lock_for_client = 0; 400 401 err = ccs_lock_is_for_client_pipe (lock, in_client_pipe, &has_pending_lock_for_client); 402 403 if (!err && has_pending_lock_for_client) { 404 cci_debug_printf ("WARNING %s: Removing unexpected pending lock %p at index %d.", 405 __FUNCTION__, lock, (int) i); 406 err = ccs_lock_status_remove_lock (io_lock_state, i); 407 if (!err) { i--; /* We removed one so back up an index */ } 408 } 409 } 410 } 411 412 if (!err) { 413 err = ccs_lock_state_check_pending_lock (io_lock_state, in_client_pipe, 414 in_lock_type, &can_grant_lock_now); 415 } 416 417 if (!err) { 418 if (!can_grant_lock_now && (in_block == cc_lock_noblock)) { 419 err = cci_check_error (io_lock_state->pending_lock_err); 420 421 } else { 422 cc_uint64 new_lock_index = 0; 423 424 err = ccs_lock_status_add_pending_lock (io_lock_state, 425 in_client_pipe, 426 in_reply_pipe, 427 in_lock_type, 428 &new_lock_index); 429 430 if (!err && can_grant_lock_now) { 431 err = ccs_lock_status_grant_lock (io_lock_state, new_lock_index); 432 433 if (!err && (in_lock_type == cc_lock_downgrade)) { 434 /* downgrades can allow us to grant other locks */ 435 err = ccs_lock_status_try_to_grant_pending_locks (io_lock_state); 436 } 437 } 438 } 439 } 440 441 if (!err) { 442 /* ccs_lock_state_add sends its replies via callback so caller shouldn't */ 443 *out_will_send_reply = 1; 444 } 445 446 return cci_check_error (err); 447 } 448 449 /* ------------------------------------------------------------------------ */ 450 451 cc_int32 ccs_lock_state_remove (ccs_lock_state_t io_lock_state, 452 ccs_pipe_t in_client_pipe) 453 { 454 cc_int32 err = ccNoError; 455 cc_uint32 found_lock = 0; 456 457 if (!io_lock_state ) { err = cci_check_error (ccErrBadParam); } 458 if (!ccs_pipe_valid (in_client_pipe)) { err = cci_check_error (ccErrBadParam); } 459 460 if (!err) { 461 cc_uint64 i; 462 463 /* Remove all locks for this client. 464 * There should only be one so warn if there are multiple */ 465 for (i = 0; !err && i < io_lock_state->first_pending_lock_index; i++) { 466 ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i); 467 cc_uint32 is_for_client = 0; 468 469 err = ccs_lock_is_for_client_pipe (lock, in_client_pipe, &is_for_client); 470 471 if (!err && is_for_client) { 472 if (found_lock) { 473 cci_debug_printf ("WARNING %s: Found multiple locks for client.", 474 __FUNCTION__); 475 } 476 477 found_lock = 1; 478 479 cci_debug_printf ("%s: Removing lock %p at index %d.", __FUNCTION__, lock, (int) i); 480 err = ccs_lock_status_remove_lock (io_lock_state, i); 481 if (!err) { i--; /* We removed one so back up an index */ } 482 } 483 } 484 } 485 486 if (!err && !found_lock) { 487 err = cci_check_error (io_lock_state->no_lock_err); 488 } 489 490 if (!err) { 491 err = ccs_lock_status_try_to_grant_pending_locks (io_lock_state); 492 } 493 494 return cci_check_error (err); 495 } 496 497 /* ------------------------------------------------------------------------ */ 498 499 cc_int32 ccs_lock_state_invalidate_lock (ccs_lock_state_t io_lock_state, 500 ccs_lock_t in_lock) 501 { 502 cc_int32 err = ccNoError; 503 504 if (!io_lock_state) { err = ccErrBadParam; } 505 506 if (!err) { 507 cc_uint64 i; 508 cc_uint64 count = ccs_lock_array_count (io_lock_state->locks); 509 510 for (i = 0; !err && i < count; i++) { 511 ccs_lock_t lock = ccs_lock_array_object_at_index (io_lock_state->locks, i); 512 513 if (lock == in_lock) { 514 err = ccs_lock_status_remove_lock (io_lock_state, i); 515 516 if (!err) { 517 err = ccs_lock_status_try_to_grant_pending_locks (io_lock_state); 518 break; 519 } 520 } 521 } 522 } 523 524 return cci_check_error (err); 525 } 526