|  | // Copyright 2021 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "file-lock.h" | 
|  |  | 
|  | namespace file_lock { | 
|  |  | 
|  | typedef std::pair<zx_koid_t, lock_completer_t> lock_pending_t; | 
|  |  | 
|  | FileLock::~FileLock() { | 
|  | running_ = false; | 
|  | const std::lock_guard<std::mutex> lock(lock_mtx_); | 
|  | for (auto& client : pending_shared_) { | 
|  | client.second(ZX_ERR_CANCELED); | 
|  | } | 
|  | for (auto& client : pending_shared_) { | 
|  | client.second(ZX_ERR_CANCELED); | 
|  | } | 
|  | } | 
|  |  | 
|  | void FileLock::Lock(zx_koid_t owner, LockRequest& req, lock_completer_t& completer) { | 
|  | const std::lock_guard<std::mutex> lock(lock_mtx_); | 
|  |  | 
|  | if (!running_ || LockInProgress(owner)) { | 
|  | completer(ZX_ERR_BAD_STATE); | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (exclusive_ != ZX_KOID_INVALID) { | 
|  | if (owner == exclusive_) { | 
|  | switch (req.type()) { | 
|  | case LockType::READ: { | 
|  | // downgrading from exclusive to shared | 
|  | // unblock all the shared clients | 
|  | exclusive_ = ZX_KOID_INVALID; | 
|  | shared_.emplace(owner); | 
|  | std::map ps(std::move(pending_shared_)); | 
|  | for (auto& owner_info : ps) { | 
|  | shared_.emplace(owner_info.first); | 
|  | } | 
|  | for (auto& owner_info : ps) { | 
|  | owner_info.second(ZX_OK); | 
|  | } | 
|  | completer(ZX_OK); | 
|  | break; | 
|  | } | 
|  | case LockType::WRITE: { | 
|  | // already have this - noop | 
|  | completer(ZX_OK); | 
|  | break; | 
|  | } | 
|  | case LockType::UNLOCK: { | 
|  | exclusive_ = ZX_KOID_INVALID; | 
|  | // this appears to be the logic of flock | 
|  | if (pending_shared_.size() > pending_exclusive_.size()) { | 
|  | std::map ps(std::move(pending_shared_)); | 
|  | for (auto& owner_info : ps) { | 
|  | shared_.emplace(owner_info.first); | 
|  | } | 
|  | for (auto& owner_info : ps) { | 
|  | owner_info.second(ZX_OK); | 
|  | } | 
|  | } else { | 
|  | auto first_exclusive = pending_exclusive_.begin(); | 
|  | if (first_exclusive != pending_exclusive_.end()) { | 
|  | exclusive_ = first_exclusive->first; | 
|  | auto exclusive_completer(std::move(first_exclusive->second)); | 
|  | pending_exclusive_.erase(first_exclusive); | 
|  | exclusive_completer(ZX_OK); | 
|  | } | 
|  | } | 
|  | completer(ZX_OK); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } else { | 
|  | switch (req.type()) { | 
|  | case LockType::READ: { | 
|  | if (req.wait()) { | 
|  | pending_shared_.emplace(std::make_pair(owner, std::move(completer))); | 
|  | } else { | 
|  | completer(ZX_ERR_SHOULD_WAIT); | 
|  | } | 
|  | break; | 
|  | } | 
|  | case LockType::WRITE: { | 
|  | if (req.wait()) { | 
|  | pending_exclusive_.emplace(std::make_pair(owner, std::move(completer))); | 
|  | } else { | 
|  | completer(ZX_ERR_SHOULD_WAIT); | 
|  | } | 
|  | break; | 
|  | } | 
|  | case LockType::UNLOCK: { | 
|  | // did not have a lock - noop | 
|  | completer(ZX_OK); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } else { | 
|  | switch (req.type()) { | 
|  | case LockType::READ: { | 
|  | shared_.emplace(owner); | 
|  | completer(ZX_OK); | 
|  | break; | 
|  | } | 
|  | case LockType::WRITE: { | 
|  | shared_.erase(owner); | 
|  | if (shared_.empty()) { | 
|  | exclusive_ = owner; | 
|  | completer(ZX_OK); | 
|  | } else { | 
|  | if (req.wait()) { | 
|  | pending_exclusive_.emplace(std::make_pair(owner, std::move(completer))); | 
|  | } else { | 
|  | // if we had a lock, we lost it | 
|  | completer(ZX_ERR_SHOULD_WAIT); | 
|  | } | 
|  | } | 
|  | break; | 
|  | } | 
|  | case LockType::UNLOCK: { | 
|  | auto extracted = shared_.extract(owner); | 
|  | if (extracted.empty()) { | 
|  | // did not have a lock - noop | 
|  | completer(ZX_OK); | 
|  | } else { | 
|  | auto first_exclusive = pending_exclusive_.begin(); | 
|  | if (first_exclusive != pending_exclusive_.end() && shared_.empty()) { | 
|  | exclusive_ = first_exclusive->first; | 
|  | auto exclusive_completer(std::move(first_exclusive->second)); | 
|  | pending_exclusive_.erase(first_exclusive); | 
|  | exclusive_completer(ZX_OK); | 
|  | } | 
|  | completer(ZX_OK); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | bool FileLock::Forget(zx_koid_t owner) { | 
|  | LockRequest req(LockType::UNLOCK, false); | 
|  |  | 
|  | bool forgotten = false; | 
|  | lock_completer_t completer([&forgotten](zx_status_t status) { | 
|  | if (status == ZX_OK) { | 
|  | forgotten = true; | 
|  | } | 
|  | }); | 
|  |  | 
|  | Lock(owner, req, completer); | 
|  |  | 
|  | return forgotten; | 
|  | } | 
|  |  | 
|  | bool FileLock::NoLocksHeld() { | 
|  | return exclusive_ == ZX_KOID_INVALID && shared_.empty() && pending_exclusive_.empty() && | 
|  | pending_shared_.empty(); | 
|  | } | 
|  |  | 
|  | }  // namespace file_lock |