From 990de0f32c6146a4039339aecce81eafb1ce1ff2 Mon Sep 17 00:00:00 2001 From: Michael Schurter Date: Mon, 8 May 2017 15:43:48 -0700 Subject: [PATCH] Update go-winio to remove cgo on Windows Thanks to Microsoft/go-winio#51 --- scripts/build.sh | 8 +- vendor/github.com/Microsoft/go-winio/file.go | 151 ++++++++++++------ vendor/github.com/Microsoft/go-winio/pipe.go | 15 +- .../Microsoft/go-winio/zsyscall_windows.go | 13 ++ vendor/vendor.json | 6 +- 5 files changed, 136 insertions(+), 57 deletions(-) diff --git a/scripts/build.sh b/scripts/build.sh index dd7147921..afa0f3eb6 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -64,11 +64,15 @@ for target in $targets; do ;; "windows_386") echo "==> Building windows 386..." - CGO_ENABLED=1 CXX=i686-w64-mingw32-g++ CC=i686-w64-mingw32-gcc GOARCH="386" GOOS="windows" go build -ldflags "-X $LDFLAG" -o "pkg/windows_386/nomad.exe" + CGO_ENABLED=0 GOARCH="386" GOOS="windows" go build -ldflags "-X $LDFLAG" -o "pkg/windows_386/nomad.exe" + # Use the following if CGO is required + #CGO_ENABLED=1 CXX=i686-w64-mingw32-g++ CC=i686-w64-mingw32-gcc GOARCH="386" GOOS="windows" go build -ldflags "-X $LDFLAG" -o "pkg/windows_386/nomad.exe" ;; "windows_amd64") echo "==> Building windows amd64..." - CGO_ENABLED=1 CXX=x86_64-w64-mingw32-g++ CC=x86_64-w64-mingw32-gcc GOARCH="amd64" GOOS="windows" go build -ldflags "-X $LDFLAG" -o "pkg/windows_amd64/nomad.exe" + CGO_ENABLED=0 GOARCH="amd64" GOOS="windows" go build -ldflags "-X $LDFLAG" -o "pkg/windows_amd64/nomad.exe" + # Use the following if CGO is required + #CGO_ENABLED=1 CXX=x86_64-w64-mingw32-g++ CC=x86_64-w64-mingw32-gcc GOARCH="amd64" GOOS="windows" go build -ldflags "-X $LDFLAG" -o "pkg/windows_amd64/nomad.exe" ;; "darwin_amd64") echo "==> Building darwin amd64..." diff --git a/vendor/github.com/Microsoft/go-winio/file.go b/vendor/github.com/Microsoft/go-winio/file.go index b955e886c..078a5687b 100644 --- a/vendor/github.com/Microsoft/go-winio/file.go +++ b/vendor/github.com/Microsoft/go-winio/file.go @@ -7,6 +7,7 @@ import ( "io" "runtime" "sync" + "sync/atomic" "syscall" "time" ) @@ -17,6 +18,12 @@ import ( //sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes //sys timeBeginPeriod(period uint32) (n int32) = winmm.timeBeginPeriod +type atomicBool int32 + +func (b *atomicBool) isSet() bool { return atomic.LoadInt32((*int32)(b)) != 0 } +func (b *atomicBool) setFalse() { atomic.StoreInt32((*int32)(b), 0) } +func (b *atomicBool) setTrue() { atomic.StoreInt32((*int32)(b), 1) } + const ( cFILE_SKIP_COMPLETION_PORT_ON_SUCCESS = 1 cFILE_SKIP_SET_EVENT_ON_HANDLE = 2 @@ -33,6 +40,8 @@ func (e *timeoutError) Error() string { return "i/o timeout" } func (e *timeoutError) Timeout() bool { return true } func (e *timeoutError) Temporary() bool { return true } +type timeoutChan chan struct{} + var ioInitOnce sync.Once var ioCompletionPort syscall.Handle @@ -63,8 +72,16 @@ type win32File struct { handle syscall.Handle wg sync.WaitGroup closing bool - readDeadline time.Time - writeDeadline time.Time + readDeadline deadlineHandler + writeDeadline deadlineHandler +} + +type deadlineHandler struct { + setLock sync.Mutex + channel timeoutChan + channelLock sync.RWMutex + timer *time.Timer + timedout atomicBool } // makeWin32File makes a new win32File from an existing file handle @@ -79,6 +96,8 @@ func makeWin32File(h syscall.Handle) (*win32File, error) { if err != nil { return nil, err } + f.readDeadline.channel = make(timeoutChan) + f.writeDeadline.channel = make(timeoutChan) return f, nil } @@ -134,53 +153,47 @@ func ioCompletionProcessor(h syscall.Handle) { // asyncIo processes the return value from ReadFile or WriteFile, blocking until // the operation has actually completed. -func (f *win32File) asyncIo(c *ioOperation, deadline time.Time, bytes uint32, err error) (int, error) { +func (f *win32File) asyncIo(c *ioOperation, d *deadlineHandler, bytes uint32, err error) (int, error) { if err != syscall.ERROR_IO_PENDING { f.wg.Done() return int(bytes), err - } else { - var r ioResult - wait := true - timedout := false - if f.closing { - cancelIoEx(f.handle, &c.o) - } else if !deadline.IsZero() { - now := time.Now() - if !deadline.After(now) { - timedout = true - } else { - timeout := time.After(deadline.Sub(now)) - select { - case r = <-c.ch: - wait = false - case <-timeout: - timedout = true - } - } - } - if timedout { - cancelIoEx(f.handle, &c.o) - } - if wait { - r = <-c.ch - } + } - // runtime.KeepAlive is needed, as c is passed via native - // code to ioCompletionProcessor, c must remain alive - // until the channel read is complete. - runtime.KeepAlive(c) + if f.closing { + cancelIoEx(f.handle, &c.o) + } + var timeout timeoutChan + if d != nil { + d.channelLock.Lock() + timeout = d.channel + d.channelLock.Unlock() + } + + var r ioResult + select { + case r = <-c.ch: err = r.err if err == syscall.ERROR_OPERATION_ABORTED { if f.closing { err = ErrFileClosed - } else if timedout { - err = ErrTimeout } } - f.wg.Done() - return int(r.bytes), err + case <-timeout: + cancelIoEx(f.handle, &c.o) + r = <-c.ch + err = r.err + if err == syscall.ERROR_OPERATION_ABORTED { + err = ErrTimeout + } } + + // runtime.KeepAlive is needed, as c is passed via native + // code to ioCompletionProcessor, c must remain alive + // until the channel read is complete. + runtime.KeepAlive(c) + f.wg.Done() + return int(r.bytes), err } // Read reads from a file handle. @@ -189,9 +202,14 @@ func (f *win32File) Read(b []byte) (int, error) { if err != nil { return 0, err } + + if f.readDeadline.timedout.isSet() { + return 0, ErrTimeout + } + var bytes uint32 err = syscall.ReadFile(f.handle, b, &bytes, &c.o) - n, err := f.asyncIo(c, f.readDeadline, bytes, err) + n, err := f.asyncIo(c, &f.readDeadline, bytes, err) runtime.KeepAlive(b) // Handle EOF conditions. @@ -210,23 +228,66 @@ func (f *win32File) Write(b []byte) (int, error) { if err != nil { return 0, err } + if f.writeDeadline.timedout.isSet() { + return 0, ErrTimeout + } + var bytes uint32 err = syscall.WriteFile(f.handle, b, &bytes, &c.o) - n, err := f.asyncIo(c, f.writeDeadline, bytes, err) + n, err := f.asyncIo(c, &f.writeDeadline, bytes, err) runtime.KeepAlive(b) return n, err } -func (f *win32File) SetReadDeadline(t time.Time) error { - f.readDeadline = t - return nil +func (f *win32File) SetReadDeadline(deadline time.Time) error { + return f.readDeadline.set(deadline) } -func (f *win32File) SetWriteDeadline(t time.Time) error { - f.writeDeadline = t - return nil +func (f *win32File) SetWriteDeadline(deadline time.Time) error { + return f.writeDeadline.set(deadline) } func (f *win32File) Flush() error { return syscall.FlushFileBuffers(f.handle) } + +func (d *deadlineHandler) set(deadline time.Time) error { + d.setLock.Lock() + defer d.setLock.Unlock() + + if d.timer != nil { + if !d.timer.Stop() { + <-d.channel + } + d.timer = nil + } + d.timedout.setFalse() + + select { + case <-d.channel: + d.channelLock.Lock() + d.channel = make(chan struct{}) + d.channelLock.Unlock() + default: + } + + if deadline.IsZero() { + return nil + } + + timeoutIO := func() { + d.timedout.setTrue() + close(d.channel) + } + + now := time.Now() + duration := deadline.Sub(now) + if deadline.After(now) { + // Deadline is in the future, set a timer to wait + d.timer = time.AfterFunc(duration, timeoutIO) + } else { + // Deadline is in the past. Cancel all pending IO now. + timeoutIO() + } + return nil +} diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go index f85f4a9f2..0ee2641ff 100644 --- a/vendor/github.com/Microsoft/go-winio/pipe.go +++ b/vendor/github.com/Microsoft/go-winio/pipe.go @@ -2,9 +2,6 @@ package winio -// #include -import "C" - import ( "errors" "io" @@ -21,10 +18,12 @@ import ( //sys waitNamedPipe(name string, timeout uint32) (err error) = WaitNamedPipeW //sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo //sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW +//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc +//sys copyMemory(dst uintptr, src uintptr, length uint32) = RtlCopyMemory type securityAttributes struct { Length uint32 - SecurityDescriptor unsafe.Pointer + SecurityDescriptor uintptr InheritHandle uint32 } @@ -237,8 +236,10 @@ func makeServerPipeHandle(path string, securityDescriptor []byte, c *PipeConfig, sa := &securityAttributes{} sa.Length = uint32(unsafe.Sizeof(*sa)) if securityDescriptor != nil { - sa.SecurityDescriptor = C.CBytes(securityDescriptor) - defer C.free(sa.SecurityDescriptor) + len := uint32(len(securityDescriptor)) + sa.SecurityDescriptor = localAlloc(0, len) + defer localFree(sa.SecurityDescriptor) + copyMemory(sa.SecurityDescriptor, uintptr(unsafe.Pointer(&securityDescriptor[0])), len) } h, err := createNamedPipe(path, flags, mode, cPIPE_UNLIMITED_INSTANCES, uint32(c.OutputBufferSize), uint32(c.InputBufferSize), 0, sa) if err != nil { @@ -367,7 +368,7 @@ func connectPipe(p *win32File) error { return err } err = connectNamedPipe(p.handle, &c.o) - _, err = p.asyncIo(c, time.Time{}, 0, err) + _, err = p.asyncIo(c, nil, 0, err) if err != nil && err != cERROR_PIPE_CONNECTED { return err } diff --git a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go index 039daaac6..857122a3e 100644 --- a/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/go-winio/zsyscall_windows.go @@ -52,6 +52,8 @@ var ( procWaitNamedPipeW = modkernel32.NewProc("WaitNamedPipeW") procGetNamedPipeInfo = modkernel32.NewProc("GetNamedPipeInfo") procGetNamedPipeHandleStateW = modkernel32.NewProc("GetNamedPipeHandleStateW") + procLocalAlloc = modkernel32.NewProc("LocalAlloc") + procRtlCopyMemory = modkernel32.NewProc("RtlCopyMemory") procLookupAccountNameW = modadvapi32.NewProc("LookupAccountNameW") procConvertSidToStringSidW = modadvapi32.NewProc("ConvertSidToStringSidW") procConvertStringSecurityDescriptorToSecurityDescriptorW = modadvapi32.NewProc("ConvertStringSecurityDescriptorToSecurityDescriptorW") @@ -228,6 +230,17 @@ func getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *u return } +func localAlloc(uFlags uint32, length uint32) (ptr uintptr) { + r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(uFlags), uintptr(length), 0) + ptr = uintptr(r0) + return +} + +func copyMemory(dst uintptr, src uintptr, length uint32) { + syscall.Syscall(procRtlCopyMemory.Addr(), 3, uintptr(dst), uintptr(src), uintptr(length)) + return +} + func lookupAccountName(systemName *uint16, accountName string, sid *byte, sidSize *uint32, refDomain *uint16, refDomainSize *uint32, sidNameUse *uint32) (err error) { var _p0 *uint16 _p0, err = syscall.UTF16PtrFromString(accountName) diff --git a/vendor/vendor.json b/vendor/vendor.json index a301dea73..90c734744 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -25,10 +25,10 @@ "revisionTime": "2016-08-22T16:14:30Z" }, { - "checksumSHA1": "A6wxSCZcKyTwZP9b7ErXPZE3Ijg=", + "checksumSHA1": "Rjy2uYZkQ8Kjht6ZFU0qzm2I/kI=", "path": "github.com/Microsoft/go-winio", - "revision": "13736c32520969a64987228c21c138a4cfdb3720", - "revisionTime": "2017-05-03T19:50:31Z" + "revision": "d311c76e775b5092c023569caacdbb4e569c3243", + "revisionTime": "2017-05-08T21:01:43Z" }, { "checksumSHA1": "XeG94RjA9o/0wo9Fuw6NSRGYnjk=",