<commit_before>//
//  JSBridge.swift
//  Client
//
//  Created by <NAME> on 21/02/2017.
//  Copyright © 2017 Mozilla. All rights reserved.
//

import Foundation
import React

@objc(JSBridge)
public class JSBridge : RCTEventEmitter {

    var registeredActions: Set<String> = []
    var actionCounter: NSInteger = 0
    var replyCache = [NSInteger: [String: NSObject]]()
    var eventSemaphores = [NSInteger: dispatch_semaphore_t]()
    
    public override init() {
        super.init()
    }
    
    public override static func moduleName() -> String! {
        return "JSBridge"
    }
    
    override public func supportedEvents() -> [String]! {
        return ["callAction", "publishEvent"]
    }
    
    public func callAction(functionName: String, args: AnyObject) -> [String: NSObject] {
        // check listener is registered on other end
        guard self.registeredActions.contains(functionName) else {
            return ["error": "function not registered"]
        }
        
        // create unique id
        actionCounter += 1
        let actionId = actionCounter
        
        // create a semaphore for this action
        let sem = dispatch_semaphore_create(0)
        self.eventSemaphores[actionId] = sem
        
        // dispatch event
        self.sendEventWithName("callAction", body: ["id": actionId, "action": functionName, "args": args])
        
        // wait for the semaphore
        let timeout = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)

        // after signal the reply should be ready in the cache
        if timeout != 0 {
            print("action timeout \(actionId), \(functionName)")
            self.replyCache[actionId] = ["error": "timeout"]
        }
        let reply = self.replyCache[actionId]!
        
        // clear semaphore and reply cache
        self.replyCache[actionId] = nil
        self.eventSemaphores[actionId] = nil
        
        return reply
    }
    
    public func publishEvent(eventName: String, args: AnyObject) {
        self.sendEventWithName("publishEvent", body: ["event": eventName, "args": args])
    }
    
    @objc(replyToAction:response:error:)
    func replyToAction(actionId: NSInteger, response: NSDictionary, error: String) {
        // lookup the semaphore for this action
        if let sem = self.eventSemaphores[actionId] {
            // place response in cache and signal on this semaphore
            self.replyCache[actionId] = ["result": response, "error": error]
            dispatch_semaphore_signal(sem)
        } else {
            print("disgarding action reply \(actionId)")
        }
    }
    
    @objc(registerAction:)
    func registerAction(actionName: String) {
        self.registeredActions.insert(actionName)
    }
    
}<commit_msg>Make access to semaphores thread-safe in JSBridge<commit_after>//
//  JSBridge.swift
//  Client
//
//  Created by <NAME> on 21/02/2017.
//  Copyright © 2017 Mozilla. All rights reserved.
//

import Foundation
import React

@objc(JSBridge)
public class JSBridge : RCTEventEmitter {

    var registeredActions: Set<String> = []
    var actionCounter: NSInteger = 0
    var replyCache = [NSInteger: [String: NSObject]]()
    var eventSemaphores = [NSInteger: dispatch_semaphore_t]()
    
    public override init() {
        super.init()
    }
    
    public override static func moduleName() -> String! {
        return "JSBridge"
    }
    
    override public func supportedEvents() -> [String]! {
        return ["callAction", "publishEvent"]
    }
    
    public func callAction(functionName: String, args: AnyObject) -> [String: NSObject] {
        // check listener is registered on other end
        guard self.registeredActions.contains(functionName) else {
            return ["error": "function not registered"]
        }
        
        // create unique id
        actionCounter += 1
        let actionId = actionCounter
        
        // create a semaphore for this action
        let sem = dispatch_semaphore_create(0)
        objc_sync_enter(self.eventSemaphores)
        self.eventSemaphores[actionId] = sem
        objc_sync_exit(self.eventSemaphores)
        
        // dispatch event
        self.sendEventWithName("callAction", body: ["id": actionId, "action": functionName, "args": args])
        
        // wait for the semaphore
        let timeout = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER)

        // after signal the reply should be ready in the cache
        objc_sync_enter(self.eventSemaphores)
        if timeout != 0 {
            print("action timeout \(actionId), \(functionName)")
            self.replyCache[actionId] = ["error": "timeout"]
        }
        let reply = self.replyCache[actionId]!
        
        // clear semaphore and reply cache
        self.replyCache[actionId] = nil
        self.eventSemaphores[actionId] = nil
        objc_sync_exit(self.eventSemaphores)
        
        return reply
    }
    
    public func publishEvent(eventName: String, args: AnyObject) {
        self.sendEventWithName("publishEvent", body: ["event": eventName, "args": args])
    }
    
    @objc(replyToAction:response:error:)
    func replyToAction(actionId: NSInteger, response: NSDictionary, error: String) {
        // lookup the semaphore for this action
        objc_sync_enter(self.eventSemaphores)
        if let sem = self.eventSemaphores[actionId] {
            // place response in cache and signal on this semaphore
            self.replyCache[actionId] = ["result": response, "error": error]
            dispatch_semaphore_signal(sem)
        } else {
            print("disgarding action reply \(actionId)")
        }
        objc_sync_exit(self.eventSemaphores)
    }
    
    @objc(registerAction:)
    func registerAction(actionName: String) {
        self.registeredActions.insert(actionName)
    }
    
}
{'fraction_non_alphanumeric': 0.10046209139140852, 'fraction_numerical': 0.005476638712989902, 'mean_word_length': 2.3741339491916857, 'pattern_counts': {'":': 18, '<': 7, '<?xml version=': 0, '>': 13, 'https://': 0, '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': '1879832', 'n_tokens_mistral': 1702, 'n_tokens_neox': 1636, 'n_words': 430}