Skip to content

Instantly share code, notes, and snippets.

@nilium
Last active August 29, 2015 14:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nilium/eae2487dee964c2e85d2 to your computer and use it in GitHub Desktop.
Save nilium/eae2487dee964c2e85d2 to your computer and use it in GitHub Desktop.
Temporary storage for a pthread rwlock wrapper class.
/*
Copyright (c) 2014 Noel Raymond Cower.
This file is part of Rusalka VM.
Rusalka VM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Rusalka VM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Rusalka VM. If not, see <http://www.gnu.org/licenses/>.
*/
#include "vm_rwlock.h"
#include <stdexcept>
#include <cerrno>
#include <cstring>
vm_rwlock_t::vm_rwlock_t()
: inited_(false)
, invalid_(false)
{
ensure_init();
}
vm_rwlock_t::~vm_rwlock_t()
{
destroy();
}
vm_rwlock_t::vm_rwlock_t(vm_rwlock_t &&other)
: inited_(other.inited_)
, invalid_(other.invalid_)
, lock_(other.lock_)
{
other.invalid_ = true;
other.inited_ = false;
}
vm_rwlock_t &vm_rwlock_t::operator = (vm_rwlock_t &&other)
{
destroy();
invalid_ = other.invalid_;
inited_ = other.inited_;
lock_ = other.lock_;
other.invalid_ = true;
other.inited_ = false;
return *this;
}
bool vm_rwlock_t::lock_read()
{
validate();
if (inited_) {
int const err = pthread_rwlock_rdlock(&lock_);
if (err != 0) {
throw std::runtime_error(std::strerror(err));
}
return true;
} else {
throw std::runtime_error("Lock is not initialized");
}
}
bool vm_rwlock_t::lock_write()
{
validate();
if (inited_) {
int const err = pthread_rwlock_wrlock(&lock_);
if (err != 0) {
throw std::runtime_error(std::strerror(err));
}
return true;
} else {
throw std::runtime_error("Lock is not initialized");
}
}
bool vm_rwlock_t::unlock() noexcept
{
if (!invalid_ && inited_) {
return pthread_rwlock_unlock(&lock_) == 0;
}
return false;
}
void vm_rwlock_t::ensure_init()
{
validate();
if (!inited_) {
int const err = pthread_rwlock_init(&lock_, NULL);
if (err != 0 && err != EAGAIN) {
throw std::runtime_error(std::strerror(err));
}
inited_ = true;
}
}
void vm_rwlock_t::destroy() noexcept
{
if (!invalid_ && inited_) {
int err = pthread_rwlock_destroy(&lock_);
while (err != 0 && err == EBUSY) {
err = pthread_rwlock_wrlock(&lock_); // ignore EDEADLK
if (err == EINVAL) {
invalid_ = true;
return;
}
err = pthread_rwlock_unlock(&lock_);
if (err == 0) {
err = pthread_rwlock_destroy(&lock_);
}
}
inited_ = false;
}
}
void vm_rwlock_t::validate()
{
if (invalid_) {
throw std::runtime_error("Lock is no longer valid (may have been moved or destroyed)");
}
}
/*
Copyright (c) 2014 Noel Raymond Cower.
This file is part of Rusalka VM.
Rusalka VM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Rusalka VM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Rusalka VM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __VM_RWLOCK_H__
#define __VM_RWLOCK_H__
#include <pthread.h>
class vm_rwlock_t
{
bool inited_;
bool invalid_;
pthread_rwlock_t lock_;
void ensure_init();
void destroy() noexcept;
void validate();
public:
class wr_lockable_t;
class rd_lockable_t;
vm_rwlock_t();
vm_rwlock_t(vm_rwlock_t const &) = delete;
vm_rwlock_t(vm_rwlock_t &&);
~vm_rwlock_t();
vm_rwlock_t &operator = (vm_rwlock_t const &) = delete;
vm_rwlock_t &operator = (vm_rwlock_t &&);
bool lock_read();
bool lock_write();
bool unlock() noexcept;
wr_lockable_t wr_lockable() { return wr_lockable_t(*this); }
rd_lockable_t rd_lockable() { return rd_lockable_t(*this); }
class wr_lockable_t
{
vm_rwlock_t &rwlock_;
public:
wr_lockable_t(vm_rwlock_t &rwlock) : rwlock_(rwlock) { /* nop */ }
bool lock() { return rwlock_.lock_write(); }
bool unlock() noexcept { return rwlock_.unlock(); }
};
class rd_lockable_t
{
vm_rwlock_t &rwlock_;
public:
rd_lockable_t(vm_rwlock_t &rwlock) : rwlock_(rwlock) { /* nop */ }
bool lock() { return rwlock_.lock_read(); }
bool unlock() noexcept { return rwlock_.unlock(); }
};
};
#endif /* end __VM_RWLOCK_H__ include guard */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment