@@ -23,8 +23,9 @@ func (t TemporaryError) Error() string { return string(t) }
2323
2424// Temporary returns always true.
2525// It exists, so you can detect it via
26+ //
2627// if te, ok := err.(interface{ Temporary() bool }); ok {
27- // fmt.Println("I am a temporay error situation, so wait and retry")
28+ // fmt.Println("I am a temporary error situation, so wait and retry")
2829// }
2930func (t TemporaryError ) Temporary () bool { return true }
3031
@@ -43,6 +44,7 @@ func New(path string) (Lockfile, error) {
4344 if ! filepath .IsAbs (path ) {
4445 return Lockfile ("" ), ErrNeedAbsPath
4546 }
47+
4648 return Lockfile (path ), nil
4749}
4850
@@ -61,6 +63,7 @@ func (l Lockfile) GetOwner() (*os.Process, error) {
6163 if err != nil {
6264 return nil , err
6365 }
66+
6467 running , err := isRunning (pid )
6568 if err != nil {
6669 return nil , err
@@ -71,10 +74,11 @@ func (l Lockfile) GetOwner() (*os.Process, error) {
7174 if err != nil {
7275 return nil , err
7376 }
77+
7478 return proc , nil
7579 }
76- return nil , ErrDeadOwner
7780
81+ return nil , ErrDeadOwner
7882}
7983
8084// TryLock tries to own the lock.
@@ -91,34 +95,38 @@ func (l Lockfile) TryLock() error {
9195 panic (ErrNeedAbsPath )
9296 }
9397
94- tmplock , err := ioutil . TempFile ( filepath . Dir ( name ), "" )
98+ tmplock , cleanup , err := makePidFile ( name , os . Getpid () )
9599 if err != nil {
96100 return err
97101 }
98102
99- cleanup := func () {
100- _ = tmplock .Close ()
101- _ = os .Remove (tmplock .Name ())
102- }
103103 defer cleanup ()
104104
105- if err := writePidLine (tmplock , os .Getpid ()); err != nil {
106- return err
105+ // EEXIST and similar error codes, caught by os.IsExist, are intentionally ignored,
106+ // as it means that someone was faster creating this link
107+ // and ignoring this kind of error is part of the algorithm.
108+ // Then we will probably fail the pid owner check later, if this process is still alive.
109+ // We cannot ignore ALL errors, since failure to support hard links, disk full
110+ // as well as many other errors can happen to a filesystem operation
111+ // and we really want to abort on those.
112+ if err := os .Link (tmplock , name ); err != nil {
113+ if ! os .IsExist (err ) {
114+ return err
115+ }
107116 }
108117
109- // return value intentionally ignored, as ignoring it is part of the algorithm
110- _ = os .Link (tmplock .Name (), name )
111-
112- fiTmp , err := os .Lstat (tmplock .Name ())
118+ fiTmp , err := os .Lstat (tmplock )
113119 if err != nil {
114120 return err
115121 }
122+
116123 fiLock , err := os .Lstat (name )
117124 if err != nil {
118125 // tell user that a retry would be a good idea
119126 if os .IsNotExist (err ) {
120127 return ErrNotExist
121128 }
129+
122130 return err
123131 }
124132
@@ -155,7 +163,7 @@ func (l Lockfile) TryLock() error {
155163 return l .TryLock ()
156164}
157165
158- // Unlock a lock again, if we owned it. Returns any error that happend during release of lock.
166+ // Unlock a lock again, if we owned it. Returns any error that happened during release of lock.
159167func (l Lockfile ) Unlock () error {
160168 proc , err := l .GetOwner ()
161169 switch err {
@@ -179,11 +187,6 @@ func (l Lockfile) Unlock() error {
179187 }
180188}
181189
182- func writePidLine (w io.Writer , pid int ) error {
183- _ , err := io .WriteString (w , fmt .Sprintf ("%d\n " , pid ))
184- return err
185- }
186-
187190func scanPidLine (content []byte ) (int , error ) {
188191 if len (content ) == 0 {
189192 return 0 , ErrInvalidPid
@@ -197,5 +200,25 @@ func scanPidLine(content []byte) (int, error) {
197200 if pid <= 0 {
198201 return 0 , ErrInvalidPid
199202 }
203+
200204 return pid , nil
201205}
206+
207+ func makePidFile (name string , pid int ) (tmpname string , cleanup func (), err error ) {
208+ tmplock , err := ioutil .TempFile (filepath .Dir (name ), filepath .Base (name )+ "." )
209+ if err != nil {
210+ return "" , nil , err
211+ }
212+
213+ cleanup = func () {
214+ _ = tmplock .Close ()
215+ _ = os .Remove (tmplock .Name ())
216+ }
217+
218+ if _ , err := io .WriteString (tmplock , fmt .Sprintf ("%d\n " , pid )); err != nil {
219+ cleanup () // Do cleanup here, so call doesn't have to.
220+ return "" , nil , err
221+ }
222+
223+ return tmplock .Name (), cleanup , nil
224+ }
0 commit comments