Files
mlpack/fastlib/thor/blockdev.h
T

346 lines
8.0 KiB
C++

#ifndef THOR_BLOCKDEV_H
#define THOR_BLOCKDEV_H
#include "base/common.h"
#include "base/otrav.h"
#include "col/intmap.h"
#include "col/string.h"
#include "fx/fx.h"
#include "par/thread.h"
class BlockDevice {
FORBID_COPY(BlockDevice);
public:
typedef int32 blockid_t;
typedef int32 offset_t;
enum modeflag_t {
F_READ = 0x01,
F_WRITABLE = 0x02,
F_WRITE = 0x04|F_WRITABLE,
F_DYNAMIC = 0x08,
F_INIT = 0x10,
};
enum mode_t {
/** Read existing data. */
M_READ = F_READ,
/** Overwrite existing data, as if the original data didn't exist. */
M_OVERWRITE = F_WRITE,
/** Modify existing data. */
M_MODIFY = F_READ|F_WRITE,
/** Create from scratch. */
M_CREATE = F_INIT|F_WRITE|F_DYNAMIC,
/** Add new data to the end. */
M_APPEND = F_WRITE|F_DYNAMIC,
/** Use the block device only as temporary storage. */
M_TEMP = F_INIT|F_WRITABLE|F_DYNAMIC,
/** Use prexisting data, but allow data to be used as scratch space. */
M_DESTROY = F_READ|F_WRITABLE,
};
/**
* Checks if a mode is creating a brand new "file," and header information
* must be written.
*
* @param mode the mode to check
*/
static bool need_init(mode_t mode) {
return (mode & F_INIT) != 0;
}
/**
* Checks if a mode requires reads.
*
* If a mode does not require reads, block devices may return garbage, and
* caches should initialize the block to its default values.
*
* @param mode the mode to check
*/
static bool need_read(mode_t mode) {
return (mode & F_READ) != 0;
}
/**
* Check if a mode requires writes.
*
* In modes that require writes, data must be saved at the end of lifetime.
* Modes may allow writes but not require them (see below).
*
* @param mode the mode to check
*/
static bool need_write(mode_t mode) {
return (mode & F_WRITE) != 0;
}
/**
* Checks if a mode allows writes.
*
* In modes that allow writes and allows reads, data that is written must
* be preserved if it is flushed from cache and read back again.
* However it need not be stored after the cache is closed unless need_write
* is true.
*
* @param mode the mode to check
*/
static bool can_write(mode_t mode) {
return (mode & F_WRITABLE) != 0;
}
/**
* Checks if a mode requires read-after-write to be true.
*
* Effectively checks can_write && need_read.
*
* @param mode the mode to check
*/
static bool need_writeread(mode_t mode) {
return (mode & (F_READ|F_WRITABLE)) == (F_READ|F_WRITABLE);
}
/**
* Checks if a mode is a dynamic mode.
*
* In dnyamic modes (create, append, temp), resizing is possible, and
* writes are always performed on the entire-block granularity. In static
* modes, writes are confined to contiguous regions on a sub-block
* granularity.
*
* @param mode the mode to check
*/
static bool is_dynamic(mode_t mode) {
return (mode & F_DYNAMIC) != 0;
}
protected:
blockid_t n_blocks_;
offset_t n_block_bytes_;
private:
Mutex mutex_;
public:
BlockDevice() {}
virtual ~BlockDevice() {}
blockid_t n_blocks() const {
return n_blocks_;
}
offset_t n_block_bytes() const {
return n_block_bytes_;
}
uint64 n_total_bytes() const {
return uint64(n_blocks_) * n_block_bytes_;
}
void Read(blockid_t blockid, char *data) {
Read(blockid, 0, n_block_bytes_, data);
}
void Write(blockid_t blockid, const char *data) {
Write(blockid, 0, n_block_bytes_, data);
}
virtual void Read(blockid_t blockid,
offset_t begin, offset_t end, char *data) = 0;
virtual void Write(blockid_t blockid,
offset_t begin, offset_t end, const char *data) = 0;
virtual blockid_t AllocBlocks(blockid_t diff);
public:
void Lock() { mutex_.Lock(); }
void Unlock() { mutex_.Unlock(); }
protected:
virtual void HandleGrowth() {}
};
class NullBlockDevice : public BlockDevice {
FORBID_COPY(NullBlockDevice);
public:
NullBlockDevice() {}
~NullBlockDevice() {}
void Init(blockid_t n_blocks_in, offset_t n_block_bytes_in) {
n_blocks_ = n_blocks_in;
n_block_bytes_ = n_block_bytes_in;
}
virtual void Read(blockid_t blockid,
offset_t begin, offset_t end, char *data) {
abort();
}
virtual void Write(blockid_t blockid,
offset_t begin, offset_t end, const char *data) {
// ignore the data
}
};
class BlockDeviceWrapper : public BlockDevice {
FORBID_COPY(BlockDeviceWrapper);
protected:
BlockDevice *inner_;
public:
BlockDeviceWrapper() {}
virtual ~BlockDeviceWrapper() {}
void Init(BlockDevice *inner_in) {
inner_ = inner_in;
n_blocks_ = inner_->n_blocks();
n_block_bytes_ = inner_->n_block_bytes();
}
virtual void Read(blockid_t blockid,
offset_t begin, offset_t end, char *data);
virtual void Write(blockid_t blockid,
offset_t begin, offset_t end, const char *data);
void ReadBypass(blockid_t blockid,
offset_t begin, offset_t end, char *data);
void WriteBypass(blockid_t blockid,
offset_t begin, offset_t end, const char *data);
virtual blockid_t AllocBlocks(blockid_t i);
/**
* The inner block device, in case you need to circumvent for some reason.
*
* (This is used for example so that a cache-array can read the header
* block in a write-only mode.)
*/
BlockDevice *inner() const { return inner_; }
};
class RandomAccessFile {
private:
int fd_;
mode_t mode_;
String fname_;
public:
RandomAccessFile() {}
~RandomAccessFile() { Close(); }
void Init(const char *fname, BlockDevice::mode_t mode);
void Read(off_t pos, size_t len, char *buffer);
void Write(off_t pos, size_t len, const char *buffer);
void Close();
off_t FindSize() const;
};
class DiskBlockDevice : public BlockDevice {
FORBID_COPY(DiskBlockDevice);
private:
mode_t mode_;
RandomAccessFile file_;
public:
DiskBlockDevice() {}
virtual ~DiskBlockDevice();
/**
* Opens a disk file with the given mode.
*
* If fname is NULL and mode is M_TEMP, then a filename is automatically
* generated, and the file is cleaned up (even if the process dies).
*/
void Init(const char *fname, mode_t mode, offset_t block_size);
void Read(blockid_t blockid, offset_t begin, offset_t end,
char *data);
void Write(blockid_t blockid, offset_t begin, offset_t end,
const char *data);
};
class MemBlockDevice : public BlockDevice {
private:
DenseIntMap<char*> blocks_;
public:
MemBlockDevice() {}
virtual ~MemBlockDevice();
void Init(offset_t block_size);
virtual void Read(blockid_t blockid,
offset_t begin, offset_t end, char *data);
virtual void Write(blockid_t blockid,
offset_t begin, offset_t end, const char *data);
};
class IoStats {
private:
uint64 n_read_bytes_;
uint64 n_write_bytes_;
uint n_reads_;
uint n_writes_;
OT_DEF(IoStats) {
OT_MY_OBJECT(n_read_bytes_);
OT_MY_OBJECT(n_write_bytes_);
OT_MY_OBJECT(n_reads_);
OT_MY_OBJECT(n_writes_);
}
public:
void Init() {
Reset();
}
uint n_reads() const {
return n_reads_;
}
uint64 n_read_bytes() const {
return n_read_bytes_;
}
uint n_io() const {
return n_reads_ + n_writes_;
}
uint64 n_io_bytes() const {
return n_read_bytes_ + n_write_bytes_;
}
void Add(const IoStats& other) {
n_reads_ += other.n_reads_;
n_writes_ += other.n_writes_;
n_read_bytes_ += other.n_read_bytes_;
n_write_bytes_ += other.n_write_bytes_;
}
void RecordRead(uint64 n_bytes) {
n_read_bytes_ += n_bytes;
n_reads_++;
}
void RecordWrite(uint64 n_bytes) {
n_write_bytes_ += n_bytes;
n_writes_++;
}
/**
* Reset all counts to zero.
*/
void Reset() {
n_reads_ = n_writes_ = 0;
n_read_bytes_ = n_write_bytes_ = 0;
}
/**
* Reports the statistics gathered, and include information based on the
* percentage of total data.
*/
void Report(BlockDevice::offset_t n_block_bytes,
BlockDevice::blockid_t n_blocks,
datanode *module) const;
/**
* Reports only the tallies.
*/
void Report(datanode *module) const;
};
#endif