File: Lock.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (140 lines) | stat: -rw-r--r-- 4,427 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#if canImport(os)
public import os
#elseif os(Windows)
public import WinSDK
#else
public import SWBLibc
#endif

// FIXME: Replace the contents of this file with the Swift standard library's Mutex type once it's available everywhere we deploy.

/// A more efficient lock than a DispatchQueue (esp. under contention).
#if canImport(os)
public typealias Lock = OSAllocatedUnfairLock
#else
public final class Lock: @unchecked Sendable {
    #if os(Windows)
    @usableFromInline
    let mutex: UnsafeMutablePointer<SRWLOCK> = UnsafeMutablePointer.allocate(capacity: 1)
    #elseif os(FreeBSD) || os(OpenBSD)
    @usableFromInline
    let mutex: UnsafeMutablePointer<pthread_mutex_t?> = UnsafeMutablePointer.allocate(capacity: 1)
    #else
    @usableFromInline
    let mutex: UnsafeMutablePointer<pthread_mutex_t> = UnsafeMutablePointer.allocate(capacity: 1)
    #endif

    public init() {
        #if os(Windows)
        InitializeSRWLock(self.mutex)
        #else
        let err = pthread_mutex_init(self.mutex, nil)
        precondition(err == 0)
        #endif
    }

    deinit {
        #if os(Windows)
        // SRWLOCK does not need to be freed
        #else
        let err = pthread_mutex_destroy(self.mutex)
        precondition(err == 0)
        #endif
        mutex.deallocate()
    }

    @usableFromInline
    func lock() {
        #if os(Windows)
        AcquireSRWLockExclusive(self.mutex)
        #else
        let err = pthread_mutex_lock(self.mutex)
        precondition(err == 0)
        #endif
    }

    @usableFromInline
    func unlock() {
        #if os(Windows)
        ReleaseSRWLockExclusive(self.mutex)
        #else
        let err = pthread_mutex_unlock(self.mutex)
        precondition(err == 0)
        #endif
    }

    @inlinable
    public func withLock<T>(_ body: () throws -> T) rethrows -> T {
        self.lock()
        defer {
            self.unlock()
        }
        return try body()
    }
}
#endif

/// Small wrapper to provide only locked access to its value.
/// Be aware that it's not possible to share this lock for multiple data
/// instances and using multiple of those can easily lead to deadlocks.
public final class LockedValue<Value: ~Copyable> {
    @usableFromInline let lock = Lock()
    /// Don't use this from outside this class. Is internal to be inlinable.
    @usableFromInline var value: Value
    public init(_ value: consuming sending Value) {
        self.value = value
    }
}

extension LockedValue where Value: ~Copyable {
    @discardableResult @inlinable
    public borrowing func withLock<Result: ~Copyable, E: Error>(_ block: (inout sending Value) throws(E) -> sending Result) throws(E) -> sending Result {
        lock.lock()
        defer { lock.unlock() }
        return try block(&value)
    }
}

extension LockedValue: @unchecked Sendable where Value: ~Copyable {
}

extension LockedValue where Value: Sendable {
    /// Sets the value of the wrapped value to `newValue` and returns the original value.
    public func exchange(_ newValue: Value) -> Value {
        withLock {
            let old = $0
            $0 = newValue
            return old
        }
    }
}

#if canImport(Darwin)
@available(macOS, deprecated: 15.0, renamed: "Synchronization.Mutex")
@available(iOS, deprecated: 18.0, renamed: "Synchronization.Mutex")
@available(tvOS, deprecated: 18.0, renamed: "Synchronization.Mutex")
@available(watchOS, deprecated: 11.0, renamed: "Synchronization.Mutex")
@available(visionOS, deprecated: 2.0, renamed: "Synchronization.Mutex")
public typealias SWBMutex = LockedValue
#else
public import Synchronization
public typealias SWBMutex = Mutex
#endif

extension SWBMutex where Value: ~Copyable, Value == Void {
    public borrowing func withLock<Result: ~Copyable, E: Error>(_ body: () throws(E) -> sending Result) throws(E) -> sending Result {
        try withLock { _ throws(E) -> sending Result in return try body() }
    }
}