Skip to content
This repository has been archived by the owner on Feb 27, 2023. It is now read-only.

Commit

Permalink
bugfix: avoid creating dup-process of dfget server.
Browse files Browse the repository at this point in the history
Signed-off-by: zhouchencheng <zhouchencheng@bilibili.com>
  • Loading branch information
zcc35357949 committed Nov 14, 2019
1 parent 7300a6b commit f2a016c
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 0 deletions.
8 changes: 8 additions & 0 deletions dfget/core/uploader/peer_server_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ import (
"io"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync/atomic"
"time"

"github.com/dragonflyoss/Dragonfly/dfget/config"
"github.com/dragonflyoss/Dragonfly/pkg/fileutils"
"github.com/dragonflyoss/Dragonfly/version"

"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -74,6 +76,12 @@ func (pe *peerServerExecutor) StartPeerServerProcess(cfg *config.Config) (port i
return port, nil
}

fileLock := fileutils.NewFileLock(filepath.Dir(cfg.RV.MetaPath))
if err = fileLock.Lock(); err != nil {
return 0, err
}
defer fileLock.Unlock()

cmd := exec.Command(os.Args[0], "server",
"--ip", cfg.RV.LocalIP,
"--port", strconv.Itoa(cfg.RV.PeerPort),
Expand Down
78 changes: 78 additions & 0 deletions pkg/fileutils/filelock.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright The Dragonfly Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package fileutils

import (
"fmt"
"os"
"syscall"

"github.com/pkg/errors"
)

// FileLock defines a file lock implemented by syscall.Flock
type FileLock struct {
fileName string
fd *os.File
}

// NewFileLock create a FileLock instance
func NewFileLock(name string) *FileLock {
return &FileLock{
fileName: name,
}
}

// Lock locks file.
// If the file is already locked, the calling goroutine blocks until the file is unlocked.
// If lock has been invoked without unlock, lock again will return an error.
func (l *FileLock) Lock() error {
var (
fd *os.File
err error
)

if l.fd != nil {
return fmt.Errorf("file %s has already been locked", l.fileName)
}

if fd, err = os.Open(l.fileName); err != nil {
return err
}
l.fd = fd

if err := syscall.Flock(int(l.fd.Fd()), syscall.LOCK_EX); err != nil {
return errors.Wrapf(err, "file %s lock failed", l.fileName)
}
return nil
}

// Unlock unlocks file.
// If lock has not been invoked before unlock, unlock will return an error.
func (l *FileLock) Unlock() error {
if l.fd == nil {
return fmt.Errorf("file %s descriptor is nil", l.fileName)
}
fd := l.fd
l.fd = nil

defer fd.Close()
if err := syscall.Flock(int(fd.Fd()), syscall.LOCK_UN); err != nil {
return errors.Wrapf(err, "file %s unlock failed", l.fileName)
}
return nil
}
76 changes: 76 additions & 0 deletions pkg/fileutils/filelock_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/*
* Copyright The Dragonfly Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package fileutils

import (
"fmt"
"io/ioutil"
"os"
"time"

"github.com/go-check/check"
)

type FileLockTestSuite struct {
tmpDir string
flockA *FileLock
flockB *FileLock
}

func init() {
check.Suite(&FileLockTestSuite{})
}

func (s *FileLockTestSuite) SetUpSuite(c *check.C) {
tmpDir, _ := ioutil.TempDir("/tmp", "dfget-FileLockTestSuite-")
os.Create(tmpDir)
s.tmpDir = tmpDir
s.flockA = NewFileLock(tmpDir)
s.flockB = NewFileLock(tmpDir)
}

func (s *FileLockTestSuite) TearDownSuite(c *check.C) {
if s.tmpDir != "" {
if err := os.RemoveAll(s.tmpDir); err != nil {
fmt.Printf("remove path:%s error", s.tmpDir)
}
}
}

func (s *FileLockTestSuite) TestFileLock(c *check.C) {
err := s.flockA.Lock()
c.Assert(err, check.IsNil)

err = s.flockA.Lock()
c.Assert(err, check.NotNil)

err = s.flockA.Unlock()
c.Assert(err, check.IsNil)

err = s.flockA.Unlock()
c.Assert(err, check.NotNil)

s.flockA.Lock()
start := time.Now()
go func() {
time.Sleep(time.Second)
s.flockA.Unlock()
}()
s.flockB.Lock()
c.Assert(time.Since(start) >= time.Second, check.Equals, true)
s.flockB.Unlock()
}

0 comments on commit f2a016c

Please sign in to comment.