         } else {
             print("disgarding action reply \(actionId)")
         }
+        objc_sync_exit(self.eventSemaphores)
     }
     
     @objc(registerAction:)

        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"]

    @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]
