Compare commits

..

2 Commits

Author SHA1 Message Date
Shadowfacts b30f149dc9 Use mutex on iOS 15 instead of os_unfair_lock
See #178
2022-10-08 10:57:59 -04:00
Shadowfacts 9b83566482 Fix TuskerTests not compiling 2022-10-08 10:55:55 -04:00
10 changed files with 223 additions and 158 deletions

View File

@ -33,6 +33,7 @@
D6114E1727F8BB210080E273 /* VersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6114E1627F8BB210080E273 /* VersionTests.swift */; };
D611C2CF232DC61100C86A49 /* HashtagTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D611C2CD232DC61100C86A49 /* HashtagTableViewCell.swift */; };
D611C2D0232DC61100C86A49 /* HashtagTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D611C2CE232DC61100C86A49 /* HashtagTableViewCell.xib */; };
D61ABEFC28F105DE00B29151 /* Pachyderm in Frameworks */ = {isa = PBXBuildFile; productRef = D61ABEFB28F105DE00B29151 /* Pachyderm */; };
D61AC1D5232E9FA600C54D2D /* InstanceSelectorTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61AC1D4232E9FA600C54D2D /* InstanceSelectorTableViewController.swift */; };
D61AC1D8232EA42D00C54D2D /* InstanceTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61AC1D6232EA42D00C54D2D /* InstanceTableViewCell.swift */; };
D61AC1D9232EA42D00C54D2D /* InstanceTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = D61AC1D7232EA42D00C54D2D /* InstanceTableViewCell.xib */; };
@ -208,8 +209,6 @@
D6A6C11525B62E9700298D0F /* CacheExpiry.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C11425B62E9700298D0F /* CacheExpiry.swift */; };
D6A6C11B25B63CEE00298D0F /* MemoryCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A6C11A25B63CEE00298D0F /* MemoryCache.swift */; };
D6AC956723C4347E008C9946 /* MainSceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AC956623C4347E008C9946 /* MainSceneDelegate.swift */; };
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F613023AE99E000F3CFD3 /* Ambassador.framework */; };
D6ACE1AD240C3BAD004EA8E2 /* Embassy.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D65F612D23AE990C00F3CFD3 /* Embassy.framework */; };
D6AEBB3E2321638100E5038B /* UIActivity+Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB3D2321638100E5038B /* UIActivity+Types.swift */; };
D6AEBB412321642700E5038B /* SendMesasgeActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB402321642700E5038B /* SendMesasgeActivity.swift */; };
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6AEBB422321685E00E5038B /* OpenInSafariActivity.swift */; };
@ -673,8 +672,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
D6ACE1AC240C3BAD004EA8E2 /* Ambassador.framework in Frameworks */,
D6ACE1AD240C3BAD004EA8E2 /* Embassy.framework in Frameworks */,
D61ABEFC28F105DE00B29151 /* Pachyderm in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -1513,6 +1511,7 @@
);
name = TuskerUITests;
packageProductDependencies = (
D61ABEFB28F105DE00B29151 /* Pachyderm */,
);
productName = TuskerUITests;
productReference = D6D4DDEB212518A200E1C4BB /* TuskerUITests.xctest */;
@ -2218,14 +2217,15 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = HGYVAQA9FW;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = TuskerTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.TuskerTests;
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.TuskerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -2239,14 +2239,15 @@
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = HGYVAQA9FW;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = TuskerTests/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.TuskerTests;
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.TuskerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -2259,14 +2260,14 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = HGYVAQA9FW;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = TuskerUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.TuskerUITests;
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.TuskerUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -2279,14 +2280,14 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_TEAM = HGYVAQA9FW;
DEVELOPMENT_TEAM = V4WK9KR9U2;
INFOPLIST_FILE = TuskerUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
PRODUCT_BUNDLE_IDENTIFIER = net.shadowfacts.TuskerUITests;
PRODUCT_BUNDLE_IDENTIFIER = space.vaccor.Tusker.TuskerUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -2439,6 +2440,10 @@
package = D60CFFD924A290BA00D00083 /* XCRemoteSwiftPackageReference "SwiftSoup" */;
productName = SwiftSoup;
};
D61ABEFB28F105DE00B29151 /* Pachyderm */ = {
isa = XCSwiftPackageProductDependency;
productName = Pachyderm;
};
D6552366289870790048A653 /* ScreenCorners */ = {
isa = XCSwiftPackageProductDependency;
package = D6552365289870790048A653 /* XCRemoteSwiftPackageReference "ScreenCorners" */;

View File

@ -19,7 +19,7 @@ class MultiThreadDictionary<Key: Hashable & Sendable, Value: Sendable> {
if #available(iOS 16.0, *) {
self.lock = OSAllocatedUnfairLock(initialState: [:])
} else {
self.lock = UnfairLock(initialState: [:])
self.lock = MutexLock(initialState: [:])
}
}
@ -65,21 +65,41 @@ fileprivate protocol Lock<State> {
extension OSAllocatedUnfairLock: Lock {
}
// from http://www.russbishop.net/the-law
fileprivate class UnfairLock<State>: Lock {
private var lock: UnsafeMutablePointer<os_unfair_lock>
// something is wrong with the UnfairLock impl and it results in segv_accerrs
fileprivate class MutexLock<State>: Lock {
private var state: State
private var lock = NSLock()
init(initialState: State) {
self.state = initialState
self.lock = .allocate(capacity: 1)
self.lock.initialize(to: os_unfair_lock())
}
deinit {
self.lock.deallocate()
}
func withLock<R>(_ body: (inout State) throws -> R) rethrows -> R where R: Sendable {
os_unfair_lock_lock(lock)
defer { os_unfair_lock_unlock(lock) }
func withLock<R>(_ body: @Sendable (inout State) throws -> R) rethrows -> R where R : Sendable {
if !lock.lock(before: Date(timeIntervalSinceNow: 1)) {
// if we can't acquire the lock after 1 second, something has gone catastrophically wrong
fatalError()
}
defer { lock.unlock() }
return try body(&state)
}
}
//// from http://www.russbishop.net/the-law
//fileprivate class UnfairLock<State>: Lock {
// private var lock: UnsafeMutablePointer<os_unfair_lock>
// private var state: State
// init(initialState: State) {
// self.state = initialState
// self.lock = .allocate(capacity: 1)
// self.lock.initialize(to: os_unfair_lock())
// }
// deinit {
// self.lock.deinitialize(count: 1)
// self.lock.deallocate()
// }
// func withLock<R>(_ body: (inout State) throws -> R) rethrows -> R where R: Sendable {
// os_unfair_lock_lock(lock)
// defer { os_unfair_lock_unlock(lock) }
// return try body(&state)
// }
//}

View File

@ -30,5 +30,45 @@ class TuskerTests: XCTestCase {
// Put the code you want to measure the time of here.
}
}
func testFuckingLock() {
let lock = MutexLock<[Int: Bool]>(initialState: [:])
for i in 0..<100 {
Thread.detachNewThread {
for j in 0..<50_000 {
lock.withLock {
$0[i * 50_000 + j] = true
}
}
}
}
while true {
if lock.withLock({ $0.count }) == 5_000_000 {
break
}
}
lock.withLock({ _ in
print("WHAT THE FUUUUUUUUUUUUCK")
})
}
}
fileprivate class MutexLock<State> {
private var state: State
private var lock = NSLock()
init(initialState: State) {
self.state = initialState
}
func withLock<R>(_ body: @Sendable (inout State) throws -> R) rethrows -> R where R : Sendable {
if !lock.lock(before: Date(timeIntervalSinceNow: 1)) {
// if we can't acquire the lock after 1 second, something has gone catastrophically wrong
fatalError()
}
defer { lock.unlock() }
return try body(&state)
}
}

View File

@ -7,70 +7,70 @@
//
import Foundation
import Ambassador
fileprivate let notFound = ["error": "Record not found"]
extension Router {
func allRoutes() {
instanceRoutes()
accountRoutes()
timelineRoutes()
notificationRoutes()
}
func instanceRoutes() {
self["/api/v1/instance"] = JSONResponse(handler: { (_) in
return [
"description": "An instance description",
"max_toot_chars": 500,
"thumbnail": "http://localhost:8080/thumbnail.png",
"title": "Localhost",
"uri": "http://localhost:8080",
"version": "2.7.2",
"urls": [:]
]
})
}
func accountRoutes() {
let selfAccount: [String: Any] = [
"id": "1",
"username": "admin",
"acct": "admin",
"display_name": "Admin Account",
"locked": false,
"created_at": "2019-12-31T11:13:42.0Z",
"followers_count": 0,
"following_count": 0,
"statuses_count": 0,
"note": "My profile description.",
"url": "http://localhost:8080/users/admin",
"avatar": "http://localhost:8080/avatar/admin.jpg",
"avatar_static": "http://localhost:8080/avatar/admin.jpg",
"header": "http://localhost:8080/header/admin.jpg",
"header_static": "http://localhost:8080/header/admin.jpg",
"emojis": []
]
self["/api/v1/accounts/verify_credentials"] = JSONResponse(result: selfAccount)
self["/api/v1/accounts/\\d+/statuses"] = JSONResponse(result: [])
self["/api/v1/accounts/(\\d+)"] = DelegatingResponse { (ctx) in
if ctx.captures[0] == "1" {
return JSONResponse(result: selfAccount)
} else {
return JSONResponse(statusCode: 404, statusMessage: "Not Found", result: notFound)
}
}
}
func timelineRoutes() {
let emptyTimeline: [Any] = []
self["/api/v1/timelines/home"] = JSONResponse(result: emptyTimeline)
self["/api/v1/timelines/public"] = JSONResponse(result: emptyTimeline)
}
func notificationRoutes() {
let emptyTimeline: [Any] = []
self["/api/v1/notifications"] = JSONResponse(result: emptyTimeline)
}
}
//import Ambassador
//
//fileprivate let notFound = ["error": "Record not found"]
//
//extension Router {
// func allRoutes() {
// instanceRoutes()
// accountRoutes()
// timelineRoutes()
// notificationRoutes()
// }
//
// func instanceRoutes() {
// self["/api/v1/instance"] = JSONResponse(handler: { (_) in
// return [
// "description": "An instance description",
// "max_toot_chars": 500,
// "thumbnail": "http://localhost:8080/thumbnail.png",
// "title": "Localhost",
// "uri": "http://localhost:8080",
// "version": "2.7.2",
// "urls": [:]
// ]
// })
// }
//
// func accountRoutes() {
// let selfAccount: [String: Any] = [
// "id": "1",
// "username": "admin",
// "acct": "admin",
// "display_name": "Admin Account",
// "locked": false,
// "created_at": "2019-12-31T11:13:42.0Z",
// "followers_count": 0,
// "following_count": 0,
// "statuses_count": 0,
// "note": "My profile description.",
// "url": "http://localhost:8080/users/admin",
// "avatar": "http://localhost:8080/avatar/admin.jpg",
// "avatar_static": "http://localhost:8080/avatar/admin.jpg",
// "header": "http://localhost:8080/header/admin.jpg",
// "header_static": "http://localhost:8080/header/admin.jpg",
// "emojis": []
// ]
// self["/api/v1/accounts/verify_credentials"] = JSONResponse(result: selfAccount)
// self["/api/v1/accounts/\\d+/statuses"] = JSONResponse(result: [])
// self["/api/v1/accounts/(\\d+)"] = DelegatingResponse { (ctx) in
// if ctx.captures[0] == "1" {
// return JSONResponse(result: selfAccount)
// } else {
// return JSONResponse(statusCode: 404, statusMessage: "Not Found", result: notFound)
// }
// }
// }
//
// func timelineRoutes() {
// let emptyTimeline: [Any] = []
// self["/api/v1/timelines/home"] = JSONResponse(result: emptyTimeline)
// self["/api/v1/timelines/public"] = JSONResponse(result: emptyTimeline)
// }
//
// func notificationRoutes() {
// let emptyTimeline: [Any] = []
// self["/api/v1/notifications"] = JSONResponse(result: emptyTimeline)
// }
//}

View File

@ -7,23 +7,23 @@
//
import Foundation
import Ambassador
struct DelegatingResponse: WebApp {
let handler: (_ ctx: Context) -> WebApp
func app(_ environ: [String : Any], startResponse: @escaping ((String, [(String, String)]) -> Void), sendBody: @escaping ((Data) -> Void)) {
let ctx = Context(environ: environ)
handler(ctx).app(environ, startResponse: startResponse, sendBody: sendBody)
}
}
extension DelegatingResponse {
struct Context {
let environ: [String: Any]
var captures: [String] {
environ["ambassador.router_captures"] as? [String] ?? []
}
}
}
//import Ambassador
//
//struct DelegatingResponse: WebApp {
// let handler: (_ ctx: Context) -> WebApp
//
// func app(_ environ: [String : Any], startResponse: @escaping ((String, [(String, String)]) -> Void), sendBody: @escaping ((Data) -> Void)) {
// let ctx = Context(environ: environ)
// handler(ctx).app(environ, startResponse: startResponse, sendBody: sendBody)
// }
//}
//
//extension DelegatingResponse {
// struct Context {
// let environ: [String: Any]
//
// var captures: [String] {
// environ["ambassador.router_captures"] as? [String] ?? []
// }
// }
//}

View File

@ -7,12 +7,12 @@
//
import Foundation
import Ambassador
extension JSONResponse {
init(statusCode: Int = 200, statusMessage: String = "OK", result: Any) {
self.init(statusCode: statusCode, statusMessage: statusMessage, handler: { (_) in
return result
})
}
}
//import Ambassador
//
//extension JSONResponse {
// init(statusCode: Int = 200, statusMessage: String = "OK", result: Any) {
// self.init(statusCode: statusCode, statusMessage: statusMessage, handler: { (_) in
// return result
// })
// }
//}

View File

@ -14,7 +14,7 @@ class ComposeTests: TuskerUITests {
override func setUp() {
super.setUp()
router.allRoutes()
// router.allRoutes()
app.launchEnvironment["UI_TESTING_LOGIN"] = "true"
app.launch()

View File

@ -13,7 +13,7 @@ class MyProfileTests: TuskerUITests {
override func setUp() {
super.setUp()
router.allRoutes()
// router.allRoutes()
app.launchEnvironment["UI_TESTING_LOGIN"] = "true"
app.launch()

View File

@ -7,14 +7,14 @@
//
import XCTest
import Ambassador
//import Ambassador
class OnboardingTests: TuskerUITests {
override func setUp() {
super.setUp()
router.instanceRoutes()
// router.instanceRoutes()
app.launch()
}

View File

@ -7,40 +7,40 @@
//
import XCTest
import Embassy
import Ambassador
//import Embassy
//import Ambassador
class TuskerUITests: XCTestCase {
var eventLoop: EventLoop!
var router: Router!
var server: HTTPServer!
var eventLoopThreadCondition: NSCondition!
var eventLoopThread: Thread!
// var eventLoop: EventLoop!
// var router: Router!
// var server: HTTPServer!
// var eventLoopThreadCondition: NSCondition!
// var eventLoopThread: Thread!
var app: XCUIApplication!
private func setupWebServer() {
eventLoop = try! SelectorEventLoop(selector: try! KqueueSelector())
router = Router()
server = DefaultHTTPServer(eventLoop: eventLoop, port: 8080, app: router.app)
router["/hello"] = JSONResponse(handler: { (_) in
return ["Hello", "World"]
})
try! server.start()
eventLoopThreadCondition = NSCondition()
eventLoopThread = Thread(block: {
self.eventLoop.runForever()
self.eventLoopThreadCondition.lock()
self.eventLoopThreadCondition.signal()
self.eventLoopThreadCondition.unlock()
})
eventLoopThread.start()
}
// private func setupWebServer() {
// eventLoop = try! SelectorEventLoop(selector: try! KqueueSelector())
// router = Router()
// server = DefaultHTTPServer(eventLoop: eventLoop, port: 8080, app: router.app)
// router["/hello"] = JSONResponse(handler: { (_) in
// return ["Hello", "World"]
// })
// try! server.start()
//
// eventLoopThreadCondition = NSCondition()
// eventLoopThread = Thread(block: {
// self.eventLoop.runForever()
// self.eventLoopThreadCondition.lock()
// self.eventLoopThreadCondition.signal()
// self.eventLoopThreadCondition.unlock()
// })
// eventLoopThread.start()
// }
override func setUp() {
setupWebServer()
// setupWebServer()
continueAfterFailure = false
app = XCUIApplication()
@ -48,14 +48,14 @@ class TuskerUITests: XCTestCase {
}
override func tearDown() {
server.stopAndWait()
eventLoopThreadCondition.lock()
eventLoop.stop()
while eventLoop.running {
if !eventLoopThreadCondition.wait(until: Date(timeIntervalSinceNow: 10)) {
fatalError("Join eventLoopThread timeout")
}
}
// server.stopAndWait()
// eventLoopThreadCondition.lock()
// eventLoop.stop()
// while eventLoop.running {
// if !eventLoopThreadCondition.wait(until: Date(timeIntervalSinceNow: 10)) {
// fatalError("Join eventLoopThread timeout")
// }
// }
}
}