Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
### Fixed

- Fix Monterey macOS shell version, shell login flag for environments [#1167](https://github.com/yonaskolb/XcodeGen/issues/1167) @bimawa
- Fixed crash caused by a simultaneous write during a glob processing [#1177](https://github.com/yonaskolb/XcodeGen/issues/1177) @tr1ckyf0x

## 2.27.0

Expand Down
37 changes: 31 additions & 6 deletions Sources/XcodeGenCore/Atomic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,45 @@
import Foundation

@propertyWrapper
struct Atomic<Value> {
private let queue = DispatchQueue(label: "com.xcodegencore.atomic")
public final class Atomic<Value> {

private var value: Value

init(wrappedValue: Value) {
private let queue = DispatchQueue(
label: "com.xcodegencore.atomic.\(UUID().uuidString)",
qos: .utility,
attributes: .concurrent,
autoreleaseFrequency: .inherit,
target: .global()
)

public init(wrappedValue: Value) {
self.value = wrappedValue
}

var wrappedValue: Value {
public var wrappedValue: Value {
get {
return queue.sync { value }
queue.sync { value }
}
set {
queue.sync { value = newValue }
queue.async(flags: .barrier) { [weak self] in
self?.value = newValue
}
}
}

/// Allows us to get the actual `Atomic` instance with the $
/// prefix.
public var projectedValue: Atomic<Value> {
return self
}

/// Modifies the protected value using `closure`.
public func with<R>(
_ closure: (inout Value) throws -> R
) rethrows -> R {
try queue.sync(flags: .barrier) {
try closure(&value)
}
}
}
8 changes: 6 additions & 2 deletions Sources/XcodeGenCore/Glob.swift
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,17 @@ public class Glob: Collection {

var isDirectoryBool = ObjCBool(false)
let isDirectory = FileManager.default.fileExists(atPath: path, isDirectory: &isDirectoryBool) && isDirectoryBool.boolValue
isDirectoryCache[path] = isDirectory
$isDirectoryCache.with { isDirectoryCache in
isDirectoryCache[path] = isDirectory
}

return isDirectory
}

private func clearCaches() {
isDirectoryCache.removeAll()
$isDirectoryCache.with { isDirectoryCache in
isDirectoryCache.removeAll()
}
}

private func paths(usingPattern pattern: String, includeFiles: Bool) -> [String] {
Expand Down
39 changes: 39 additions & 0 deletions Tests/XcodeGenCoreTests/AtomicTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// AtomicTests.swift
//
//
// Created by Vladislav Lisianskii on 27.03.2022.
//

import XCTest
@testable import XcodeGenCore

final class AtomicTests: XCTestCase {

@Atomic private var atomicDictionary = [String: Int]()

func testSimultaneousWriteOrder() {
let group = DispatchGroup()

for index in (0..<100) {
group.enter()
DispatchQueue.global().async {
self.$atomicDictionary.with { atomicDictionary in
atomicDictionary["\(index)"] = index
}
group.leave()
}
}

group.notify(queue: .main, execute: {
var expectedValue = [String: Int]()
for index in (0..<100) {
expectedValue["\(index)"] = index
}
XCTAssertEqual(
self.atomicDictionary,
expectedValue
)
})
}
}