Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VergeNormalization module - NormalizedStorage #413

Merged
merged 23 commits into from
Oct 12, 2023
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
29 changes: 29 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ let package = Package(
.library(name: "Verge", targets: ["Verge"]),
.library(name: "VergeTiny", targets: ["VergeTiny"]),
.library(name: "VergeORM", targets: ["VergeORM"]),
.library(name: "VergeNormalization", targets: ["VergeNormalization"]),
.library(name: "VergeRx", targets: ["VergeRx"]),
.library(name: "VergeClassic", targets: ["VergeClassic"]),
.library(name: "VergeMacros", targets: ["VergeMacros"]),
Expand Down Expand Up @@ -44,10 +45,12 @@ let package = Package(
.target(name: "VergeMacros", dependencies: ["VergeMacrosPlugin"]),

.target(name: "VergeTiny", dependencies: []),
.target(name: "VergeComparator"),
.target(
name: "Verge",
dependencies: [
"VergeMacros",
"VergeComparator",
.product(name: "Atomics", package: "swift-atomics"),
.product(name: "DequeModule", package: "swift-collections"),
.product(name: "ConcurrencyTaskManager", package: "swift-concurrency-task-manager"),
Expand All @@ -59,9 +62,27 @@ let package = Package(
"VergeRx"
]
),
.target(
name: "VergeNormalization",
dependencies: [
"VergeMacros",
"VergeComparator",
.product(name: "HashTreeCollections", package: "swift-collections"),
]
),
.target(
name: "VergeNormalizationDerived",
dependencies: [
"Verge",
"VergeNormalization",
.product(name: "HashTreeCollections", package: "swift-collections"),
]
),
.target(
name: "VergeORM",
dependencies: [
"VergeNormalization",
"VergeNormalizationDerived",
"Verge",
.product(name: "HashTreeCollections", package: "swift-collections"),
]
Expand All @@ -78,6 +99,14 @@ let package = Package(
name: "VergeClassicTests",
dependencies: ["VergeClassic"]
),
.testTarget(
name: "VergeNormalizationTests",
dependencies: ["VergeNormalization"]
),
.testTarget(
name: "VergeNormalizationDerivedTests",
dependencies: ["VergeNormalizationDerived"]
),
.testTarget(
name: "VergeORMTests",
dependencies: ["VergeORM"]
Expand Down
105 changes: 100 additions & 5 deletions Sources/Verge/Library/VergeConcurrency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ public enum VergeConcurrency {
}

/// An atomic variable.
@propertyWrapper
public final class UnfairLockAtomic<Value>: @unchecked Sendable {

public var unsafelyWrappedValue: Value {
Expand All @@ -143,21 +144,36 @@ public enum VergeConcurrency {
get {
return withValue { $0 }
}

set(newValue) {
swap(newValue)
}
}


public var wrappedValue: Value {
get {
return withValue { $0 }
}
set(newValue) {
swap(newValue)
}
}

/// Initialize the variable with the given initial value.
///
/// - parameters:
/// - value: Initial value for `self`.
public init(_ value: Value) {
_value = value
public init(_ wrappedValue: Value) {
_value = wrappedValue
lock = .init()
}


public init(wrappedValue: Value) {
_value = wrappedValue
lock = .init()
}

public var projectedValue: UnfairLockAtomic<Value> { self }

/// Atomically modifies the variable.
///
/// - parameters:
Expand Down Expand Up @@ -203,4 +219,83 @@ public enum VergeConcurrency {
}
}

/// A container that initializes value when it needs.
///
/// Supports multi-threading.
@propertyWrapper
public final class AtomicLazy<T>: @unchecked Sendable {

private enum State {
case initialized(T)
case notInitialized
}

public typealias Initializer = () -> T

private var _onInitialized: (T) -> Void = { _ in }

private let lock: UnfairLock = .init()

public var wrappedValue: T {

lock.lock()
defer {
lock.unlock()
}

return unsafeValue
}

public var projectedValue: AtomicLazy<T> {
self
}

@discardableResult
public func modify<Result>(_ action: (inout T) throws -> Result) rethrows -> Result {
lock.lock()
defer { lock.unlock() }

var new = unsafeValue
let result = try action(&new)
self._synchronized_state = .initialized(new)
return consume result
}

private var _synchronized_state: State = .notInitialized

private var unsafeValue: T {
get {
switch _synchronized_state {
case .notInitialized:
let value = initializer()
_onInitialized(value)
self._synchronized_state = .initialized(value)
self.initializer = nil
return value
case .initialized(let value):
return value
}
}
}

private var initializer: Initializer!

public init(_ initializer: @escaping Initializer) {
self.initializer = initializer
}

public init(wrappedValue initializer: @autoclosure @escaping Initializer) {
self.initializer = initializer
}

/// Set closure on value initialized.
/// the closure would be called on thread which value initialized.
@discardableResult
public func onInitialized(_ perform: @escaping (T) -> Void) -> Self {
_onInitialized = perform
return self
}
}


}
1 change: 1 addition & 0 deletions Sources/Verge/Store/Changes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
// THE SOFTWARE.

import Foundation
@_spi(Internal) import VergeComparator

#if !COCOAPODS
#endif
Expand Down
35 changes: 35 additions & 0 deletions Sources/Verge/Store/Store.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ open class Store<State: Equatable, Activity>: EventEmitter<_StoreEvent<State, Ac

private let wasInvalidated = Atomics.ManagedAtomic(false)

@_spi(NormalizedStorage)
@VergeConcurrency.UnfairLockAtomic public var _derivedCache: NSMapTable<KeyObject<AnyHashable>, AnyObject> = .init(keyOptions: [.copyIn, .objectPersonality], valueOptions: [.weakMemory])

@_spi(NormalizedStorage)
@VergeConcurrency.AtomicLazy public var _nonnull_derivedCache: NSMapTable<KeyObject<AnyHashable>, AnyObject> = .init(keyOptions: [.copyIn, .objectPersonality], valueOptions: [.weakMemory])

// MARK: - Deinit

deinit {
Expand Down Expand Up @@ -815,3 +821,32 @@ extension Store {
}

}

public final class KeyObject<Content: Hashable>: NSObject, NSCopying {

public func copy(with zone: NSZone? = nil) -> Any {
return KeyObject(content: content)
}

public let content: Content

public init(content: consuming Content) {
self.content = content
}

public override var hash: Int {
content.hashValue
}

public override func isEqual(_ object: Any?) -> Bool {

guard let other = object as? KeyObject<Content> else {
return false
}

guard content == other.content else { return false }

return true
}

}
1 change: 1 addition & 0 deletions Sources/Verge/Verge.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

@_exported import ConcurrencyTaskManager
@_exported import Combine
@_exported import VergeComparator
@_exported import VergeMacros

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import Foundation
import os.log

// TODO: will rename as Comparator
public protocol Comparison<Input>: Sendable {
associatedtype Input

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@

func areEqual<each Element: Equatable>(_ lhs: (repeat each Element), _ rhs: (repeat each Element)) -> Bool {
@_spi(Internal)
public func areEqual<each Element: Equatable>(_ lhs: (repeat each Element), _ rhs: (repeat each Element)) -> Bool {

// https://github.com/apple/swift-evolution/blob/main/proposals/0408-pack-iteration.md

Expand Down
17 changes: 17 additions & 0 deletions Sources/VergeMacrosPlugin/IndexMacro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct IndexMacro: Macro {

}

extension IndexMacro: PeerMacro {
public static func expansion(
of node: SwiftSyntax.AttributeSyntax,
providingPeersOf declaration: some SwiftSyntax.DeclSyntaxProtocol,
in context: some SwiftSyntaxMacros.MacroExpansionContext
) throws -> [SwiftSyntax.DeclSyntax] {
[]
}
}
17 changes: 17 additions & 0 deletions Sources/VergeMacrosPlugin/MacroError.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

import SwiftDiagnostics

public struct MacroError: Error, DiagnosticMessage {

public var message: String

public var diagnosticID: SwiftDiagnostics.MessageID {
.init(domain: "Verge", id: "MacroError")
}

public var severity: SwiftDiagnostics.DiagnosticSeverity = .error

init(message: String) {
self.message = message
}
}
Loading
Loading