~ubuntu-branches/ubuntu/saucy/monodevelop/saucy

« back to all changes in this revision

Viewing changes to external/ngit/NGit/NGit.Storage.File/LockFile.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2012-05-27 18:08:20 UTC
  • mfrom: (1.8.5) (1.5.8 sid)
  • Revision ID: package-import@ubuntu.com-20120527180820-f1ub6lhg0s50wci1
Tags: 3.0.2+dfsg-3
* [fcecfe7] Fix monodevelop-core-addins.pc.in to point to actual 
  installed location of assemblies.
* [26e1a07] DebSrc 3.0 does not support Quilt's -p parameter, so 
  manually adjust the path in the patch file.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
This code is derived from jgit (http://eclipse.org/jgit).
 
3
Copyright owners are documented in jgit's IP log.
 
4
 
 
5
This program and the accompanying materials are made available
 
6
under the terms of the Eclipse Distribution License v1.0 which
 
7
accompanies this distribution, is reproduced below, and is
 
8
available at http://www.eclipse.org/org/documents/edl-v10.php
 
9
 
 
10
All rights reserved.
 
11
 
 
12
Redistribution and use in source and binary forms, with or
 
13
without modification, are permitted provided that the following
 
14
conditions are met:
 
15
 
 
16
- Redistributions of source code must retain the above copyright
 
17
  notice, this list of conditions and the following disclaimer.
 
18
 
 
19
- Redistributions in binary form must reproduce the above
 
20
  copyright notice, this list of conditions and the following
 
21
  disclaimer in the documentation and/or other materials provided
 
22
  with the distribution.
 
23
 
 
24
- Neither the name of the Eclipse Foundation, Inc. nor the
 
25
  names of its contributors may be used to endorse or promote
 
26
  products derived from this software without specific prior
 
27
  written permission.
 
28
 
 
29
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 
30
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 
31
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 
32
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 
33
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
34
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
35
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 
36
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 
37
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 
38
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 
39
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 
40
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
41
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
42
*/
 
43
 
 
44
using System;
 
45
using System.IO;
 
46
using NGit;
 
47
using NGit.Internal;
 
48
using NGit.Storage.File;
 
49
using NGit.Util;
 
50
using Sharpen;
 
51
 
 
52
namespace NGit.Storage.File
 
53
{
 
54
        /// <summary>Git style file locking and replacement.</summary>
 
55
        /// <remarks>
 
56
        /// Git style file locking and replacement.
 
57
        /// <p>
 
58
        /// To modify a ref file Git tries to use an atomic update approach: we write the
 
59
        /// new data into a brand new file, then rename it in place over the old name.
 
60
        /// This way we can just delete the temporary file if anything goes wrong, and
 
61
        /// nothing has been damaged. To coordinate access from multiple processes at
 
62
        /// once Git tries to atomically create the new temporary file under a well-known
 
63
        /// name.
 
64
        /// </remarks>
 
65
        public class LockFile
 
66
        {
 
67
                internal static readonly string SUFFIX = ".lock";
 
68
 
 
69
                //$NON-NLS-1$
 
70
                /// <summary>Unlock the given file.</summary>
 
71
                /// <remarks>
 
72
                /// Unlock the given file.
 
73
                /// <p>
 
74
                /// This method can be used for recovering from a thrown
 
75
                /// <see cref="NGit.Errors.LockFailedException">NGit.Errors.LockFailedException</see>
 
76
                /// . This method does not validate that the lock
 
77
                /// is or is not currently held before attempting to unlock it.
 
78
                /// </remarks>
 
79
                /// <param name="file"></param>
 
80
                /// <returns>true if unlocked, false if unlocking failed</returns>
 
81
                public static bool Unlock(FilePath file)
 
82
                {
 
83
                        FilePath lockFile = GetLockFile(file);
 
84
                        int flags = FileUtils.RETRY | FileUtils.SKIP_MISSING;
 
85
                        try
 
86
                        {
 
87
                                FileUtils.Delete(lockFile, flags);
 
88
                        }
 
89
                        catch (IOException)
 
90
                        {
 
91
                        }
 
92
                        // Ignore and return whether lock file still exists
 
93
                        return !lockFile.Exists();
 
94
                }
 
95
 
 
96
                /// <summary>Get the lock file corresponding to the given file.</summary>
 
97
                /// <remarks>Get the lock file corresponding to the given file.</remarks>
 
98
                /// <param name="file"></param>
 
99
                /// <returns>lock file</returns>
 
100
                internal static FilePath GetLockFile(FilePath file)
 
101
                {
 
102
                        return new FilePath(file.GetParentFile(), file.GetName() + SUFFIX);
 
103
                }
 
104
 
 
105
                private sealed class _FilenameFilter_111 : FilenameFilter
 
106
                {
 
107
                        public _FilenameFilter_111()
 
108
                        {
 
109
                        }
 
110
 
 
111
                        public bool Accept(FilePath dir, string name)
 
112
                        {
 
113
                                return !name.EndsWith(NGit.Storage.File.LockFile.SUFFIX);
 
114
                        }
 
115
                }
 
116
 
 
117
                /// <summary>Filter to skip over active lock files when listing a directory.</summary>
 
118
                /// <remarks>Filter to skip over active lock files when listing a directory.</remarks>
 
119
                internal static readonly FilenameFilter FILTER = new _FilenameFilter_111();
 
120
 
 
121
                private readonly FilePath @ref;
 
122
 
 
123
                private readonly FilePath lck;
 
124
 
 
125
                private bool haveLck;
 
126
 
 
127
                private FileOutputStream os;
 
128
 
 
129
                private bool needSnapshot;
 
130
 
 
131
                private bool fsync;
 
132
 
 
133
                private FileSnapshot commitSnapshot;
 
134
 
 
135
                private readonly FS fs;
 
136
 
 
137
                /// <summary>Create a new lock for any file.</summary>
 
138
                /// <remarks>Create a new lock for any file.</remarks>
 
139
                /// <param name="f">the file that will be locked.</param>
 
140
                /// <param name="fs">
 
141
                /// the file system abstraction which will be necessary to perform
 
142
                /// certain file system operations.
 
143
                /// </param>
 
144
                public LockFile(FilePath f, FS fs)
 
145
                {
 
146
                        @ref = f;
 
147
                        lck = GetLockFile(@ref);
 
148
                        this.fs = fs;
 
149
                }
 
150
 
 
151
                /// <summary>Try to establish the lock.</summary>
 
152
                /// <remarks>Try to establish the lock.</remarks>
 
153
                /// <returns>
 
154
                /// true if the lock is now held by the caller; false if it is held
 
155
                /// by someone else.
 
156
                /// </returns>
 
157
                /// <exception cref="System.IO.IOException">
 
158
                /// the temporary output file could not be created. The caller
 
159
                /// does not hold the lock.
 
160
                /// </exception>
 
161
                public virtual bool Lock()
 
162
                {
 
163
                        FileUtils.Mkdirs(lck.GetParentFile(), true);
 
164
                        if (lck.CreateNewFile())
 
165
                        {
 
166
                                haveLck = true;
 
167
                                try
 
168
                                {
 
169
                                        os = new FileOutputStream(lck);
 
170
                                }
 
171
                                catch (IOException ioe)
 
172
                                {
 
173
                                        Unlock();
 
174
                                        throw;
 
175
                                }
 
176
                        }
 
177
                        return haveLck;
 
178
                }
 
179
 
 
180
                /// <summary>Try to establish the lock for appending.</summary>
 
181
                /// <remarks>Try to establish the lock for appending.</remarks>
 
182
                /// <returns>
 
183
                /// true if the lock is now held by the caller; false if it is held
 
184
                /// by someone else.
 
185
                /// </returns>
 
186
                /// <exception cref="System.IO.IOException">
 
187
                /// the temporary output file could not be created. The caller
 
188
                /// does not hold the lock.
 
189
                /// </exception>
 
190
                public virtual bool LockForAppend()
 
191
                {
 
192
                        if (!Lock())
 
193
                        {
 
194
                                return false;
 
195
                        }
 
196
                        CopyCurrentContent();
 
197
                        return true;
 
198
                }
 
199
 
 
200
                /// <summary>Copy the current file content into the temporary file.</summary>
 
201
                /// <remarks>
 
202
                /// Copy the current file content into the temporary file.
 
203
                /// <p>
 
204
                /// This method saves the current file content by inserting it into the
 
205
                /// temporary file, so that the caller can safely append rather than replace
 
206
                /// the primary file.
 
207
                /// <p>
 
208
                /// This method does nothing if the current file does not exist, or exists
 
209
                /// but is empty.
 
210
                /// </remarks>
 
211
                /// <exception cref="System.IO.IOException">
 
212
                /// the temporary file could not be written, or a read error
 
213
                /// occurred while reading from the current file. The lock is
 
214
                /// released before throwing the underlying IO exception to the
 
215
                /// caller.
 
216
                /// </exception>
 
217
                /// <exception cref="Sharpen.RuntimeException">
 
218
                /// the temporary file could not be written. The lock is released
 
219
                /// before throwing the underlying exception to the caller.
 
220
                /// </exception>
 
221
                public virtual void CopyCurrentContent()
 
222
                {
 
223
                        RequireLock();
 
224
                        try
 
225
                        {
 
226
                                FileInputStream fis = new FileInputStream(@ref);
 
227
                                try
 
228
                                {
 
229
                                        if (fsync)
 
230
                                        {
 
231
                                                FileChannel @in = fis.GetChannel();
 
232
                                                long pos = 0;
 
233
                                                long cnt = @in.Size();
 
234
                                                while (0 < cnt)
 
235
                                                {
 
236
                                                        long r = os.GetChannel().TransferFrom(@in, pos, cnt);
 
237
                                                        pos += r;
 
238
                                                        cnt -= r;
 
239
                                                }
 
240
                                        }
 
241
                                        else
 
242
                                        {
 
243
                                                byte[] buf = new byte[2048];
 
244
                                                int r;
 
245
                                                while ((r = fis.Read(buf)) >= 0)
 
246
                                                {
 
247
                                                        os.Write(buf, 0, r);
 
248
                                                }
 
249
                                        }
 
250
                                }
 
251
                                finally
 
252
                                {
 
253
                                        fis.Close();
 
254
                                }
 
255
                        }
 
256
                        catch (FileNotFoundException)
 
257
                        {
 
258
                        }
 
259
                        catch (IOException ioe)
 
260
                        {
 
261
                                // Don't worry about a file that doesn't exist yet, it
 
262
                                // conceptually has no current content to copy.
 
263
                                //
 
264
                                Unlock();
 
265
                                throw;
 
266
                        }
 
267
                        catch (RuntimeException ioe)
 
268
                        {
 
269
                                Unlock();
 
270
                                throw;
 
271
                        }
 
272
                        catch (Error ioe)
 
273
                        {
 
274
                                Unlock();
 
275
                                throw;
 
276
                        }
 
277
                }
 
278
 
 
279
                /// <summary>Write an ObjectId and LF to the temporary file.</summary>
 
280
                /// <remarks>Write an ObjectId and LF to the temporary file.</remarks>
 
281
                /// <param name="id">
 
282
                /// the id to store in the file. The id will be written in hex,
 
283
                /// followed by a sole LF.
 
284
                /// </param>
 
285
                /// <exception cref="System.IO.IOException">
 
286
                /// the temporary file could not be written. The lock is released
 
287
                /// before throwing the underlying IO exception to the caller.
 
288
                /// </exception>
 
289
                /// <exception cref="Sharpen.RuntimeException">
 
290
                /// the temporary file could not be written. The lock is released
 
291
                /// before throwing the underlying exception to the caller.
 
292
                /// </exception>
 
293
                public virtual void Write(ObjectId id)
 
294
                {
 
295
                        byte[] buf = new byte[Constants.OBJECT_ID_STRING_LENGTH + 1];
 
296
                        id.CopyTo(buf, 0);
 
297
                        buf[Constants.OBJECT_ID_STRING_LENGTH] = (byte)('\n');
 
298
                        Write(buf);
 
299
                }
 
300
 
 
301
                /// <summary>Write arbitrary data to the temporary file.</summary>
 
302
                /// <remarks>Write arbitrary data to the temporary file.</remarks>
 
303
                /// <param name="content">
 
304
                /// the bytes to store in the temporary file. No additional bytes
 
305
                /// are added, so if the file must end with an LF it must appear
 
306
                /// at the end of the byte array.
 
307
                /// </param>
 
308
                /// <exception cref="System.IO.IOException">
 
309
                /// the temporary file could not be written. The lock is released
 
310
                /// before throwing the underlying IO exception to the caller.
 
311
                /// </exception>
 
312
                /// <exception cref="Sharpen.RuntimeException">
 
313
                /// the temporary file could not be written. The lock is released
 
314
                /// before throwing the underlying exception to the caller.
 
315
                /// </exception>
 
316
                public virtual void Write(byte[] content)
 
317
                {
 
318
                        RequireLock();
 
319
                        try
 
320
                        {
 
321
                                if (fsync)
 
322
                                {
 
323
                                        FileChannel fc = os.GetChannel();
 
324
                                        ByteBuffer buf = ByteBuffer.Wrap(content);
 
325
                                        while (0 < buf.Remaining())
 
326
                                        {
 
327
                                                fc.Write(buf);
 
328
                                        }
 
329
                                        fc.Force(true);
 
330
                                }
 
331
                                else
 
332
                                {
 
333
                                        os.Write(content);
 
334
                                }
 
335
                                os.Close();
 
336
                                os = null;
 
337
                        }
 
338
                        catch (IOException ioe)
 
339
                        {
 
340
                                Unlock();
 
341
                                throw;
 
342
                        }
 
343
                        catch (RuntimeException ioe)
 
344
                        {
 
345
                                Unlock();
 
346
                                throw;
 
347
                        }
 
348
                        catch (Error ioe)
 
349
                        {
 
350
                                Unlock();
 
351
                                throw;
 
352
                        }
 
353
                }
 
354
 
 
355
                /// <summary>Obtain the direct output stream for this lock.</summary>
 
356
                /// <remarks>
 
357
                /// Obtain the direct output stream for this lock.
 
358
                /// <p>
 
359
                /// The stream may only be accessed once, and only after
 
360
                /// <see cref="Lock()">Lock()</see>
 
361
                /// has
 
362
                /// been successfully invoked and returned true. Callers must close the
 
363
                /// stream prior to calling
 
364
                /// <see cref="Commit()">Commit()</see>
 
365
                /// to commit the change.
 
366
                /// </remarks>
 
367
                /// <returns>a stream to write to the new file. The stream is unbuffered.</returns>
 
368
                public virtual OutputStream GetOutputStream()
 
369
                {
 
370
                        RequireLock();
 
371
                        OutputStream @out;
 
372
                        if (fsync)
 
373
                        {
 
374
                                @out = Channels.NewOutputStream(os.GetChannel());
 
375
                        }
 
376
                        else
 
377
                        {
 
378
                                @out = os;
 
379
                        }
 
380
                        return new _OutputStream_323(this, @out);
 
381
                }
 
382
 
 
383
                private sealed class _OutputStream_323 : OutputStream
 
384
                {
 
385
                        public _OutputStream_323(LockFile _enclosing, OutputStream @out)
 
386
                        {
 
387
                                this._enclosing = _enclosing;
 
388
                                this.@out = @out;
 
389
                        }
 
390
 
 
391
                        /// <exception cref="System.IO.IOException"></exception>
 
392
                        public override void Write(byte[] b, int o, int n)
 
393
                        {
 
394
                                @out.Write(b, o, n);
 
395
                        }
 
396
 
 
397
                        /// <exception cref="System.IO.IOException"></exception>
 
398
                        public override void Write(byte[] b)
 
399
                        {
 
400
                                @out.Write(b);
 
401
                        }
 
402
 
 
403
                        /// <exception cref="System.IO.IOException"></exception>
 
404
                        public override void Write(int b)
 
405
                        {
 
406
                                @out.Write(b);
 
407
                        }
 
408
 
 
409
                        /// <exception cref="System.IO.IOException"></exception>
 
410
                        public override void Close()
 
411
                        {
 
412
                                try
 
413
                                {
 
414
                                        if (this._enclosing.fsync)
 
415
                                        {
 
416
                                                this._enclosing.os.GetChannel().Force(true);
 
417
                                        }
 
418
                                        @out.Close();
 
419
                                        this._enclosing.os = null;
 
420
                                }
 
421
                                catch (IOException ioe)
 
422
                                {
 
423
                                        this._enclosing.Unlock();
 
424
                                        throw;
 
425
                                }
 
426
                                catch (RuntimeException ioe)
 
427
                                {
 
428
                                        this._enclosing.Unlock();
 
429
                                        throw;
 
430
                                }
 
431
                                catch (Error ioe)
 
432
                                {
 
433
                                        this._enclosing.Unlock();
 
434
                                        throw;
 
435
                                }
 
436
                        }
 
437
 
 
438
                        private readonly LockFile _enclosing;
 
439
 
 
440
                        private readonly OutputStream @out;
 
441
                }
 
442
 
 
443
                private void RequireLock()
 
444
                {
 
445
                        if (os == null)
 
446
                        {
 
447
                                Unlock();
 
448
                                throw new InvalidOperationException(MessageFormat.Format(JGitText.Get().lockOnNotHeld
 
449
                                        , @ref));
 
450
                        }
 
451
                }
 
452
 
 
453
                /// <summary>
 
454
                /// Request that
 
455
                /// <see cref="Commit()">Commit()</see>
 
456
                /// remember modification time.
 
457
                /// <p>
 
458
                /// This is an alias for
 
459
                /// <code>setNeedSnapshot(true)</code>
 
460
                /// .
 
461
                /// </summary>
 
462
                /// <param name="on">true if the commit method must remember the modification time.</param>
 
463
                public virtual void SetNeedStatInformation(bool on)
 
464
                {
 
465
                        SetNeedSnapshot(on);
 
466
                }
 
467
 
 
468
                /// <summary>
 
469
                /// Request that
 
470
                /// <see cref="Commit()">Commit()</see>
 
471
                /// remember the
 
472
                /// <see cref="FileSnapshot">FileSnapshot</see>
 
473
                /// .
 
474
                /// </summary>
 
475
                /// <param name="on">true if the commit method must remember the FileSnapshot.</param>
 
476
                public virtual void SetNeedSnapshot(bool on)
 
477
                {
 
478
                        needSnapshot = on;
 
479
                }
 
480
 
 
481
                /// <summary>
 
482
                /// Request that
 
483
                /// <see cref="Commit()">Commit()</see>
 
484
                /// force dirty data to the drive.
 
485
                /// </summary>
 
486
                /// <param name="on">true if dirty data should be forced to the drive.</param>
 
487
                public virtual void SetFSync(bool on)
 
488
                {
 
489
                        fsync = on;
 
490
                }
 
491
 
 
492
                /// <summary>Wait until the lock file information differs from the old file.</summary>
 
493
                /// <remarks>
 
494
                /// Wait until the lock file information differs from the old file.
 
495
                /// <p>
 
496
                /// This method tests the last modification date. If both are the same, this
 
497
                /// method sleeps until it can force the new lock file's modification date to
 
498
                /// be later than the target file.
 
499
                /// </remarks>
 
500
                /// <exception cref="System.Exception">
 
501
                /// the thread was interrupted before the last modified date of
 
502
                /// the lock file was different from the last modified date of
 
503
                /// the target file.
 
504
                /// </exception>
 
505
                public virtual void WaitForStatChange()
 
506
                {
 
507
                        FileSnapshot o = FileSnapshot.Save(@ref);
 
508
                        FileSnapshot n = FileSnapshot.Save(lck);
 
509
                        while (o.Equals(n))
 
510
                        {
 
511
                                Sharpen.Thread.Sleep(25);
 
512
                                lck.SetLastModified(Runtime.CurrentTimeMillis());
 
513
                                n = FileSnapshot.Save(lck);
 
514
                        }
 
515
                }
 
516
 
 
517
                /// <summary>Commit this change and release the lock.</summary>
 
518
                /// <remarks>
 
519
                /// Commit this change and release the lock.
 
520
                /// <p>
 
521
                /// If this method fails (returns false) the lock is still released.
 
522
                /// </remarks>
 
523
                /// <returns>
 
524
                /// true if the commit was successful and the file contains the new
 
525
                /// data; false if the commit failed and the file remains with the
 
526
                /// old data.
 
527
                /// </returns>
 
528
                /// <exception cref="System.InvalidOperationException">the lock is not held.</exception>
 
529
                public virtual bool Commit()
 
530
                {
 
531
                        if (os != null)
 
532
                        {
 
533
                                Unlock();
 
534
                                throw new InvalidOperationException(MessageFormat.Format(JGitText.Get().lockOnNotClosed
 
535
                                        , @ref));
 
536
                        }
 
537
                        SaveStatInformation();
 
538
                        if (lck.RenameTo(@ref))
 
539
                        {
 
540
                                return true;
 
541
                        }
 
542
                        if (!@ref.Exists() || DeleteRef())
 
543
                        {
 
544
                                if (RenameLock())
 
545
                                {
 
546
                                        return true;
 
547
                                }
 
548
                        }
 
549
                        Unlock();
 
550
                        return false;
 
551
                }
 
552
 
 
553
                private bool DeleteRef()
 
554
                {
 
555
                        if (!fs.RetryFailedLockFileCommit())
 
556
                        {
 
557
                                return @ref.Delete();
 
558
                        }
 
559
                        // File deletion fails on windows if another thread is
 
560
                        // concurrently reading the same file. So try a few times.
 
561
                        //
 
562
                        for (int attempts = 0; attempts < 10; attempts++)
 
563
                        {
 
564
                                if (@ref.Delete())
 
565
                                {
 
566
                                        return true;
 
567
                                }
 
568
                                try
 
569
                                {
 
570
                                        Sharpen.Thread.Sleep(100);
 
571
                                }
 
572
                                catch (Exception)
 
573
                                {
 
574
                                        return false;
 
575
                                }
 
576
                        }
 
577
                        return false;
 
578
                }
 
579
 
 
580
                private bool RenameLock()
 
581
                {
 
582
                        if (!fs.RetryFailedLockFileCommit())
 
583
                        {
 
584
                                return lck.RenameTo(@ref);
 
585
                        }
 
586
                        // File renaming fails on windows if another thread is
 
587
                        // concurrently reading the same file. So try a few times.
 
588
                        //
 
589
                        for (int attempts = 0; attempts < 10; attempts++)
 
590
                        {
 
591
                                if (lck.RenameTo(@ref))
 
592
                                {
 
593
                                        return true;
 
594
                                }
 
595
                                try
 
596
                                {
 
597
                                        Sharpen.Thread.Sleep(100);
 
598
                                }
 
599
                                catch (Exception)
 
600
                                {
 
601
                                        return false;
 
602
                                }
 
603
                        }
 
604
                        return false;
 
605
                }
 
606
 
 
607
                private void SaveStatInformation()
 
608
                {
 
609
                        if (needSnapshot)
 
610
                        {
 
611
                                commitSnapshot = FileSnapshot.Save(lck);
 
612
                        }
 
613
                }
 
614
 
 
615
                /// <summary>Get the modification time of the output file when it was committed.</summary>
 
616
                /// <remarks>Get the modification time of the output file when it was committed.</remarks>
 
617
                /// <returns>modification time of the lock file right before we committed it.</returns>
 
618
                public virtual long GetCommitLastModified()
 
619
                {
 
620
                        return commitSnapshot.LastModified();
 
621
                }
 
622
 
 
623
                /// <returns>
 
624
                /// get the
 
625
                /// <see cref="FileSnapshot">FileSnapshot</see>
 
626
                /// just before commit.
 
627
                /// </returns>
 
628
                public virtual FileSnapshot GetCommitSnapshot()
 
629
                {
 
630
                        return commitSnapshot;
 
631
                }
 
632
 
 
633
                /// <summary>Unlock this file and abort this change.</summary>
 
634
                /// <remarks>
 
635
                /// Unlock this file and abort this change.
 
636
                /// <p>
 
637
                /// The temporary file (if created) is deleted before returning.
 
638
                /// </remarks>
 
639
                public virtual void Unlock()
 
640
                {
 
641
                        if (os != null)
 
642
                        {
 
643
                                try
 
644
                                {
 
645
                                        os.Close();
 
646
                                }
 
647
                                catch (IOException)
 
648
                                {
 
649
                                }
 
650
                                // Ignore this
 
651
                                os = null;
 
652
                        }
 
653
                        if (haveLck)
 
654
                        {
 
655
                                haveLck = false;
 
656
                                try
 
657
                                {
 
658
                                        FileUtils.Delete(lck, FileUtils.RETRY);
 
659
                                }
 
660
                                catch (IOException)
 
661
                                {
 
662
                                }
 
663
                        }
 
664
                }
 
665
 
 
666
                // couldn't delete the file even after retry.
 
667
                public override string ToString()
 
668
                {
 
669
                        return "LockFile[" + lck + ", haveLck=" + haveLck + "]";
 
670
                }
 
671
        }
 
672
}