From 1220e67fd11aacd313ec3519cef5ddcb851c2424 Mon Sep 17 00:00:00 2001 From: matt-farmer Date: Wed, 28 Nov 2018 14:58:25 +1100 Subject: [PATCH 1/6] windows support added windows support, see github.com/nsip/gommap for details. --- consts_win.go | 69 ++++++++++++++++++++ gommap_win.go | 166 ++++++++++++++++++++++++++++++++++++++++++++++++ mmap_windows.go | 143 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 378 insertions(+) create mode 100755 consts_win.go create mode 100755 gommap_win.go create mode 100755 mmap_windows.go diff --git a/consts_win.go b/consts_win.go new file mode 100755 index 0000000..c364a72 --- /dev/null +++ b/consts_win.go @@ -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 +) diff --git a/gommap_win.go b/gommap_win.go new file mode 100755 index 0000000..1dae248 --- /dev/null +++ b/gommap_win.go @@ -0,0 +1,166 @@ +// This package offers the MMap type that manipulates a memory mapped file or +// device. +// +// IMPORTANT NOTE (1): The MMap type is backed by an unsafe memory region, +// which is not covered by the normal rules of Go's memory management. If a +// slice is taken out of it, and then the memory is explicitly unmapped through +// one of the available methods, both the MMap value itself and the slice +// obtained will now silently point to invalid memory. Attempting to access +// data in them will crash the application. + +// +build windows + +package gommap + +import ( + "errors" + "os" + "reflect" + "syscall" + "unsafe" +) + +// The MMap type represents a memory mapped file or device. The slice offers +// direct access to the memory mapped content. +// +// IMPORTANT: Please see note in the package documentation regarding the way +// in which this type behaves. +type MMap []byte + +// In order to implement 'Protect', use this to get back the original MMap properties from the memory address. +var mmapAttrs = map[uintptr]*struct { + fd uintptr + offset int64 + length int64 + prot ProtFlags + flags MapFlags +}{} + +// GetFileSize gets the file length from its fd +func GetFileSize(fd uintptr) (int64, error) { + fh := syscall.Handle(fd) + fsize, err := syscall.Seek(syscall.Handle(fh), 0, 2) + syscall.Seek(fh, 0, 0) + return fsize, err +} + +// Map creates a new mapping in the virtual address space of the calling process. +// This function will attempt to map the entire file by using the fstat system +// call with the provided file descriptor to discover its length. +func Map(fd uintptr, prot ProtFlags, flags MapFlags) (MMap, error) { + return MapRegion(fd, 0, -1, prot, flags) +} + +// MapRegion creates a new mapping in the virtual address space of the calling +// process, using the specified region of the provided file or device. If -1 is +// provided as length, this function will attempt to map until the end of the +// provided file descriptor by using the fstat system call to discover its +// length. +func MapRegion(fd uintptr, offset, length int64, prot ProtFlags, flags MapFlags) (MMap, error) { + if offset%int64(os.Getpagesize()) != 0 { + return nil, errors.New("offset parameter must be a multiple of the system's page size") + } + if length == -1 { + length, _ = GetFileSize(fd) + } + /* on windows, use PROT_COPY to do the same thing as linux MAP_PRIVATE flag do */ + if flags == MAP_PRIVATE { + prot = PROT_COPY + } + // return mmap(length, uintptr(prot), uintptr(flags), fd, offset) + + /*******************************/ + m, e := mmap(length, uintptr(prot), uintptr(flags), fd, offset) + dh := (*reflect.SliceHeader)(unsafe.Pointer(&m)) + mmapAttrs[dh.Data] = &struct { + fd uintptr + offset int64 + length int64 + prot ProtFlags + flags MapFlags + }{fd, offset, length, prot, flags} + return m, e +} + +func (mmap *MMap) header() *reflect.SliceHeader { + return (*reflect.SliceHeader)(unsafe.Pointer(mmap)) +} + +// UnsafeUnmap deletes the memory mapped region defined by the mmap slice. This +// will also flush any remaining changes, if necessary. Using mmap or any +// other slices based on it after this method has been called will crash the +// application. +func (mmap MMap) UnsafeUnmap() error { + dh := mmap.header() + return unmap(dh.Data, uintptr(dh.Len)) +} + +// Sync flushes changes made to the region determined by the mmap slice +// back to the device. Without calling this method, there are no guarantees +// that changes will be flushed back before the region is unmapped. The +// flags parameter specifies whether flushing should be done synchronously +// (before the method returns) with MS_SYNC, or asynchronously (flushing is just +// scheduled) with MS_ASYNC. +func (mmap MMap) Sync(flags SyncFlags) error { + dh := mmap.header() + return flush(dh.Data, uintptr(dh.Len)) +} + +// // Advise advises the kernel about how to handle the mapped memory +// // region in terms of input/output paging within the memory region +// // defined by the mmap slice. +// func (mmap MMap) Advise(advice AdviseFlags) error { +// // rh := *(*reflect.SliceHeader)(unsafe.Pointer(&mmap)) +// // _, _, err := syscall.Syscall(syscall.SYS_MADVISE, uintptr(rh.Data), uintptr(rh.Len), uintptr(advice)) +// // if err != 0 { +// // return err +// // } +// // return nil +// } + +// Protect changes the protection flags for the memory mapped region +// defined by the mmap slice. +// We use unmap & map again to implement this on windows. So can only change the protect flags on the whole +func (mmap *MMap) Protect(prot ProtFlags) (err error) { + dh := mmap.header() + var m MMap + if err = mmap.UnsafeUnmap(); err == nil { + fd, offset, length, flags := mmapAttrs[dh.Data].fd, mmapAttrs[dh.Data].offset, mmapAttrs[dh.Data].length, mmapAttrs[dh.Data].flags + mmapAttrs[dh.Data] = nil + if m, err = MapRegion(fd, offset, length, prot, flags); err == nil { + mmap = &m + } + } + return +} + +// Lock locks the mapped region defined by the mmap slice, +// preventing it from being swapped out. +func (mmap MMap) Lock() error { + dh := mmap.header() + return lock(dh.Data, uintptr(dh.Len)) +} + +// Unlock unlocks the mapped region defined by the mmap slice, +// allowing it to swap out again. +func (mmap MMap) Unlock() error { + dh := mmap.header() + return unlock(dh.Data, uintptr(dh.Len)) +} + +// // IsResident returns a slice of booleans informing whether the respective +// // memory page in mmap was mapped at the time the call was made. +// func (mmap MMap) IsResident() ([]bool, error) { +// pageSize := os.Getpagesize() +// result := make([]bool, (len(mmap)+pageSize-1)/pageSize) +// rh := *(*reflect.SliceHeader)(unsafe.Pointer(&mmap)) +// resulth := *(*reflect.SliceHeader)(unsafe.Pointer(&result)) +// _, _, err := syscall.Syscall(syscall.SYS_MINCORE, uintptr(rh.Data), uintptr(rh.Len), uintptr(resulth.Data)) +// for i := range result { +// *(*uint8)(unsafe.Pointer(&result[i])) &= 1 +// } +// if err != 0 { +// return nil, err +// } +// return result, nil +// } diff --git a/mmap_windows.go b/mmap_windows.go new file mode 100755 index 0000000..caf2d37 --- /dev/null +++ b/mmap_windows.go @@ -0,0 +1,143 @@ +// Copyright 2011 Evan Shaw. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build windows + +package gommap + +import ( + "errors" + "os" + "sync" + "syscall" +) + +// mmap on Windows is a two-step process. +// First, we call CreateFileMapping to get a handle. +// Then, we call MapviewToFile to get an actual pointer into memory. +// Because we want to emulate a POSIX-style mmap, we don't want to expose +// the handle -- only the pointer. We also want to return only a byte slice, +// not a struct, so it's convenient to manipulate. + +// We keep this map so that we can get back the original handle from the memory address. +var handleLock sync.Mutex +var handleMap = map[uintptr]syscall.Handle{} +var addrLocked = map[uintptr]bool{} + +func mmap(len int64, prot, flags, hfile uintptr, off int64) ([]byte, error) { + flProtect := uint32(syscall.PAGE_READONLY) + dwDesiredAccess := uint32(syscall.FILE_MAP_READ) + switch { + case prot© != 0: + flProtect = syscall.PAGE_WRITECOPY + dwDesiredAccess = syscall.FILE_MAP_COPY + case prot&RDWR != 0: + flProtect = syscall.PAGE_READWRITE + dwDesiredAccess = syscall.FILE_MAP_WRITE + } + if prot&EXEC != 0 { + flProtect <<= 4 + dwDesiredAccess |= syscall.FILE_MAP_EXECUTE + } + + // The maximum size is the area of the file, starting from 0, + // that we wish to allow to be mappable. It is the sum of + // the length the user requested, plus the offset where that length + // is starting from. This does not map the data into memory. + maxSizeHigh := uint32((off + len) >> 32) + maxSizeLow := uint32((off + len) & 0xFFFFFFFF) + // TODO: Do we need to set some security attributes? It might help portability. + h, errno := syscall.CreateFileMapping(syscall.Handle(hfile), nil, flProtect, maxSizeHigh, maxSizeLow, nil) + if h == 0 { + if errno == syscall.ERROR_ACCESS_DENIED { + return nil, syscall.EACCES + } + return nil, os.NewSyscallError("CreateFileMapping", errno) + } + + // Actually map a view of the data into memory. The view's size + // is the length the user requested. + fileOffsetHigh := uint32(off >> 32) + fileOffsetLow := uint32(off & 0xFFFFFFFF) + addr, errno := syscall.MapViewOfFile(h, dwDesiredAccess, fileOffsetHigh, fileOffsetLow, uintptr(len)) + if addr == 0 { + return nil, os.NewSyscallError("MapViewOfFile", errno) + } + handleLock.Lock() + handleMap[addr] = h + handleLock.Unlock() + + m := MMap{} + dh := m.header() + dh.Data = addr + dh.Len = int(len) + dh.Cap = dh.Len + + return m, nil +} + +func flush(addr, len uintptr) error { + errno := syscall.FlushViewOfFile(addr, len) + if errno != nil { + return os.NewSyscallError("FlushViewOfFile", errno) + } + + handleLock.Lock() + defer handleLock.Unlock() + handle, ok := handleMap[addr] + if !ok { + // should be impossible; we would've errored above + return errors.New("unknown base address") + } + + errno = syscall.FlushFileBuffers(handle) + return os.NewSyscallError("FlushFileBuffers", errno) +} + +func lock(addr, len uintptr) error { + if addrLocked[addr] { + return nil + } + errno := syscall.VirtualLock(addr, len) + if errno == nil { + addrLocked[addr] = true + } + return os.NewSyscallError("VirtualLock", errno) +} + +func unlock(addr, len uintptr) error { + if !addrLocked[addr] { + return nil + } + errno := syscall.VirtualUnlock(addr, len) + if errno == nil { + addrLocked[addr] = false + } + return os.NewSyscallError("VirtualUnlock", errno) +} + +func unmap(addr, len uintptr) error { + flush(addr, len) + // Lock the UnmapViewOfFile along with the handleMap deletion. + // As soon as we unmap the view, the OS is free to give the + // same addr to another new map. We don't want another goroutine + // to insert and remove the same addr into handleMap while + // we're trying to remove our old addr/handle pair. + handleLock.Lock() + defer handleLock.Unlock() + err := syscall.UnmapViewOfFile(addr) + if err != nil { + return err + } + + handle, ok := handleMap[addr] + if !ok { + // should be impossible; we would've errored above + return errors.New("unknown base address") + } + delete(handleMap, addr) + + e := syscall.CloseHandle(syscall.Handle(handle)) + return os.NewSyscallError("CloseHandle", e) +} From 673e1584bbfd2e39753e79e74dc7e3c54941ef80 Mon Sep 17 00:00:00 2001 From: matt-farmer Date: Wed, 28 Nov 2018 15:40:09 +1100 Subject: [PATCH 2/6] Update README.md --- README.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/README.md b/README.md index cd58f54..f7f3f2d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ + +## This is the original text from the upstream readme, preserved for reference: + gommap ====== @@ -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 liftbrodge 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 impement 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 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. + + From 38c48777232fbb4f108b6592a04dd2da87b5a8bc Mon Sep 17 00:00:00 2001 From: matt-farmer Date: Wed, 28 Nov 2018 15:41:46 +1100 Subject: [PATCH 3/6] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f7f3f2d..5521dd8 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ The state of many implementations of the mem-maps is unclear in terms of support 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 liftbrodge 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. +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 From 2d71e7f8c9611956277cc47cbc1b76b5eb411c61 Mon Sep 17 00:00:00 2001 From: matt-farmer Date: Wed, 28 Nov 2018 15:46:54 +1100 Subject: [PATCH 4/6] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5521dd8..5a860b1 100644 --- a/README.md +++ b/README.md @@ -32,11 +32,11 @@ The windows version is designed to require no changes to existing code; certainl ### Limitations 1. We have only tested this on 64-bit Windows -1. We have not been able to impement 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. +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 which anyone else may find helpful if you need cross-platform memory-mapping in go: +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. From b49d814b1ebed6a43790b39fb4dbec822dd00d1d Mon Sep 17 00:00:00 2001 From: matt-farmer Date: Tue, 18 Dec 2018 09:20:03 +1100 Subject: [PATCH 5/6] added windows test file runs same tests as gommap_test.go for the windows implementation. Does not test Advise() or IsResident() methods as not currenlty implemented. Tests will fail on non-windows build machine. --- gommap_win_test.go | 132 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100755 gommap_win_test.go diff --git a/gommap_win_test.go b/gommap_win_test.go new file mode 100755 index 0000000..45108cd --- /dev/null +++ b/gommap_win_test.go @@ -0,0 +1,132 @@ +package gommap_test + +import ( + "io/ioutil" + "os" + "path" + "syscall" + "testing" + + "./gommap" + . "gopkg.in/check.v1" +) + +func TestAll(t *testing.T) { + TestingT(t) +} + +type S struct { + file *os.File +} + +var _ = Suite(&S{}) + +var testData = []byte("0123456789ABCDEF") + +func (s *S) SetUpTest(c *C) { + testPath := path.Join(c.MkDir(), "test.txt") + file, err := os.Create(testPath) + if err != nil { + panic(err.Error()) + } + s.file = file + s.file.Write(testData) +} + +func (s *S) TearDownTest(c *C) { + s.file.Close() +} + +func (s *S) TestUnsafeUnmap(c *C) { + mmap, err := gommap.Map(s.file.Fd(), gommap.PROT_READ|gommap.PROT_WRITE, gommap.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) + c.Assert(err, IsNil) + defer mmap.UnsafeUnmap() + c.Assert([]byte(mmap), DeepEquals, testData) + + mmap[9] = 'X' + mmap.Sync(gommap.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) + c.Assert(err, IsNil) + defer mmap.UnsafeUnmap() + c.Assert([]byte(mmap), DeepEquals, testData) + + mmap[9] = 'X' + mmap[7:10].Sync(gommap.MS_SYNC) + + fileData, err := ioutil.ReadFile(s.file.Name()) + c.Assert(err, IsNil) + c.Assert(fileData, DeepEquals, []byte("012345678XABCDEF")) +} + +func (s *S) TestProtFlagsAndErr(c *C) { + testPath := s.file.Name() + s.file.Close() + 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) + // 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) + c.Assert(err, IsNil) + defer mmap.UnsafeUnmap() + + mmap[9] = 'X' + mmap.Sync(gommap.MS_SYNC) + + fileData, err := ioutil.ReadFile(s.file.Name()) + c.Assert(err, IsNil) + // Shouldn't have written, since the map is private. + c.Assert(fileData, DeepEquals, []byte("0123456789ABCDEF")) +} + +func (s *S) TestProtect(c *C) { + mmap, err := gommap.Map(s.file.Fd(), gommap.PROT_READ, gommap.MAP_SHARED) + c.Assert(err, IsNil) + defer mmap.UnsafeUnmap() + c.Assert([]byte(mmap), DeepEquals, testData) + + err = mmap.Protect(gommap.PROT_READ | gommap.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) + c.Assert(err, IsNil) + defer mmap.UnsafeUnmap() + + // A bit tricky to blackbox-test these. + err = mmap.Lock() + c.Assert(err, IsNil) + + err = mmap.Lock() + c.Assert(err, IsNil) + + err = mmap.Lock() + c.Assert(err, IsNil) + + err = mmap.Unlock() + c.Assert(err, IsNil) + + err = mmap.Unlock() + c.Assert(err, IsNil) +} From 2ea32acaf26b8d8f68d6b1dd68a4a79fe6d051dc Mon Sep 17 00:00:00 2001 From: Tyler Treat Date: Fri, 28 Dec 2018 19:08:49 -0600 Subject: [PATCH 6/6] Remove relative import and fix tests The relative import causes dep to fail and the tests were not properly building due to OS packaging issues. --- consts.c.txt | 1 + consts.go | 33 +++++++++--------- consts_win.go => consts_windows.go | 0 gommap.go | 2 ++ gommap_test.go | 35 ++++++++++---------- gommap_win.go => gommap_windows.go | 0 gommap_win_test.go => gommap_windows_test.go | 25 +++++++------- 7 files changed, 50 insertions(+), 46 deletions(-) rename consts_win.go => consts_windows.go (100%) rename gommap_win.go => gommap_windows.go (100%) rename gommap_win_test.go => gommap_windows_test.go (73%) diff --git a/consts.c.txt b/consts.c.txt index cb8d4ed..a448ec5 100644 --- a/consts.c.txt +++ b/consts.c.txt @@ -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( diff --git a/consts.go b/consts.go index f7e39ea..50c9432 100644 --- a/consts.go +++ b/consts.go @@ -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 diff --git a/consts_win.go b/consts_windows.go similarity index 100% rename from consts_win.go rename to consts_windows.go diff --git a/gommap.go b/gommap.go index 0e137f8..7bfe3f0 100644 --- a/gommap.go +++ b/gommap.go @@ -1,3 +1,5 @@ +// +build !windows + // This package offers the MMap type that manipulates a memory mapped file or // device. // diff --git a/gommap_test.go b/gommap_test.go index 365c5f9..3a4d4d3 100644 --- a/gommap_test.go +++ b/gommap_test.go @@ -1,7 +1,8 @@ -package gommap_test +// +build !windows + +package gommap import ( - "github.com/tysontate/gommap" "io/ioutil" . "launchpad.net/gocheck" "os" @@ -37,19 +38,19 @@ 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) @@ -57,13 +58,13 @@ func (s *S) TestReadWrite(c *C) { } 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) @@ -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) @@ -96,12 +97,12 @@ 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) @@ -109,12 +110,12 @@ func (s *S) TestAdvise(c *C) { } 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. @@ -122,7 +123,7 @@ func (s *S) TestProtect(c *C) { } 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() @@ -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() @@ -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() diff --git a/gommap_win.go b/gommap_windows.go similarity index 100% rename from gommap_win.go rename to gommap_windows.go diff --git a/gommap_win_test.go b/gommap_windows_test.go similarity index 73% rename from gommap_win_test.go rename to gommap_windows_test.go index 45108cd..0120a99 100755 --- a/gommap_win_test.go +++ b/gommap_windows_test.go @@ -1,4 +1,4 @@ -package gommap_test +package gommap import ( "io/ioutil" @@ -7,7 +7,6 @@ import ( "syscall" "testing" - "./gommap" . "gopkg.in/check.v1" ) @@ -38,19 +37,19 @@ 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) @@ -58,13 +57,13 @@ func (s *S) TestReadWrite(c *C) { } 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) @@ -77,18 +76,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) @@ -97,12 +96,12 @@ func (s *S) TestFlags(c *C) { } 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. @@ -110,7 +109,7 @@ func (s *S) TestProtect(c *C) { } 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()