github.com/nevins-b/terraform@v0.3.8-0.20170215184714-bbae22007d5a/state/local_lock_windows.go (about)

     1  // +build windows
     2  
     3  package state
     4  
     5  import (
     6  	"math"
     7  	"os"
     8  	"sync"
     9  	"syscall"
    10  	"unsafe"
    11  )
    12  
    13  type stateLock struct {
    14  	handle syscall.Handle
    15  }
    16  
    17  var (
    18  	modkernel32      = syscall.NewLazyDLL("kernel32.dll")
    19  	procLockFileEx   = modkernel32.NewProc("LockFileEx")
    20  	procCreateEventW = modkernel32.NewProc("CreateEventW")
    21  
    22  	lockedFilesMu sync.Mutex
    23  	lockedFiles   = map[*os.File]syscall.Handle{}
    24  )
    25  
    26  const (
    27  	// dwFlags defined for LockFileEx
    28  	// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
    29  	_LOCKFILE_FAIL_IMMEDIATELY = 1
    30  	_LOCKFILE_EXCLUSIVE_LOCK   = 2
    31  )
    32  
    33  func (s *LocalState) lock() error {
    34  	lockedFilesMu.Lock()
    35  	defer lockedFilesMu.Unlock()
    36  
    37  	name, err := syscall.UTF16PtrFromString(s.PathOut)
    38  	if err != nil {
    39  		return err
    40  	}
    41  
    42  	handle, err := syscall.CreateFile(
    43  		name,
    44  		syscall.GENERIC_READ|syscall.GENERIC_WRITE,
    45  		// since this file is already open in out process, we need shared
    46  		// access here for this call.
    47  		syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE,
    48  		nil,
    49  		syscall.OPEN_EXISTING,
    50  		syscall.FILE_ATTRIBUTE_NORMAL,
    51  		0,
    52  	)
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	lockedFiles[s.stateFileOut] = handle
    58  
    59  	// even though we're failing immediately, an overlapped event structure is
    60  	// required
    61  	ol, err := newOverlapped()
    62  	if err != nil {
    63  		return err
    64  	}
    65  	defer syscall.CloseHandle(ol.HEvent)
    66  
    67  	return lockFileEx(
    68  		handle,
    69  		_LOCKFILE_EXCLUSIVE_LOCK|_LOCKFILE_FAIL_IMMEDIATELY,
    70  		0,              // reserved
    71  		0,              // bytes low
    72  		math.MaxUint32, // bytes high
    73  		ol,
    74  	)
    75  }
    76  
    77  func (s *LocalState) unlock() error {
    78  	lockedFilesMu.Lock()
    79  	defer lockedFilesMu.Unlock()
    80  
    81  	handle, ok := lockedFiles[s.stateFileOut]
    82  	if !ok {
    83  		// we allow multiple Unlock calls
    84  		return nil
    85  	}
    86  	delete(lockedFiles, s.stateFileOut)
    87  	return syscall.Close(handle)
    88  }
    89  
    90  func lockFileEx(h syscall.Handle, flags, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
    91  	r1, _, e1 := syscall.Syscall6(
    92  		procLockFileEx.Addr(),
    93  		6,
    94  		uintptr(h),
    95  		uintptr(flags),
    96  		uintptr(reserved),
    97  		uintptr(locklow),
    98  		uintptr(lockhigh),
    99  		uintptr(unsafe.Pointer(ol)),
   100  	)
   101  	if r1 == 0 {
   102  		if e1 != 0 {
   103  			err = error(e1)
   104  		} else {
   105  			err = syscall.EINVAL
   106  		}
   107  	}
   108  	return
   109  }
   110  
   111  // newOverlapped creates a structure used to track asynchronous
   112  // I/O requests that have been issued.
   113  func newOverlapped() (*syscall.Overlapped, error) {
   114  	event, err := createEvent(nil, true, false, nil)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	return &syscall.Overlapped{HEvent: event}, nil
   119  }
   120  
   121  func createEvent(sa *syscall.SecurityAttributes, manualReset bool, initialState bool, name *uint16) (handle syscall.Handle, err error) {
   122  	var _p0 uint32
   123  	if manualReset {
   124  		_p0 = 1
   125  	}
   126  	var _p1 uint32
   127  	if initialState {
   128  		_p1 = 1
   129  	}
   130  
   131  	r0, _, e1 := syscall.Syscall6(
   132  		procCreateEventW.Addr(),
   133  		4,
   134  		uintptr(unsafe.Pointer(sa)),
   135  		uintptr(_p0),
   136  		uintptr(_p1),
   137  		uintptr(unsafe.Pointer(name)),
   138  		0,
   139  		0,
   140  	)
   141  	handle = syscall.Handle(r0)
   142  	if handle == syscall.InvalidHandle {
   143  		if e1 != 0 {
   144  			err = error(e1)
   145  		} else {
   146  			err = syscall.EINVAL
   147  		}
   148  	}
   149  	return
   150  }