File: UnfairLock.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (120 lines) | stat: -rw-r--r-- 3,203 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
// RUN: %empty-directory(%t)
// RUN: %target-build-swift -enable-experimental-feature RawLayout -enable-experimental-feature BuiltinModule %s -o %t/a.out
// RUN: %target-codesign %t/a.out
// RUN: %target-run %t/a.out | %FileCheck %s

// REQUIRES: OS=macosx
// REQUIRES: executable_test

import Builtin
import Darwin

@_rawLayout(like: os_unfair_lock_s)
struct UnfairLock: ~Copyable, @unchecked Sendable {
    // TODO: Clang importer can't handle the OS_UNFAIR_LOCK_INIT macro definition
    private static let OS_UNFAIR_LOCK_INIT = os_unfair_lock_s()

    // The lock is at a stable address as long as someone is borrowing it.
    // If the address is gotten, it should only be used within the scope
    // of a borrowing method.
    @inline(__always)
    private var _address: os_unfair_lock_t {
        os_unfair_lock_t(Builtin.addressOfBorrow(self))
    }

    @inline(__always)
    init() {
        _address.initialize(to: UnfairLock.OS_UNFAIR_LOCK_INIT)
    }

    @inline(__always)
    borrowing func withLock<R>(_ body: () throws -> R) rethrows -> R {
        let address = _address
        os_unfair_lock_lock(address)
        defer { os_unfair_lock_unlock(address) }

        return try body()
    }
}

final class Locked<T>: @unchecked Sendable {
    private let lock = UnfairLock()

    // Don't need exclusivity checking since accesses always go through the
    // lock
    @exclusivity(unchecked)
    private var value: T

    init(initialValue: T) {
        self.value = consume initialValue
    }

    func withLock<R>(_ body: (inout T) throws -> R) rethrows -> R {
        return try lock.withLock { try body(&value) }
    }
}

let myString = Locked(initialValue: "")

@Sendable
func thread1(_: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? {
    usleep(1)
    myString.withLock { $0 += "apple\n" }
    usleep(1)
    myString.withLock { $0 += "banana\n" }
    usleep(1)
    myString.withLock { $0 += "grapefruit\n" }
    return nil
}

@Sendable
func thread2(_: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? {
    usleep(1)
    myString.withLock { $0 += "BRUSSELS SPROUTS\n" }
    usleep(1)
    myString.withLock { $0 += "CAULIFLOWER\n" }
    usleep(1)
    myString.withLock { $0 += "BROCCOLI\n" }
    return nil
}

@Sendable
func thread3(_: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer? {
    usleep(1)
    myString.withLock { $0 += "Croissant\n" }
    usleep(1)
    myString.withLock { $0 += "Boule\n" }
    usleep(1)
    myString.withLock { $0 += "Batard\n" }
    return nil
}

func createPthread(
    _ body: @Sendable @convention(c) (UnsafeMutableRawPointer) -> UnsafeMutableRawPointer?
) -> pthread_t? {
    var thread: pthread_t? = nil
    let r = pthread_create(&thread, nil, body, nil)
    if r != 0 {
        return nil
    }
    return thread
}

var t1 = createPthread(thread1)!
var t2 = createPthread(thread2)!
var t3 = createPthread(thread3)!

pthread_join(t1, nil)
pthread_join(t2, nil)
pthread_join(t3, nil)

// CHECK-DAG: apple
// CHECK-DAG: banana
// CHECK-DAG: grapefruit
// CHECK-DAG: BRUSSELS SPROUTS
// CHECK-DAG: CAULIFLOWER
// CHECK-DAG: BROCCOLI
// CHECK-DAG: Croissant
// CHECK-DAG: Boule
// CHECK-DAG: Batard
myString.withLock { print($0) }