Add extfs background sync thread.
This commit is contained in:
parent
26336de7ff
commit
6e8f17b5df
|
@ -12,6 +12,7 @@ CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti
|
||||||
LIBS:=$(LIBS)
|
LIBS:=$(LIBS)
|
||||||
|
|
||||||
ifeq ($(HOST_IS_SORTIX),0)
|
ifeq ($(HOST_IS_SORTIX),0)
|
||||||
|
PTHREAD_OPTION:=-pthread
|
||||||
LIBS:=$(LIBS) -lfuse
|
LIBS:=$(LIBS) -lfuse
|
||||||
CPPFLAGS:=$(CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
CPPFLAGS:=$(CPPFLAGS) -D_FILE_OFFSET_BITS=64
|
||||||
endif
|
endif
|
||||||
|
@ -27,7 +28,7 @@ install: all
|
||||||
install $(BINARIES) $(DESTDIR)$(BINDIR)
|
install $(BINARIES) $(DESTDIR)$(BINDIR)
|
||||||
|
|
||||||
extfs: *.cpp *.h
|
extfs: *.cpp *.h
|
||||||
$(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) *.cpp -o $@ $(LIBS)
|
$(CXX) $(PTHREAD_OPTION) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) *.cpp -o $@ $(LIBS)
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f $(BINARIES) *.o
|
rm -f $(BINARIES) *.o
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -31,6 +33,8 @@
|
||||||
|
|
||||||
Block::Block(Device* device, uint32_t block_id)
|
Block::Block(Device* device, uint32_t block_id)
|
||||||
{
|
{
|
||||||
|
this->modify_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
|
||||||
|
this->transit_done_cond = PTHREAD_COND_INITIALIZER;
|
||||||
this->prev_block = NULL;
|
this->prev_block = NULL;
|
||||||
this->next_block = NULL;
|
this->next_block = NULL;
|
||||||
this->prev_hashed = NULL;
|
this->prev_hashed = NULL;
|
||||||
|
@ -41,7 +45,8 @@ Block::Block(Device* device, uint32_t block_id)
|
||||||
this->reference_count = 1;
|
this->reference_count = 1;
|
||||||
this->block_id = block_id;
|
this->block_id = block_id;
|
||||||
this->dirty = false;
|
this->dirty = false;
|
||||||
this->block_data = 0;
|
this->is_in_transit = false;
|
||||||
|
this->block_data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
Block::~Block()
|
Block::~Block()
|
||||||
|
@ -68,6 +73,15 @@ void Block::Unref()
|
||||||
|
|
||||||
void Block::Sync()
|
void Block::Sync()
|
||||||
{
|
{
|
||||||
|
if ( device->has_sync_thread )
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&device->sync_thread_lock);
|
||||||
|
while ( dirty || is_in_transit )
|
||||||
|
pthread_cond_wait(&transit_done_cond, &device->sync_thread_lock);
|
||||||
|
pthread_mutex_unlock(&device->sync_thread_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if ( !dirty )
|
if ( !dirty )
|
||||||
return;
|
return;
|
||||||
dirty = false;
|
dirty = false;
|
||||||
|
@ -84,10 +98,14 @@ void Block::Sync()
|
||||||
|
|
||||||
void Block::BeginWrite()
|
void Block::BeginWrite()
|
||||||
{
|
{
|
||||||
|
assert(device->write);
|
||||||
|
pthread_mutex_lock(&modify_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Block::FinishWrite()
|
void Block::FinishWrite()
|
||||||
{
|
{
|
||||||
|
pthread_mutex_unlock(&modify_lock);
|
||||||
|
pthread_mutex_lock(&device->sync_thread_lock);
|
||||||
if ( !dirty )
|
if ( !dirty )
|
||||||
{
|
{
|
||||||
dirty = true;
|
dirty = true;
|
||||||
|
@ -96,7 +114,9 @@ void Block::FinishWrite()
|
||||||
if ( next_dirty )
|
if ( next_dirty )
|
||||||
next_dirty->prev_dirty = this;
|
next_dirty->prev_dirty = this;
|
||||||
device->dirty_block = this;
|
device->dirty_block = this;
|
||||||
|
pthread_cond_signal(&device->sync_thread_cond);
|
||||||
}
|
}
|
||||||
|
pthread_mutex_unlock(&device->sync_thread_lock);
|
||||||
Use();
|
Use();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,6 +32,8 @@ public:
|
||||||
~Block();
|
~Block();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
pthread_mutex_t modify_lock;
|
||||||
|
pthread_cond_t transit_done_cond;
|
||||||
Block* prev_block;
|
Block* prev_block;
|
||||||
Block* next_block;
|
Block* next_block;
|
||||||
Block* prev_hashed;
|
Block* prev_hashed;
|
||||||
|
@ -42,6 +44,7 @@ public:
|
||||||
size_t reference_count;
|
size_t reference_count;
|
||||||
uint32_t block_id;
|
uint32_t block_id;
|
||||||
bool dirty;
|
bool dirty;
|
||||||
|
bool is_in_transit;
|
||||||
uint8_t* block_data;
|
uint8_t* block_data;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <pthread.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -33,6 +34,12 @@
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "ioleast.h"
|
#include "ioleast.h"
|
||||||
|
|
||||||
|
void* Device__SyncThread(void* ctx)
|
||||||
|
{
|
||||||
|
((Device*) ctx)->SyncThread();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
Device::Device(int fd, uint32_t block_size, bool write)
|
Device::Device(int fd, uint32_t block_size, bool write)
|
||||||
{
|
{
|
||||||
this->write = write;
|
this->write = write;
|
||||||
|
@ -46,16 +53,38 @@ Device::Device(int fd, uint32_t block_size, bool write)
|
||||||
this->dirty_block = NULL;
|
this->dirty_block = NULL;
|
||||||
for ( size_t i = 0; i < DEVICE_HASH_LENGTH; i++ )
|
for ( size_t i = 0; i < DEVICE_HASH_LENGTH; i++ )
|
||||||
hash_blocks[i] = NULL;
|
hash_blocks[i] = NULL;
|
||||||
|
this->sync_thread_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
this->sync_thread_idle_cond = PTHREAD_COND_INITIALIZER;
|
||||||
|
this->sync_thread_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
this->sync_in_transit = false;
|
||||||
|
this->has_sync_thread = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Device::~Device()
|
Device::~Device()
|
||||||
{
|
{
|
||||||
|
if ( has_sync_thread )
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
sync_thread_should_exit = true;
|
||||||
|
pthread_cond_signal(&sync_thread_cond);
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
pthread_join(sync_thread, NULL);
|
||||||
|
has_sync_thread = false;
|
||||||
|
}
|
||||||
Sync();
|
Sync();
|
||||||
while ( mru_block )
|
while ( mru_block )
|
||||||
delete mru_block;
|
delete mru_block;
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Device::SpawnSyncThread()
|
||||||
|
{
|
||||||
|
if ( this->has_sync_thread )
|
||||||
|
return;
|
||||||
|
this->has_sync_thread = write &&
|
||||||
|
pthread_create(&this->sync_thread, NULL, Device__SyncThread, this) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
Block* Device::GetBlock(uint32_t block_id)
|
Block* Device::GetBlock(uint32_t block_id)
|
||||||
{
|
{
|
||||||
if ( Block* block = GetCachedBlock(block_id) )
|
if ( Block* block = GetCachedBlock(block_id) )
|
||||||
|
@ -97,6 +126,56 @@ Block* Device::GetCachedBlock(uint32_t block_id)
|
||||||
|
|
||||||
void Device::Sync()
|
void Device::Sync()
|
||||||
{
|
{
|
||||||
|
if ( has_sync_thread )
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
while ( dirty_block || sync_in_transit )
|
||||||
|
pthread_cond_wait(&sync_thread_cond, &sync_thread_lock);
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
while ( dirty_block )
|
while ( dirty_block )
|
||||||
dirty_block->Sync();
|
dirty_block->Sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Device::SyncThread()
|
||||||
|
{
|
||||||
|
uint8_t transit_block_data[block_size];
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
while ( true )
|
||||||
|
{
|
||||||
|
while ( !(dirty_block || sync_thread_should_exit) )
|
||||||
|
pthread_cond_wait(&sync_thread_cond, &sync_thread_lock);
|
||||||
|
if ( sync_thread_should_exit )
|
||||||
|
break;
|
||||||
|
|
||||||
|
Block* block = dirty_block;
|
||||||
|
|
||||||
|
if ( block->next_dirty )
|
||||||
|
block->next_dirty->prev_dirty = NULL;
|
||||||
|
dirty_block = block->next_dirty;
|
||||||
|
block->next_dirty = NULL;
|
||||||
|
|
||||||
|
block->dirty = false;
|
||||||
|
block->is_in_transit = true;
|
||||||
|
sync_in_transit = true;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&block->modify_lock);
|
||||||
|
memcpy(transit_block_data, block->block_data, block_size);
|
||||||
|
pthread_mutex_unlock(&block->modify_lock);
|
||||||
|
|
||||||
|
off_t offset = (off_t) block_size * (off_t) block->block_id;
|
||||||
|
pwriteall(fd, transit_block_data, block_size, offset);
|
||||||
|
|
||||||
|
pthread_mutex_lock(&sync_thread_lock);
|
||||||
|
block->is_in_transit = false;
|
||||||
|
sync_in_transit = false;
|
||||||
|
pthread_cond_signal(&block->transit_done_cond);
|
||||||
|
if ( !dirty_block )
|
||||||
|
pthread_cond_signal(&sync_thread_idle_cond);
|
||||||
|
}
|
||||||
|
pthread_mutex_unlock(&sync_thread_lock);
|
||||||
|
}
|
||||||
|
|
|
@ -34,6 +34,10 @@ public:
|
||||||
~Device();
|
~Device();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
pthread_t sync_thread;
|
||||||
|
pthread_cond_t sync_thread_cond;
|
||||||
|
pthread_cond_t sync_thread_idle_cond;
|
||||||
|
pthread_mutex_t sync_thread_lock;
|
||||||
Block* mru_block;
|
Block* mru_block;
|
||||||
Block* lru_block;
|
Block* lru_block;
|
||||||
Block* dirty_block;
|
Block* dirty_block;
|
||||||
|
@ -42,12 +46,17 @@ public:
|
||||||
uint32_t block_size;
|
uint32_t block_size;
|
||||||
int fd;
|
int fd;
|
||||||
bool write;
|
bool write;
|
||||||
|
bool has_sync_thread;
|
||||||
|
bool sync_thread_should_exit;
|
||||||
|
bool sync_in_transit;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
void SpawnSyncThread();
|
||||||
Block* GetBlock(uint32_t block_id);
|
Block* GetBlock(uint32_t block_id);
|
||||||
Block* GetBlockZeroed(uint32_t block_id);
|
Block* GetBlockZeroed(uint32_t block_id);
|
||||||
Block* GetCachedBlock(uint32_t block_id);
|
Block* GetCachedBlock(uint32_t block_id);
|
||||||
void Sync();
|
void Sync();
|
||||||
|
void SyncThread();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -668,6 +668,8 @@ int fsmarshall_main(const char* argv0,
|
||||||
setpgid(0, 0);
|
setpgid(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dev->SpawnSyncThread();
|
||||||
|
|
||||||
// Listen for filesystem messages and sync the filesystem every few seconds.
|
// Listen for filesystem messages and sync the filesystem every few seconds.
|
||||||
struct timespec last_sync_at;
|
struct timespec last_sync_at;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &last_sync_at);
|
clock_gettime(CLOCK_MONOTONIC, &last_sync_at);
|
||||||
|
@ -690,7 +692,8 @@ int fsmarshall_main(const char* argv0,
|
||||||
struct timespec now;
|
struct timespec now;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||||
|
|
||||||
if ( dev->write && 5 <= timespec_sub(now, last_sync_at).tv_sec )
|
if ( dev->write && !dev->has_sync_thread &&
|
||||||
|
5 <= timespec_sub(now, last_sync_at).tv_sec )
|
||||||
{
|
{
|
||||||
fs->Sync();
|
fs->Sync();
|
||||||
last_sync_at = now;
|
last_sync_at = now;
|
||||||
|
|
Loading…
Reference in New Issue