Emcee

Форк
0
/
FileLock.swift 
111 строк · 3.2 Кб
1
import Foundation
2

3
/// A lock that multiple processes on same host can use to restrict access to some shared resource, such as a file.
4
/// It can be locked recursively from the same thread.
5
/// Calls to `lock()` must be balanced by same number of `unlock()` calls.
6
///
7
/// - Two instances locking on the same resource won't behave as a recursive lock.
8
/// - If unbalanced `unlock()` call is detected `Error` will be thrown.
9
public final class FileLock {
10
    private let fileDescriptor: Int32
11
    private let recursiveLock = NSRecursiveLock()
12
    private var counter = 0
13
    
14
    public enum FileLockError: Error, CustomStringConvertible {
15
        case errno(Int32)
16
        case unbalancedUnlockCalls
17
        
18
        public var description: String {
19
            switch self {
20
            case .errno(let code):
21
                guard let systemDescription = strerror(code) else { return "Error: errno \(code)" }
22
                let message = String(validatingUTF8: systemDescription) ?? "Unknown Error"
23
                return "\(message) (errno \(code))"
24
            case .unbalancedUnlockCalls:
25
                return "Unbalanced number of calls: unlock() has been called more that lock()"
26
            }
27
        }
28
    }
29
    
30
    public static func named(_ name: String) throws -> FileLock {
31
        let tempFolderPath = NSTemporaryDirectory() as NSString
32
        return try FileLock(lockFilePath: tempFolderPath.appendingPathComponent(name + ".lock"))
33
    }
34

35
    public init(lockFilePath: String) throws {
36
        fileDescriptor = open(lockFilePath, O_WRONLY | O_CREAT | O_CLOEXEC, 0o666)
37
        if fileDescriptor == -1 {
38
            throw FileLockError.errno(errno)
39
        }
40
    }
41
    
42
    deinit {
43
        close(fileDescriptor)
44
    }
45
    
46
    public func lock() throws {
47
        recursiveLock.lock()
48
        defer { recursiveLock.unlock() }
49
        
50
        counter += 1
51
        if counter > 1 {
52
            return
53
        }
54
        
55
        while true {
56
            if flock(fileDescriptor, LOCK_EX) == 0 {
57
                break
58
            }
59
            // Retry if interrupted.
60
            if errno == EINTR { continue }
61
            throw FileLockError.errno(errno)
62
        }
63
    }
64
    
65
    public func unlock() throws {
66
        recursiveLock.lock()
67
        defer { recursiveLock.unlock() }
68
        
69
        guard counter > 0 else {
70
            throw FileLockError.unbalancedUnlockCalls
71
        }
72
        
73
        counter -= 1
74
        
75
        if counter == 0 {
76
            flock(fileDescriptor, LOCK_UN)
77
        }
78
    }
79
    
80
    /// Attempts to lock. If this method returns `true`, you must `unlock()`.
81
    public func tryToLock() -> Bool {
82
        recursiveLock.lock()
83
        defer { recursiveLock.unlock() }
84
        
85
        let result = flock(fileDescriptor, LOCK_EX | LOCK_NB) == 0
86
        if result {
87
            counter += 1
88
        }
89
        return result
90
    }
91
    
92
    public var isLocked: Bool {
93
        recursiveLock.lock()
94
        defer { recursiveLock.unlock() }
95
        
96
        return counter > 0
97
    }
98
    
99
    public func whileLocked<T>(work: () throws -> (T)) throws -> T {
100
        try lock()
101
        
102
        do {
103
            let result = try work()
104
            try unlock()
105
            return result
106
        } catch {
107
            try unlock()
108
            throw error
109
        }
110
    }
111
}
112

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.