Skip to content

Commit

Permalink
Add a dirty flag to avoid syscall
Browse files Browse the repository at this point in the history
  • Loading branch information
mangalaman93 committed Jan 14, 2020
1 parent cf5d1cf commit 7c9f4c3
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 1 deletion.
1 change: 1 addition & 0 deletions mmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var (
type File struct {
data []byte
length int64
dirty bool
}

// NewSharedFileMmap maps a file into memory starting at a given offset, for given length.
Expand Down
11 changes: 10 additions & 1 deletion mmap_data.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ func (m *File) ReadAt(dest []byte, offset int64) (int, error) {
// err is always nil, hence, can be ignored.
func (m *File) WriteAt(src []byte, offset int64) (int, error) {
m.boundaryChecks(offset, 1)
m.dirty = true
return copy(m.data[offset:], src), nil
}

Expand All @@ -64,6 +65,7 @@ func (m *File) ReadStringAt(dest *strings.Builder, offset int64) int {
// given offset and returns number of bytes copied to the mapped region.
func (m *File) WriteStringAt(src string, offset int64) int {
m.boundaryChecks(offset, 1)
m.dirty = true
return copy(m.data[offset:], src)
}

Expand All @@ -76,16 +78,23 @@ func (m *File) ReadUint64At(offset int64) uint64 {
// WriteUint64At writes num at offset.
func (m *File) WriteUint64At(num uint64, offset int64) {
m.boundaryChecks(offset, 8)
m.dirty = true
binary.LittleEndian.PutUint64(m.data[offset:offset+8], num)
}

// Flush flushes the memory mapped region to disk.
// Flush flushes the memory mapped region to disk. Flush makes a
// syscall only if the memory region is modified since the last flush.
func (m *File) Flush(flags int) error {
if !m.dirty {
return nil
}

_, _, err := syscall.Syscall(syscall.SYS_MSYNC,
uintptr(unsafe.Pointer(&m.data[0])), uintptr(m.length), uintptr(flags))
if err != 0 {
return err
}

m.dirty = false
return nil
}
77 changes: 77 additions & 0 deletions mmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,3 +597,80 @@ func TestIOInterfaces(t *testing.T) {
t.Fatalf("zip file not read correctly, contains unexpected file")
}
}

func TestDirtyFlag(t *testing.T) {
setup(t)
defer tearDown(t)

f, err := os.OpenFile(testPath, os.O_RDWR, 0644)
if err != nil {
t.Fatalf("error in opening file :: %v", err)
}
defer func() {
if err := f.Close(); err != nil {
t.Fatalf("error in closing file :: %v", err)
}
}()

m, err := NewSharedFileMmap(f, 0, len(testData), protPage)
if err != nil {
t.Fatalf("error in mapping :: %v", err)
}
defer func() {
if err := m.Unmap(); err != nil {
t.Fatalf("error in calling unmap :: %v", err)
}
}()

m.WriteUint64At(0, 0)
if !m.dirty {
t.Fatalf("expected file to be dirty")
}
if err := m.Flush(syscall.MS_SYNC); err != nil {
t.Fatalf("error in calling flush :: %v", err)
}
if m.dirty {
t.Fatalf("expected file to be not dirty")
}
_ = m.ReadUint64At(0)
// No actual syscall here
if err := m.Flush(syscall.MS_SYNC); err != nil {
t.Fatalf("error in calling flush :: %v", err)
}
if m.dirty {
t.Fatalf("expected file to be not dirty")
}

_ = m.WriteStringAt("string", 0)
if !m.dirty {
t.Fatalf("expected file to be dirty")
}
if err := m.Flush(syscall.MS_SYNC); err != nil {
t.Fatalf("error in calling flush :: %v", err)
}
if m.dirty {
t.Fatalf("expected file to be not dirty")
}
sb := &strings.Builder{}
sb.Grow(len("string"))
_ = m.ReadStringAt(sb, 0)
if m.dirty {
t.Fatalf("expected file to be not dirty")
}

_, _ = m.WriteAt([]byte{1, 2}, 0)
if !m.dirty {
t.Fatalf("expected file to be dirty")
}
if err := m.Flush(syscall.MS_SYNC); err != nil {
t.Fatalf("error in calling flush :: %v", err)
}
if m.dirty {
t.Fatalf("expected file to be not dirty")
}
bs := make([]byte, 2)
_, _ = m.ReadAt(bs, 0)
if m.dirty {
t.Fatalf("expected file to be not dirty")
}
}

0 comments on commit 7c9f4c3

Please sign in to comment.