blob: cd348f770a508147bc9cb00a3df65bab15205ea4 [file] [log] [blame]
// 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