mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
iOS Security Stack 2/5: Concurrency Locks (#33241)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: b99ad804fb
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Co-authored-by: mbelinky <132747814+mbelinky@users.noreply.github.com>
Reviewed-by: @mbelinky
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import AVFoundation
|
||||
import OpenClawKit
|
||||
import Foundation
|
||||
import os
|
||||
|
||||
actor CameraController {
|
||||
struct CameraDeviceInfo: Codable, Sendable {
|
||||
@@ -260,7 +261,7 @@ actor CameraController {
|
||||
|
||||
private final class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegate {
|
||||
private let continuation: CheckedContinuation<Data, Error>
|
||||
private var didResume = false
|
||||
private let resumed = OSAllocatedUnfairLock(initialState: false)
|
||||
|
||||
init(_ continuation: CheckedContinuation<Data, Error>) {
|
||||
self.continuation = continuation
|
||||
@@ -271,8 +272,12 @@ private final class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegat
|
||||
didFinishProcessingPhoto photo: AVCapturePhoto,
|
||||
error: Error?
|
||||
) {
|
||||
guard !self.didResume else { return }
|
||||
self.didResume = true
|
||||
let alreadyResumed = self.resumed.withLock { old in
|
||||
let was = old
|
||||
old = true
|
||||
return was
|
||||
}
|
||||
guard !alreadyResumed else { return }
|
||||
|
||||
if let error {
|
||||
self.continuation.resume(throwing: error)
|
||||
@@ -301,15 +306,19 @@ private final class PhotoCaptureDelegate: NSObject, AVCapturePhotoCaptureDelegat
|
||||
error: Error?
|
||||
) {
|
||||
guard let error else { return }
|
||||
guard !self.didResume else { return }
|
||||
self.didResume = true
|
||||
let alreadyResumed = self.resumed.withLock { old in
|
||||
let was = old
|
||||
old = true
|
||||
return was
|
||||
}
|
||||
guard !alreadyResumed else { return }
|
||||
self.continuation.resume(throwing: error)
|
||||
}
|
||||
}
|
||||
|
||||
private final class MovieFileDelegate: NSObject, AVCaptureFileOutputRecordingDelegate {
|
||||
private let continuation: CheckedContinuation<URL, Error>
|
||||
private var didResume = false
|
||||
private let resumed = OSAllocatedUnfairLock(initialState: false)
|
||||
|
||||
init(_ continuation: CheckedContinuation<URL, Error>) {
|
||||
self.continuation = continuation
|
||||
@@ -321,8 +330,12 @@ private final class MovieFileDelegate: NSObject, AVCaptureFileOutputRecordingDel
|
||||
from connections: [AVCaptureConnection],
|
||||
error: Error?)
|
||||
{
|
||||
guard !self.didResume else { return }
|
||||
self.didResume = true
|
||||
let alreadyResumed = self.resumed.withLock { old in
|
||||
let was = old
|
||||
old = true
|
||||
return was
|
||||
}
|
||||
guard !alreadyResumed else { return }
|
||||
|
||||
if let error {
|
||||
let ns = error as NSError
|
||||
|
||||
@@ -9,6 +9,7 @@ import Darwin
|
||||
import OpenClawKit
|
||||
import Network
|
||||
import Observation
|
||||
import os
|
||||
import Photos
|
||||
import ReplayKit
|
||||
import Security
|
||||
@@ -990,12 +991,16 @@ extension GatewayConnectionController {
|
||||
#endif
|
||||
|
||||
private final class GatewayTLSFingerprintProbe: NSObject, URLSessionDelegate, @unchecked Sendable {
|
||||
private struct ProbeState {
|
||||
var didFinish = false
|
||||
var session: URLSession?
|
||||
var task: URLSessionWebSocketTask?
|
||||
}
|
||||
|
||||
private let url: URL
|
||||
private let timeoutSeconds: Double
|
||||
private let onComplete: (String?) -> Void
|
||||
private var didFinish = false
|
||||
private var session: URLSession?
|
||||
private var task: URLSessionWebSocketTask?
|
||||
private let state = OSAllocatedUnfairLock(initialState: ProbeState())
|
||||
|
||||
init(url: URL, timeoutSeconds: Double, onComplete: @escaping (String?) -> Void) {
|
||||
self.url = url
|
||||
@@ -1008,9 +1013,11 @@ private final class GatewayTLSFingerprintProbe: NSObject, URLSessionDelegate, @u
|
||||
config.timeoutIntervalForRequest = self.timeoutSeconds
|
||||
config.timeoutIntervalForResource = self.timeoutSeconds
|
||||
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
|
||||
self.session = session
|
||||
let task = session.webSocketTask(with: self.url)
|
||||
self.task = task
|
||||
self.state.withLock { s in
|
||||
s.session = session
|
||||
s.task = task
|
||||
}
|
||||
task.resume()
|
||||
|
||||
DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + self.timeoutSeconds) { [weak self] in
|
||||
@@ -1036,12 +1043,18 @@ private final class GatewayTLSFingerprintProbe: NSObject, URLSessionDelegate, @u
|
||||
}
|
||||
|
||||
private func finish(_ fingerprint: String?) {
|
||||
objc_sync_enter(self)
|
||||
defer { objc_sync_exit(self) }
|
||||
guard !self.didFinish else { return }
|
||||
self.didFinish = true
|
||||
self.task?.cancel(with: .goingAway, reason: nil)
|
||||
self.session?.invalidateAndCancel()
|
||||
let (shouldComplete, taskToCancel, sessionToInvalidate) = self.state.withLock { s -> (Bool, URLSessionWebSocketTask?, URLSession?) in
|
||||
guard !s.didFinish else { return (false, nil, nil) }
|
||||
s.didFinish = true
|
||||
let task = s.task
|
||||
let session = s.session
|
||||
s.task = nil
|
||||
s.session = nil
|
||||
return (true, task, session)
|
||||
}
|
||||
guard shouldComplete else { return }
|
||||
taskToCancel?.cancel(with: .goingAway, reason: nil)
|
||||
sessionToInvalidate?.invalidateAndCancel()
|
||||
self.onComplete(fingerprint)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user