<commit_before>import Foundation

protocol WebPresenter: class {
    func presentWith(urlRequest: URLRequest) throws -> String
    func dismiss()
}

enum WebControllerResult {
    case response(URL?)
    case error(VKError)
}

private enum WebPresenterResult {
    case response(String)
    case error(VKError)
}

private enum ResponseParsingResult {
    case response(String)
    case fail
    case nothing
}

final class WebPresenterImpl: WebPresenter {
    private let uiSyncQueue: DispatchQueue
    private weak var controllerMaker: WebControllerMaker?
    private weak var currentController: WebController?
    private let maxFails: Int
    private let timeout: TimeInterval

    init(
        uiSyncQueue: DispatchQueue,
        controllerMaker: WebControllerMaker,
        maxFails: Int,
        timeout: TimeInterval
        ) {
        self.uiSyncQueue = uiSyncQueue
        self.controllerMaker = controllerMaker
        self.maxFails = maxFails
        self.timeout = timeout
    }
    
    func presentWith(urlRequest: URLRequest) throws -> String {
        guard let controllerMaker = controllerMaker else { throw VKError.weakObjectWasDeallocated }

        let semaphore = DispatchSemaphore(value: 0)
        var fails: Int = 0
        var result: WebPresenterResult?
        
        return try uiSyncQueue.sync {
            let controller = controllerMaker.webController {
                semaphore.signal()
            }
            
            // This is a hack to avoid crash while WebKit deinitializing not in the main thread
            // https://github.com/SwiftyVK/SwiftyVK/issues/142
            defer { releaseInMainThreadAfterDelay(controller) }
            
            let originalPath = urlRequest.url?.path ?? ""
            currentController = controller
            
            controller.load(
                urlRequest: urlRequest,
                onResult: { [weak self] in
                    self?.handle(
                        result: $0,
                        originalPath: originalPath,
                        fails: &fails,
                        finalResult: &result
                    )
                }
            )
            
            switch semaphore.wait(timeout: .now() + timeout) {
            case .timedOut:
                throw VKError.webPresenterTimedOut
            case .success:
                break
            }
            
            switch result {
            case .response(let response)?:
                return response
            case .error(let error)?:
                throw error
            case nil:
                throw VKError.webPresenterResultIsNil
            }
        }
    }
    
    private func handle(
        result: WebControllerResult,
        originalPath: String,
        fails: inout Int,
        finalResult: inout WebPresenterResult?
    ) {
        do {
            let parsedResult: ResponseParsingResult
            
            switch result {
            case .response(let url):
                parsedResult = try parse(url: url, originalPath: originalPath)
            case .error(let error):
                parsedResult = try parse(error: error, fails: fails)
            }
            
            switch parsedResult {
            case let .response(value):
                finalResult = .response(value)
                
            case .fail:
                fails += 1
                currentController?.reload()
                
            case .nothing:
                break
                
            }
        }
            
        catch let error {
            finalResult = .error(error.toVK())
        }
        
        if finalResult != nil {
            currentController?.dismiss()
        }
    }
    
    private func parse(url maybeUrl: URL?, originalPath: String) throws -> ResponseParsingResult {
        guard let url = maybeUrl else {
            throw VKError.authorizationUrlIsNil
        }<commit_msg>[Core] Fix simultaneously access to fails variable<commit_after>import Foundation

protocol WebPresenter: class {
    func presentWith(urlRequest: URLRequest) throws -> String
    func dismiss()
}

enum WebControllerResult {
    case response(URL?)
    case error(VKError)
}

private enum WebPresenterResult {
    case response(String)
    case error(VKError)
}

private enum ResponseParsingResult {
    case response(String)
    case fail
    case nothing
}

private final class LoadingState {
    var originalPath: String
    var fails: Int = 0
    var result: WebPresenterResult?
    
    init(originalPath: String) {
        self.originalPath = originalPath
    }
}

final class WebPresenterImpl: WebPresenter {
    private let uiSyncQueue: DispatchQueue
    private weak var controllerMaker: WebControllerMaker?
    private weak var currentController: WebController?
    private let maxFails: Int
    private let timeout: TimeInterval

    init(
        uiSyncQueue: DispatchQueue,
        controllerMaker: WebControllerMaker,
        maxFails: Int,
        timeout: TimeInterval
        ) {
        self.uiSyncQueue = uiSyncQueue
        self.controllerMaker = controllerMaker
        self.maxFails = maxFails
        self.timeout = timeout
    }
    
    func presentWith(urlRequest: URLRequest) throws -> String {
        guard let controllerMaker = controllerMaker else { throw VKError.weakObjectWasDeallocated }

        let semaphore = DispatchSemaphore(value: 0)
        let state = LoadingState(originalPath: urlRequest.url?.path ?? "")
        
        return try uiSyncQueue.sync {
            let controller = controllerMaker.webController {
                semaphore.signal()
            }
            
            // This is a hack to avoid crash while WebKit deinitializing not in the main thread
            // https://github.com/SwiftyVK/SwiftyVK/issues/142
            defer { releaseInMainThreadAfterDelay(controller) }
            
            currentController = controller
            
            controller.load(
                urlRequest: urlRequest,
                onResult: { [weak self] in
                    self?.handle(
                        result: $0,
                        state: state
                    )
                }
            )
            
            switch semaphore.wait(timeout: .now() + timeout) {
            case .timedOut:
                throw VKError.webPresenterTimedOut
            case .success:
                break
            }
            
            switch state.result {
            case .response(let response)?:
                return response
            case .error(let error)?:
                throw error
            case nil:
                throw VKError.webPresenterResultIsNil
            }
        }
    }
    
    private func handle(result: WebControllerResult, state: LoadingState) {
        do {
            let parsedResult: ResponseParsingResult
            
            switch result {
            case .response(let url):
                parsedResult = try parse(url: url, originalPath: state.originalPath)
            case .error(let error):
                parsedResult = try parse(error: error, fails: state.fails)
            }
            
            switch parsedResult {
            case let .response(value):
                state.result = .response(value)
                
            case .fail:
                state.fails += 1
                currentController?.reload()
                
            case .nothing:
                break
                
            }
        }
            
        catch let error {
            state.result = .error(error.toVK())
        }
        
        if state.result != nil {
            currentController?.dismiss()
        }
    }
    
    private func parse(url maybeUrl: URL?, originalPath: String) throws -> ResponseParsingResult {
        guard let url = maybeUrl else {
            throw VKError.authorizationUrlIsNil
        }
{'fraction_non_alphanumeric': 0.06483696336396827, 'fraction_numerical': 0.0017625582273700113, 'mean_word_length': 1.5028355387523629, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3395607', 'n_tokens_mistral': 1970, 'n_tokens_neox': 1913, 'n_words': 507}