CommandLineToolkit
96 строк · 2.4 Кб
1import AtomicModels
2import Dispatch
3import Foundation
4import Socket
5import SocketModels
6
7public final class StatsdClientImpl: StatsdClient {
8private let statsdSocketAddress: SocketAddress
9private let group = DispatchGroup()
10private let tornDown = AtomicValue(false)
11
12public struct InvalidSocketAddressError: Error, CustomStringConvertible {
13public let address: SocketAddress
14
15public var description: String {
16"Invalid socket address: \(address)"
17}
18}
19
20public init(
21statsdSocketAddress: SocketAddress
22) {
23self.statsdSocketAddress = statsdSocketAddress
24}
25
26public func send(
27content: Data,
28queue: DispatchQueue,
29completion: @escaping (Error?) -> ()
30) {
31guard tornDown.currentValue() == false else {
32return
33}
34
35guard let address = Socket.createAddress(
36for: statsdSocketAddress.host,
37on: Int32(statsdSocketAddress.port.value)
38) else {
39queue.async { [statsdSocketAddress] in
40completion(InvalidSocketAddressError(address: statsdSocketAddress))
41}
42return
43}
44
45queue.async { [group] in
46group.enter()
47defer {
48group.leave()
49}
50
51do {
52let socket = try Socket.create(
53type: .datagram,
54proto: .udp
55)
56defer {
57socket.close()
58}
59
60try socket.write(
61from: content,
62to: address
63)
64completion(nil)
65} catch {
66completion(error)
67}
68}
69}
70
71public func tearDown(
72queue: DispatchQueue,
73timeout: TimeInterval,
74completion: @escaping () -> ()
75) {
76self.tornDown.set(true)
77
78let tearDownCompleted = AtomicValue(false)
79
80let complete = {
81tearDownCompleted.withExclusiveAccess {
82if $0 == false {
83$0 = true
84completion()
85}
86}
87}
88
89group.notify(queue: queue) {
90complete()
91}
92queue.asyncAfter(deadline: .now() + timeout) {
93complete()
94}
95}
96}
97