8000 Added Windows support by matt-farmer · Pull Request #3 · tysonmote/gommap · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Added Windows support #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jan 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@

## This is the original text from the upstream readme, preserved for reference:

gommap
======

Expand All @@ -10,3 +13,47 @@ includes [this patch][osx_patch], which adds support for darwin64 (MacOS X).
[Read more](http://labix.org/gommap)

[API documentation](http://godoc.org/github.com/tysontate/gommap)


nsip changes
============

This fork was created so that we could add windows support to gommap.

Gommap is a dependency within the [liftbridge](https://github.com/liftbridge-io/liftbridge) streaming engine which we are using, and we need to be able to run the streaming server on windows.

The Gommap windows implementation was done by Qing Miao from the NSIP team, but we drew heavily on work from others which are referenced below to make sure they get the credit they deserve - it's also ended up being a bit of a sample of known golang memory maps that work cross-platform, so putting it here in case others find it useful.

The state of many implementations of the mem-maps is unclear in terms of support, how active the repos are etc. Of course this is a low-level piece of any system so probably once people have one working they don't do any changes until an OS or processor architecture change forces the need.

We created this fork really because we're prepared to support the implementation for now, and we're passing the changes back up to the original repo so they can accept them if they want to.

The windows version is designed to require no changes to existing code; certainly this is the case for our liftbridge requirement, the liftbridge server now builds and runs fine on windows, and it's been subjected to high volume tests that would force the flushing of the map without incident.

### Limitations
1. We have only tested this on 64-bit Windows
1. We have not been able to implement the (mmap)Advise() or (mmap)IsResident() functions - later versions of windows may have apis that can help to support these (from the documentation we've been able to find), but go can only distinguish os and architecture and those 'advanced' features are not generically available to 'windows'. Please raise an issue if this is a show-stopper.


### Prior Art
Here’s a list of the alternative go mem-map packages that we looked at for help and/or borrowed from directly, and which anyone else may find helpful if you need cross-platform memory-mapping in go:

https://github.com/edsrzf/mmap-go
This one has a very similar interface to the original labix library, but does seem to provide support for windows and various linux distros and Mac.

https://github.com/golang/exp/tree/master/mmap
Package from the golang devs for mmap. Not immediately useful for our requirement as it only offers a reader interface, but does have nice use of the syscall package in the windows golang file that might be useful if we end up having to create our own library completely from scratch.

https://github.com/justmao945/tama/tree/master/mmap
This one uses a full go File paradigm rather than a byte array.

https://github.com/influxdata/platform/tree/master/pkg/mmap
This one is used by influxdb, so we know it works on multiple platforms (we also use influxdb as part of the same project). Difference here is that the API is much simpler, just open - returns a byte array, and then close!

https://github.com/go-util/mmap
An interesting one that is actively developed. Uses a mixture of file/byte array paradigms, so may operate on Windows using a file-based approach, with lower-level calls for unix systems; offers reader and writer interfaces if required.

https://github.com/AlexStocks/goext/tree/master/syscall
Another active repo, with a mmap for unix and windows, offers the simple interface for byte array which should be compatible with the simple calls used by liftbridge.


1 change: 1 addition & 0 deletions consts.c.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

int main(int argc, char *argvc[]) {
pcomment("** This file is automatically generated from consts.c.txt **\n");
pcomment("+build !windows\n");
ppackage("gommap");
ptype(ProtFlags, uint);
pconstblock(
Expand Down
33 changes: 17 additions & 16 deletions consts.go
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
// ** This file is automatically generated from consts.c.txt **
// +build !windows

package gommap

type ProtFlags uint

const (
PROT_NONE ProtFlags = 0x0
PROT_READ ProtFlags = 0x1
PROT_WRITE ProtFlags = 0x2
PROT_EXEC ProtFlags = 0x4
PROT_NONE ProtFlags = 0x0
PROT_READ ProtFlags = 0x1
PROT_WRITE ProtFlags = 0x2
PROT_EXEC ProtFlags = 0x4
)

type MapFlags uint

const (
MAP_SHARED MapFlags = 0x1
MAP_PRIVATE MapFlags = 0x2
MAP_FIXED MapFlags = 0x10
MAP_ANONYMOUS MapFlags = 0x20
MAP_GROWSDOWN MapFlags = 0x100
MAP_LOCKED MapFlags = 0x2000
MAP_NONBLOCK MapFlags = 0x10000
MAP_NORESERVE MapFlags = 0x4000
MAP_POPULATE MapFlags = 0x8000
MAP_SHARED MapFlags = 0x1
MAP_PRIVATE MapFlags = 0x2
MAP_FIXED MapFlags = 0x10
MAP_ANONYMOUS MapFlags = 0x20
MAP_GROWSDOWN MapFlags = 0x100
MAP_LOCKED MapFlags = 0x2000
MAP_NONBLOCK MapFlags = 0x10000
MAP_NORESERVE MapFlags = 0x4000
MAP_POPULATE MapFlags = 0x8000
)

type SyncFlags uint

const (
MS_SYNC SyncFlags = 0x4
MS_ASYNC SyncFlags = 0x1
MS_INVALIDATE SyncFlags = 0x2
MS_SYNC SyncFlags = 0x4
MS_ASYNC SyncFlags = 0x1
MS_INVALIDATE SyncFlags = 0x2
)

type AdviseFlags uint
Expand Down
69 changes: 69 additions & 0 deletions consts_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// +build windows

package gommap

const (
// RDONLY maps the memory read-only.
// Attempts to write to the MMap object will result in undefined behavior.
RDONLY = 0
// RDWR maps the memory as read-write. Writes to the MMap object will update the
// underlying file.
RDWR = 1 << iota
// COPY maps the memory as copy-on-write. Writes to the MMap object will affect
// memory, but the underlying file will remain unchanged.
COPY
// If EXEC is set, the mapped memory is marked as executable.
EXEC
)

type ProtFlags uint

const (
// PROT_NONE ProtFlags = 0x0
// PROT_READ ProtFlags = 0x1
// PROT_WRITE ProtFlags = 0x2
// PROT_EXEC ProtFlags = 0x4

// PROT_NONE ProtFlags = 0x0 /* No PROT_NONE on windows */
PROT_READ ProtFlags = RDONLY
PROT_WRITE ProtFlags = RDWR
PROT_COPY ProtFlags = COPY /* PROT_COPY Only on windows, use this flag to implement MAP_PRIVATE */
PROT_EXEC ProtFlags = EXEC
)

/* on win, use ProtFlags to simulate MapFlags */

type MapFlags uint

const (
MAP_SHARED MapFlags = 0x1
MAP_PRIVATE MapFlags = 0x2
MAP_FIXED MapFlags = 0x10
MAP_ANONYMOUS MapFlags = 0x20
MAP_GROWSDOWN MapFlags = 0x100
MAP_LOCKED MapFlags = 0x2000
MAP_NONBLOCK MapFlags = 0x10000
MAP_NORESERVE MapFlags = 0x4000
MAP_POPULATE MapFlags = 0x8000
)

type SyncFlags uint

const (
MS_SYNC SyncFlags = 0x4
MS_ASYNC SyncFlags = 0x1
MS_INVALIDATE SyncFlags = 0x2
)

type AdviseFlags uint

const (
MADV_NORMAL AdviseFlags = 0x0
MADV_RANDOM AdviseFlags = 0x1
MADV_SEQUENTIAL AdviseFlags = 0x2
MADV_WILLNEED AdviseFlags = 0x3
MADV_DONTNEED AdviseFlags = 0x4
MADV_REMOVE AdviseFlags = 0x9
MADV_DONTFORK AdviseFlags = 0xa
MADV_DOFORK AdviseFlags = 0xb
)
2 changes: 2 additions & 0 deletions gommap.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// +build !windows

// This package offers the MMap type that m 6D40 anipulates a memory mapped file or
// device.
//
Expand Down
35 changes: 18 additions & 17 deletions gommap_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package gommap_test
// +build !windows

package gommap

import (
"github.com/tysontate/gommap"
"io/ioutil"
. "launchpad.net/gocheck"
"os"
Expand Down Expand Up @@ -37,33 +38,33 @@ func (s *S) TearDownTest(c *C) {
}

func (s *S) TestUnsafeUnmap(c *C) {
mmap, err := gommap.Map(s.file.Fd(), gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
mmap, err := Map(s.file.Fd(), PROT_READ|PROT_WRITE, MAP_SHARED)
c.Assert(err, IsNil)
c.Assert(mmap.UnsafeUnmap(), IsNil)
}

func (s *S) TestReadWrite(c *C) {
mmap, err := gommap.Map(s.file.Fd(), gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
mmap, err := Map(s.file.Fd(), PROT_READ|PROT_WRITE, MAP_SHARED)
c.Assert(err, IsNil)
defer mmap.UnsafeUnmap()
c.Assert([]byte(mmap), DeepEquals, testData)

mmap[9] = 'X'
mmap.Sync(gommap.MS_SYNC)
mmap.Sync(MS_SYNC)

fileData, err := ioutil.ReadFile(s.file.Name())
c.Assert(err, IsNil)
c.Assert(fileData, DeepEquals, []byte("012345678XABCDEF"))
}

func (s *S) TestSliceMethods(c *C) {
mmap, err := gommap.Map(s.file.Fd(), gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
mmap, err := Map(s.file.Fd(), PROT_READ|PROT_WRITE, MAP_SHARED)
c.Assert(err, IsNil)
defer mmap.UnsafeUnmap()
c.Assert([]byte(mmap), DeepEquals, testData)

mmap[9] = 'X'
mmap[7:10].Sync(gommap.MS_SYNC)
mmap[7:10].Sync(MS_SYNC)

fileData, err := ioutil.ReadFile(s.file.Name())
c.Assert(err, IsNil)
Expand All @@ -76,18 +77,18 @@ func (s *S) TestProtFlagsAndErr(c *C) {
file, err := os.Open(testPath)
c.Assert(err, IsNil)
s.file = file
_, err = gommap.Map(s.file.Fd(), gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_SHARED)
_, err = Map(s.file.Fd(), PROT_READ|PROT_WRITE, MAP_SHARED)
// For this to happen, both the error and the protection flag must work.
c.Assert(err, Equals, syscall.EACCES)
}

func (s *S) TestFlags(c *C) {
mmap, err := gommap.Map(s.file.Fd(), gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_PRIVATE)
mmap, err := Map(s.file.Fd(), PROT_READ|PROT_WRITE, MAP_PRIVATE)
c.Assert(err, IsNil)
defer mmap.UnsafeUnmap()

mmap[9] = 'X'
mmap.Sync(gommap.MS_SYNC)
mmap.Sync(MS_SYNC)

fileData, err := ioutil.ReadFile(s.file.Name())
c.Assert(err, IsNil)
Expand All @@ -96,33 +97,33 @@ func (s *S) TestFlags(c *C) {
}

func (s *S) TestAdvise(c *C) {
mmap, err := gommap.Map(s.file.Fd(), gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_PRIVATE)
mmap, err := Map(s.file.Fd(), PROT_READ|PROT_WRITE, MAP_PRIVATE)
c.Assert(err, IsNil)
defer mmap.UnsafeUnmap()

// A bit tricky to blackbox-test these.
err = mmap.Advise(gommap.MADV_RANDOM)
err = mmap.Advise(MADV_RANDOM)
c.Assert(err, IsNil)

err = mmap.Advise(9999)
c.Assert(err, ErrorMatches, "invalid argument")
}

func (s *S) TestProtect(c *C) {
mmap, err := gommap.Map(s.file.Fd(), gommap.PROT_READ, gommap.MAP_SHARED)
mmap, err := Map(s.file.Fd(), PROT_READ, MAP_SHARED)
c.Assert(err, IsNil)
defer mmap.UnsafeUnmap()
c.Assert([]byte(mmap), DeepEquals, testData)

err = mmap.Protect(gommap.PROT_READ | gommap.PROT_WRITE)
err = mmap.Protect(PROT_READ | PROT_WRITE)
c.Assert(err, IsNil)

// If this operation doesn't blow up tests, the call above worked.
mmap[9] = 'X'
}

func (s *S) TestLock(c *C) {
mmap, err := gommap.Map(s.file.Fd(), gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_PRIVATE)
mmap, err := Map(s.file.Fd(), PROT_READ|PROT_WRITE, MAP_PRIVATE)
c.Assert(err, IsNil)
defer mmap.UnsafeUnmap()

Expand All @@ -141,7 +142,7 @@ func (s *S) TestLock(c *C) {
}

func (s *S) TestIsResidentUnderOnePage(c *C) {
mmap, err := gommap.Map(s.file.Fd(), gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_PRIVATE)
mmap, err := Map(s.file.Fd(), PROT_READ|PROT_WRITE, MAP_PRIVATE)
c.Assert(err, IsNil)
defer mmap.UnsafeUnmap()

Expand All @@ -159,7 +160,7 @@ func (s *S) TestIsResidentTwoPages(c *C) {
file.Seek(int64(os.Getpagesize()*2-1), 0)
file.Write([]byte{'x'})

mmap, err := gommap.Map(file.Fd(), gommap.PROT_READ|gommap.PROT_WRITE, gommap.MAP_PRIVATE)
mmap, err := Map(file.Fd(), PROT_READ|PROT_WRITE, MAP_PRIVATE)
c.Assert(err, IsNil)
defer mmap.UnsafeUnmap()

Expand Down
Loading
0