Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

flock.go 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // Copyright 2015 Tim Heckman. All rights reserved.
  2. // Use of this source code is governed by the BSD 3-Clause
  3. // license that can be found in the LICENSE file.
  4. // Package flock implements a thread-safe interface for file locking.
  5. // It also includes a non-blocking TryLock() function to allow locking
  6. // without blocking execution.
  7. //
  8. // Package flock is released under the BSD 3-Clause License. See the LICENSE file
  9. // for more details.
  10. //
  11. // While using this library, remember that the locking behaviors are not
  12. // guaranteed to be the same on each platform. For example, some UNIX-like
  13. // operating systems will transparently convert a shared lock to an exclusive
  14. // lock. If you Unlock() the flock from a location where you believe that you
  15. // have the shared lock, you may accidentally drop the exclusive lock.
  16. package flock
  17. import (
  18. "context"
  19. "os"
  20. "runtime"
  21. "sync"
  22. "time"
  23. )
  24. // Flock is the struct type to handle file locking. All fields are unexported,
  25. // with access to some of the fields provided by getter methods (Path() and Locked()).
  26. type Flock struct {
  27. path string
  28. m sync.RWMutex
  29. fh *os.File
  30. l bool
  31. r bool
  32. }
  33. // New returns a new instance of *Flock. The only parameter
  34. // it takes is the path to the desired lockfile.
  35. func New(path string) *Flock {
  36. return &Flock{path: path}
  37. }
  38. // NewFlock returns a new instance of *Flock. The only parameter
  39. // it takes is the path to the desired lockfile.
  40. //
  41. // Deprecated: Use New instead.
  42. func NewFlock(path string) *Flock {
  43. return New(path)
  44. }
  45. // Close is equivalent to calling Unlock.
  46. //
  47. // This will release the lock and close the underlying file descriptor.
  48. // It will not remove the file from disk, that's up to your application.
  49. func (f *Flock) Close() error {
  50. return f.Unlock()
  51. }
  52. // Path returns the path as provided in NewFlock().
  53. func (f *Flock) Path() string {
  54. return f.path
  55. }
  56. // Locked returns the lock state (locked: true, unlocked: false).
  57. //
  58. // Warning: by the time you use the returned value, the state may have changed.
  59. func (f *Flock) Locked() bool {
  60. f.m.RLock()
  61. defer f.m.RUnlock()
  62. return f.l
  63. }
  64. // RLocked returns the read lock state (locked: true, unlocked: false).
  65. //
  66. // Warning: by the time you use the returned value, the state may have changed.
  67. func (f *Flock) RLocked() bool {
  68. f.m.RLock()
  69. defer f.m.RUnlock()
  70. return f.r
  71. }
  72. func (f *Flock) String() string {
  73. return f.path
  74. }
  75. // TryLockContext repeatedly tries to take an exclusive lock until one of the
  76. // conditions is met: TryLock succeeds, TryLock fails with error, or Context
  77. // Done channel is closed.
  78. func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) {
  79. return tryCtx(ctx, f.TryLock, retryDelay)
  80. }
  81. // TryRLockContext repeatedly tries to take a shared lock until one of the
  82. // conditions is met: TryRLock succeeds, TryRLock fails with error, or Context
  83. // Done channel is closed.
  84. func (f *Flock) TryRLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) {
  85. return tryCtx(ctx, f.TryRLock, retryDelay)
  86. }
  87. func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Duration) (bool, error) {
  88. if ctx.Err() != nil {
  89. return false, ctx.Err()
  90. }
  91. for {
  92. if ok, err := fn(); ok || err != nil {
  93. return ok, err
  94. }
  95. select {
  96. case <-ctx.Done():
  97. return false, ctx.Err()
  98. case <-time.After(retryDelay):
  99. // try again
  100. }
  101. }
  102. }
  103. func (f *Flock) setFh() error {
  104. // open a new os.File instance
  105. // create it if it doesn't exist, and open the file read-only.
  106. flags := os.O_CREATE
  107. if runtime.GOOS == "aix" {
  108. // AIX cannot preform write-lock (ie exclusive) on a
  109. // read-only file.
  110. flags |= os.O_RDWR
  111. } else {
  112. flags |= os.O_RDONLY
  113. }
  114. fh, err := os.OpenFile(f.path, flags, os.FileMode(0600))
  115. if err != nil {
  116. return err
  117. }
  118. // set the filehandle on the struct
  119. f.fh = fh
  120. return nil
  121. }
  122. // ensure the file handle is closed if no lock is held
  123. func (f *Flock) ensureFhState() {
  124. if !f.l && !f.r && f.fh != nil {
  125. f.fh.Close()
  126. f.fh = nil
  127. }
  128. }