Skip to content
Merged

x #1

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 9 additions & 7 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,29 @@ jobs:
destination: 'platform=macOS,name=My Mac'
- name: watchOS
destination: 'platform=watchOS Simulator,name=Apple Watch Series 11 (46mm)'
fail-fast: false
with:
runsonlabels: '["macOS", "self-hosted"]'
scheme: ThreadLocal-Package
# runsonlabels: '["macOS", "self-hosted"]'
scheme: ThreadLocal
destination: ${{ matrix.platform.destination }}
buildConfig: ${{ matrix.config }}
resultBundle: ${{ format('ThreadLocal-Package-{0}-{1}.xcresult', matrix.platform.name, matrix.config) }}
artifactname: ${{ format('ThreadLocal-Package-{0}-{1}.xcresult', matrix.platform.name, matrix.config) }}
resultBundle: ${{ format('ThreadLocal-{0}-{1}.xcresult', matrix.platform.name, matrix.config) }}
artifactname: ${{ format('ThreadLocal-{0}-{1}.xcresult', matrix.platform.name, matrix.config) }}
spm-disable-prebuilts: true
package_tests_linux:
name: Build and Test Swift Package Linux (${{ matrix.config }})
uses: StanfordBDHG/.github/.github/workflows/swift-test.yml@v2
strategy:
matrix:
config: [Debug, Release]
with:
artifact_name: ${{ format('ThreadLocal-Package-Linux-{0}.lcov', matrix.config) }}
artifact_name: ${{ format('ThreadLocal-Linux-{0}.lcov', matrix.config) }}
uploadcoveragereport:
name: Upload Coverage Report
needs: [package_tests, package_tests_linux]
uses: StanfordBDHG/.github/.github/workflows/create-and-upload-coverage-report.yml@v2
with:
coveragereports: ThreadLocal-Package-*.xcresult
coveragereports_lcov: ThreadLocal-Package-Linux-*.lcov
coveragereports: ThreadLocal-*.xcresult
coveragereports_lcov: ThreadLocal-Linux-*.lcov
secrets:
token: ${{ secrets.CODECOV_TOKEN }}
6 changes: 3 additions & 3 deletions .github/workflows/static-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ jobs:
swiftlint:
name: SwiftLint
uses: StanfordSpezi/.github/.github/workflows/swiftlint.yml@v2
markdown_link_check:
name: Markdown Link Check
uses: StanfordBDHG/.github/.github/workflows/markdown-link-check.yml@v2
# markdown_link_check:
# name: Markdown Link Check
# uses: StanfordBDHG/.github/.github/workflows/markdown-link-check.yml@v2
breaking_changes:
name: Diagnose Breaking Changes
uses: StanfordSpezi/.github/.github/workflows/breaking-changes.yml@v2
Expand Down
14 changes: 14 additions & 0 deletions .spi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#
# This source file is part of the TemplatePackage open source project
#
# SPDX-FileCopyrightText: 2022 Stanford University and the project authors (see CONTRIBUTORS.md)
#
# SPDX-License-Identifier: MIT
#

version: 1
builder:
configs:
- platform: ios
documentation_targets:
- ThreadLocal
452 changes: 452 additions & 0 deletions .swiftlint.yml

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ let package = Package(
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax")
]
),
)
]
)
38 changes: 26 additions & 12 deletions Sources/ThreadLocal/ThreadLocal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,14 @@
// SPDX-License-Identifier: MIT
//

// swiftlint:disable type_name identifier_name

import Foundation
import pthread
#if os(Linux)
import Glibc
#else
import Darwin.C
#endif


/// Manages a thread-local defined via the ``ThreadLocal()`` macro.
Expand All @@ -17,24 +23,32 @@ public final class ThreadLocal<Value>: Sendable {
nonisolated(unsafe) public private(set) var _key: pthread_key_t
public let _deallocator: Deallocator

@_unavailableFromAsync
public init(_deallocator deallocator: Deallocator = .default) {
_key = pthread_key_t()
pthread_key_create(&_key) { (ptr: UnsafeMutableRawPointer) in
unsafeBitCast(ptr, to: Unmanaged<AnyObject>.self).release()
}
self._deallocator = deallocator
}

@inlinable
var _box: _Box? {
@inlinable var _box: _Box? {
guard let ptr = pthread_getspecific(_key) else {
return nil
}
let unmanaged = Unmanaged<_Box>.fromOpaque(ptr)
return unmanaged.takeUnretainedValue()
}

@_unavailableFromAsync
public init(_deallocator deallocator: Deallocator = .default) {
_key = pthread_key_t()
_deallocator = deallocator
#if os(Linux)
let destroyFn: @convention(c) (UnsafeMutableRawPointer?) -> Void = { ptr in
if let ptr {
unsafeBitCast(ptr, to: Unmanaged<AnyObject>.self).release()
}
}
#else
let destroyFn: @convention(c) (UnsafeMutableRawPointer) -> Void = { ptr in
unsafeBitCast(ptr, to: Unmanaged<AnyObject>.self).release()
}
#endif
pthread_key_create(&_key, destroyFn)
}

@inlinable
@_unavailableFromAsync
func _makeBox(_ value: Value) -> _Box {
Expand Down
34 changes: 27 additions & 7 deletions Sources/ThreadLocalMacros/ThreadLocalMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -65,12 +65,6 @@ extension ThreadLocalMacro: AccessorMacro {
}


extension VariableDeclSyntax {
var isStatic: Bool {
modifiers.contains(where: { $0.name.trimmed.text == "static" })
}
}

extension ThreadLocalMacro: PeerMacro {
public static func expansion( // swiftlint:disable:this function_body_length cyclomatic_complexity
of node: AttributeSyntax,
Expand Down Expand Up @@ -130,8 +124,34 @@ extension ThreadLocalMacro: PeerMacro {
throw SimpleError("unexpected")
}

let attrs: AttributeSyntax? = variableDeclaration.isInlinable ? "@usableFromInline" : nil
let modifiers = DeclModifierSyntax(name: variableDeclaration.isInlinable ? "internal" : "private")

return [
"private static let _\(identifier) = ThreadLocal<\(valueTypeInitializer.trimmed)>(\(args))"
"\(attrs) \(modifiers) static let _\(identifier) = ThreadLocal<\(valueTypeInitializer.trimmed)>(\(args))"
]
}
}


// MARK: Utils

extension VariableDeclSyntax {
var isStatic: Bool {
modifiers.contains { $0.name.trimmed.text == "static" }
}
var isInlinable: Bool {
attributes.contains { $0.attribute?.attributeName.trimmed.as(IdentifierTypeSyntax.self)?.name.text == "inlinable" }
}
}

extension AttributeListSyntax.Element {
var attribute: AttributeSyntax? {
switch self {
case .attribute(let attr):
attr
case .ifConfigDecl:
nil
}
}
}
28 changes: 26 additions & 2 deletions Tests/ThreadLocalTests/ThreadLocalMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@

#if os(macOS) // macro tests can only be run on the host machine
import Foundation
import ThreadLocal
import ThreadLocalMacros
import SwiftSyntaxMacroExpansion
import SwiftSyntaxMacros
import SwiftSyntaxMacrosGenericTestSupport
import Testing
import ThreadLocal
import ThreadLocalMacros

let testMacrosSpecs: [String: MacroSpec] = [
"ThreadLocal": MacroSpec(type: ThreadLocalMacro.self)
Expand Down Expand Up @@ -46,6 +46,30 @@ struct ThreadLocalMacroTests {
)
}

@Test
func inlinable() {
assertMacroExpansion(
"""
@ThreadLocal @inlinable static var counter: Int = 0
""",
expandedSource:
"""
@inlinable static var counter: Int {
get {
_counter._get(default: 0)
}
set {
_counter._set(newValue)
}
}

@usableFromInline internal static let _counter = ThreadLocal<Int>()
""",
macroSpecs: testMacrosSpecs,
failureHandler: { Issue.record("\($0.message)") }
)
}

@Test
func customDeallocator() {
assertMacroExpansion(
Expand Down
3 changes: 1 addition & 2 deletions Tests/ThreadLocalTests/ThreadLocalTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
//

import Foundation
import ThreadLocal
import Testing
import ThreadLocal


@Suite
Expand Down Expand Up @@ -39,6 +39,5 @@ struct ThreadLocalTests {
thread.cancel()
}
sleep(1)
fatalError()
}
}
Loading