inline void remove_if_can_lock_file(const char *file_path) { file_handle_t fhnd = open_existing_file(file_path, read_write); if(fhnd != invalid_file()){ bool acquired; if(try_acquire_file_lock(fhnd, acquired) && acquired){ delete_file(file_path); } close_file(fhnd); } }
robust_mutex_lock_file() { permissions p; p.set_unrestricted(); //Remove old lock files of other processes remove_old_robust_lock_files(); //Create path and obtain lock file path for this process create_and_get_robust_lock_file_path(fname, get_current_process_id()); //Now try to open or create the lock file fd = create_or_open_file(fname.c_str(), read_write, p); //If we can't open or create it, then something unrecoverable has happened if(fd == invalid_file()){ throw interprocess_exception(other_error, "Robust emulation robust_mutex_lock_file constructor failed: could not open or create file"); } //Now we must take in care a race condition with another process //calling "remove_old_robust_lock_files()". No other threads from this //process will be creating the lock file because intermodule_singleton //guarantees this. So let's loop acquiring the lock and checking if we //can't exclusively create the file (if the file is erased by another process //then this exclusive open would fail). If the file can't be exclusively created //then we have correctly open/create and lock the file. If the file can //be exclusively created, then close previous locked file and try again. while(1){ bool acquired; if(!try_acquire_file_lock(fd, acquired) || !acquired ){ throw interprocess_exception(other_error, "Robust emulation robust_mutex_lock_file constructor failed: try_acquire_file_lock"); } //Creating exclusively must fail with already_exists_error //to make sure we've locked the file and no one has //deleted it between creation and locking file_handle_t fd2 = create_new_file(fname.c_str(), read_write, p); if(fd2 != invalid_file()){ close_file(fd); fd = fd2; continue; } //If exclusive creation fails with expected error go ahead else if(error_info(system_error_code()).get_error_code() == already_exists_error){ //must already exist //Leak descriptor to mantain the file locked until the process dies break; } //If exclusive creation fails with unexpected error throw an unrecoverable error else{ close_file(fd); throw interprocess_exception(other_error, "Robust emulation robust_mutex_lock_file constructor failed: create_file filed with unexpected error"); } } }
inline bool robust_spin_mutex<Mutex>::is_owner_dead(boost::uint32_t own) { //If owner is an invalid id, then it's clear it's dead if(own == (boost::uint32_t)get_invalid_process_id()){ return true; } //Obtain the lock filename of the owner field std::string file; this->owner_to_filename(own, file); //Now the logic is to open and lock it file_handle_t fhnd = open_existing_file(file.c_str(), read_write); if(fhnd != invalid_file()){ //If we can open the file, lock it. bool acquired; if(try_acquire_file_lock(fhnd, acquired) && acquired){ //If locked, just delete the file delete_file(file.c_str()); close_file(fhnd); return true; } //If not locked, the owner is suppossed to be still alive close_file(fhnd); } else{ //If the lock file does not exist then the owner is dead (a previous cleanup) //function has deleted the file. If there is another reason, then this is //an unrecoverable error if(error_info(system_error_code()).get_error_code() == not_found_error){ return true; } } return false; }
/* * osprd_ioctl(inode, filp, cmd, arg) * Called to perform an ioctl on the named file. */ int osprd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { osprd_info_t *d = file2osprd(filp); // device info int r = 0; // return value: initially 0 // Set 'r' to the ioctl's return value: 0 on success, negative on error if (cmd == OSPRDIOCACQUIRE) { // EXERCISE: Lock the ramdisk. // // If *filp is open for writing (filp_writable), then attempt // to write-lock the ramdisk; otherwise attempt to read-lock // the ramdisk. // // This lock request must block using 'd->blockq' until: // 1) no other process holds a write lock; // 2) either the request is for a read lock, or no other process // holds a read lock; and // 3) lock requests should be serviced in order, so no process // that blocked earlier is still blocked waiting for the // lock. // // If a process acquires a lock, mark this fact by setting // 'filp->f_flags |= F_OSPRD_LOCKED'. You also need to // keep track of how many read and write locks are held: // change the 'osprd_info_t' structure to do this. // // Also wake up processes waiting on 'd->blockq' as needed. // // If the lock request would cause a deadlock, return -EDEADLK. // If the lock request blocks and is awoken by a signal, then // return -ERESTARTSYS. // Otherwise, if we can grant the lock request, return 0. // 'd->ticket_head' and 'd->ticket_tail' should help you // service lock requests in order. These implement a ticket // order: 'ticket_tail' is the next ticket, and 'ticket_head' // is the ticket currently being served. You should set a local // variable to 'd->ticket_head' and increment 'd->ticket_head'. // Then, block at least until 'd->ticket_tail == local_ticket'. // (Some of these operations are in a critical section and must // be protected by a spinlock; which ones?) // Used to track the current request unsigned local_ticket; if(check_deadlock(d)) return -EDEADLK; // If we can't acquire the lock we put ourselves in the back of the queue // when the lock is released ticket_head will be incremented and we'll be // woken up while(try_acquire_file_lock(filp) != 0) { spin_lock(&d->mutex); d->ticket_tail++; local_ticket = d->ticket_tail; d->lock_waiter_l = list_add_to_front(d->lock_waiter_l, current->pid); spin_unlock(&d->mutex); wait_event_interruptible(d->blockq, d->ticket_head == local_ticket || d->num_to_requeue > 0); spin_lock(&d->mutex); d->lock_waiter_l = list_remove_element(d->lock_waiter_l, current->pid); spin_unlock(&d->mutex); // process any pending signals by re-queueing everything if(d->num_to_requeue > 0) { // Note: do NOT wake threads here, each thread should requeue only ONCE spin_lock(&d->mutex); d->num_to_requeue--; spin_unlock(&d->mutex); // If we find another pending signal, dispatch that too if(signal_pending(current)) return -ERESTARTSYS; } else if(signal_pending(current)) // See if we were woken up by a signal { // For simplicity, we requeue all waiting tasks (-1 which // is the process) being "popped" off the wait queue spin_lock(&d->mutex); d->num_to_requeue = (d->ticket_tail - d->ticket_head - 1); d->ticket_head = 0; d->ticket_tail = 0; // All threads are woken to notify them of requeuing // meanwhile, we wait until that finishes (no need to check // for more interrupts, the process will exit anyway). wake_up_all(&d->blockq); spin_unlock(&d->mutex); // Sanity check if(d->num_to_requeue > 0) { wait_event(d->blockq, d->num_to_requeue == 0); wake_up_all(&d->blockq); // Wake everyone up again to check for other pending signals } return -ERESTARTSYS; } } r = 0; } else if (cmd == OSPRDIOCTRYACQUIRE) { // EXERCISE: ATTEMPT to lock the ramdisk. // // This is just like OSPRDIOCACQUIRE, except it should never // block. If OSPRDIOCACQUIRE would block or return deadlock, // OSPRDIOCTRYACQUIRE should return -EBUSY. // Otherwise, if we can grant the lock request, return 0. r = try_acquire_file_lock(filp); if(r == -EDEADLK) r = -EBUSY; } else if (cmd == OSPRDIOCRELEASE) { // EXERCISE: Unlock the ramdisk. // // If the file hasn't locked the ramdisk, return -EINVAL. // Otherwise, clear the lock from filp->f_flags, wake up // the wait queue, perform any additional accounting steps // you need, and return 0. r = release_file_lock(filp); } else r = -ENOTTY; /* unknown command */ return r; }