Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 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
15 changes: 15 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${fileDirname}"
}
]
}
66 changes: 66 additions & 0 deletions cross_platform_lock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import (
"errors"
"fmt"
"os"
"time"

"github.com/AzureAD/microsoft-authentication-extensions-for-go/flock"
)

type CrossPlatLock struct {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

type Lock will be better
Lock.New() for initializing Lock
Directory structure - extensions main directory and then lock directory

retryNumber int

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename "retries"

retryDelayMilliseconds time.Duration

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename retryDelay


lockFile *os.File

fileLock *flock.Flock

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fLock


lockfileName string

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not used.

locked bool

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not used.

}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add internal sync.Mutex called mu.


func NewLock(lockFileName string, retryNumber int, retryDelayMilliseconds time.Duration) (CrossPlatLock, error) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have this return a pointer

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's get rid of lockFileName, use a default defined in a constructor.

Let's use this signature:

type Option func(l *Lock)

func WithRetries(n int) Option{
return func(l *Lock) {
l.retries = n
}
}

func New(options ...Option) (*Lock, error) {
l := &Lock{
...
}
for _, o := range options {
o(l)
}
...
return l, nil
}

lockfile, err := os.Create(lockFileName)
if err != nil {
return CrossPlatLock{}, err
}
return CrossPlatLock{
lockfileName: lockFileName,
retryNumber: retryNumber,
lockFile: lockfile,
fileLock: flock.New(lockfile.Name()),
}, nil
}

func (c CrossPlatLock) Lock() error {
for tryCount := 0; tryCount < c.retryNumber; tryCount++ {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add these lines:

c.mu.Lock()
defer c.mu.Unlock()

Change (c CrossPlatLock) to use a pointer

err := c.fileLock.Lock()
if err != nil {
time.Sleep(c.retryDelayMilliseconds * time.Millisecond)
continue
} else {
if c.fileLock.Locked() {
c.fileLock.Fh.WriteString(fmt.Sprintf("{%d} {%s}", os.Getpid(), os.Args[0]))
}
c.locked = true
return nil
}
}
return errors.New("Failed to acquire lock")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error text must start with lower case letter

}

func (c CrossPlatLock) UnLock() error {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add defer c.mu.Unlock()

if c.fileLock != nil {
if err := c.fileLock.Unlock(); err != nil {
return err
}
c.lockFile.Close()
if err := os.Remove(c.fileLock.Path()); err != nil {
return err
}
c.locked = false
}
return nil
}
108 changes: 108 additions & 0 deletions cross_platform_lock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
package main

import (
"bytes"
"fmt"
"log"
"os"
"strings"
"sync"
"testing"
"time"

"github.com/AzureAD/microsoft-authentication-extensions-for-go/internal"
)

func spinThreads(noOfThreads int, sleepInterval time.Duration, t *testing.T) int {
cacheFile := "cache.txt"
var wg sync.WaitGroup
wg.Add(noOfThreads)
for i := 0; i < noOfThreads; i++ {
go func(i int) {
defer wg.Done()
acquireLockAndWriteToCache(i, sleepInterval, cacheFile)
}(i)
}
wg.Wait()
t.Cleanup(func() {
if err := os.Remove(cacheFile); err != nil {
log.Println("Failed to remove cache file", err)
}
})
return validateResult(cacheFile, t)
}

func acquireLockAndWriteToCache(threadNo int, sleepInterval time.Duration, cacheFile string) {
cacheAccessor := internal.NewFileAccessor(cacheFile)
lockfileName := cacheFile + ".lockfile"
lock, err := NewLock(lockfileName, 60, 100)
if err := lock.Lock(); err != nil {
log.Println("Couldn't acquire lock", err.Error())
return
}
defer lock.UnLock()
data, err := cacheAccessor.Read()
if err != nil {
log.Println(err)
}
var buffer bytes.Buffer
buffer.Write(data)
buffer.WriteString(fmt.Sprintf("< %d \n", threadNo))
time.Sleep(sleepInterval * time.Millisecond)
buffer.WriteString(fmt.Sprintf("> %d \n", threadNo))
cacheAccessor.Write(buffer.Bytes())
}

func validateResult(cacheFile string, t *testing.T) int {
count := 0
var prevProc string = ""
var tag string
var proc string
data, err := os.ReadFile(cacheFile)
if err != nil {
log.Println(err)
}
dat := string(data)
temp := strings.Split(dat, "\n")
for _, ele := range temp {
if ele != "" {
count += 1
split := strings.Split(ele, " ")
tag = split[0]
proc = split[1]
if prevProc != "" {
if proc != prevProc {
t.Fatal("Process overlap found")
}
if tag != ">" {
t.Fatal("Process overlap found")
}
prevProc = ""

} else {
if tag != "<" {
t.Fatal("Opening bracket not found")
}
prevProc = proc
}
}
}
return count
}
func TestForNormalWorkload(t *testing.T) {
noOfThreads := 4
sleepInterval := 100
n := spinThreads(noOfThreads, time.Duration(sleepInterval), t)
if n != 4*2 {
t.Fatalf("Should not observe starvation")
}
}

func TestForHighWorkload(t *testing.T) {
noOfThreads := 80
sleepInterval := 100
n := spinThreads(noOfThreads, time.Duration(sleepInterval), t)
if n > 80*2 {
t.Fatalf("Starvation or not, we should not observe garbled payload")
}
}
24 changes: 24 additions & 0 deletions flock/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so

# Folders
_obj
_test

# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out

*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*

_testmain.go

*.exe
*.test
*.prof
10 changes: 10 additions & 0 deletions flock/.travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
language: go
go:
- 1.14.x
- 1.15.x
script: go test -v -check.vv -race ./...
sudo: false
notifications:
email:
on_success: never
on_failure: always
27 changes: 27 additions & 0 deletions flock/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
Copyright (c) 2015-2020, Tim Heckman
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.

* Neither the name of gofrs nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
41 changes: 41 additions & 0 deletions flock/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# flock
[![TravisCI Build Status](https://img.shields.io/travis/gofrs/flock/master.svg?style=flat)](https://travis-ci.org/gofrs/flock)
[![GoDoc](https://img.shields.io/badge/godoc-flock-blue.svg?style=flat)](https://godoc.org/github.com/gofrs/flock)
[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/master/LICENSE)
[![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/flock)](https://goreportcard.com/report/github.com/gofrs/flock)

`flock` implements a thread-safe sync.Locker interface for file locking. It also
includes a non-blocking TryLock() function to allow locking without blocking execution.

## License
`flock` is released under the BSD 3-Clause License. See the `LICENSE` file for more details.

## Go Compatibility
This package makes use of the `context` package that was introduced in Go 1.7. As such, this
package has an implicit dependency on Go 1.7+.

## Installation
```
go get -u github.com/gofrs/flock
```

## Usage
```Go
import "github.com/gofrs/flock"

fileLock := flock.New("/var/lock/go-lock.lock")

locked, err := fileLock.TryLock()

if err != nil {
// handle locking error
}

if locked {
// do work
fileLock.Unlock()
}
```

For more detailed usage information take a look at the package API docs on
[GoDoc](https://godoc.org/github.com/gofrs/flock).
25 changes: 25 additions & 0 deletions flock/appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
version: '{build}'

build: false
deploy: false

clone_folder: 'c:\gopath\src\github.com\gofrs\flock'

environment:
GOPATH: 'c:\gopath'
GOVERSION: '1.15'

init:
- git config --global core.autocrlf input

install:
- rmdir c:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.msi
- msiexec /i go%GOVERSION%.windows-amd64.msi /q
- set Path=c:\go\bin;c:\gopath\bin;%Path%
- go version
- go env

test_script:
- go get -t ./...
- go test -race -v ./...
Loading