@@ -13,37 +13,46 @@ import AndroidNDK
13
13
import CoreFoundation
14
14
import Dispatch
15
15
import SystemPackage
16
+ import Socket
16
17
17
18
@available ( macOS 13 . 0 , iOS 13 . 0 , * )
18
19
public extension Looper {
19
20
20
21
// Swift structured concurrency executor that enqueues jobs on an Android Looper.
21
22
final class Executor : SerialExecutor , @unchecked Sendable {
22
23
23
- let eventFd : FileDescriptor
24
+ #if os(Android)
25
+ let eventFd : SocketDescriptor . Event
26
+ #endif
24
27
let looper : Looper
25
28
let queue = LockedState ( initialState: [ UnownedJob] ( ) )
26
29
27
30
/// Initialize with Android Looper
28
31
internal init ( looper: consuming Looper ) throws ( AndroidLooperError) {
32
+ #if os(Android)
33
+ let eventFd : SocketDescriptor . Event
29
34
// open fd
30
- let fd = eventfd ( 0 , EFD_CLOEXEC | EFD_NONBLOCK) // TODO: Move to System / Socket package
31
- if fd < 0 {
32
- throw . bionic( Errno ( rawValue: errno) )
35
+ do {
36
+ eventFd = try SocketDescriptor . Event ( 0 , flags: [ . closeOnExec, . nonBlocking] )
37
+ }
38
+ catch {
39
+ throw . bionic( error)
33
40
}
34
41
// initialize
35
- let eventFd = FileDescriptor ( rawValue: fd)
36
42
self . eventFd = eventFd
43
+ #endif
37
44
self . looper = looper
38
45
// Add fd to Looper
39
46
try configureLooper ( )
40
47
}
41
48
42
49
deinit {
50
+ #if os(Android)
43
51
if eventFd. rawValue != - 1 {
44
- _ = try ? looper. remove ( fileDescriptor: eventFd)
52
+ _ = try ? looper. remove ( fileDescriptor: . init ( rawValue : eventFd. rawValue ) )
45
53
try ? eventFd. close ( )
46
54
}
55
+ #endif
47
56
}
48
57
49
58
/// Enqueue a single job
@@ -66,36 +75,30 @@ internal extension Looper.Executor {
66
75
func configureLooper( ) throws ( AndroidLooperError) {
67
76
do {
68
77
// add to looper
69
- try looper. handle. add ( fileDescriptor: eventFd, callback: drainAExecutor, data: Unmanaged . passUnretained ( self ) . toOpaque ( ) ) . get ( )
78
+ try looper. handle. add ( fileDescriptor: . init ( rawValue : eventFd. rawValue ) , callback: drainAExecutor, data: Unmanaged . passUnretained ( self ) . toOpaque ( ) ) . get ( )
70
79
}
71
80
catch {
81
+ #if os(Android)
72
82
try ? eventFd. close ( )
83
+ #endif
73
84
throw error
74
85
}
75
86
}
76
87
77
88
/// Read number of remaining events from eventFd
78
89
var eventsRemaining : UInt64 {
79
90
get throws {
80
- var value = UInt64 ( 0 )
81
- try withUnsafeMutableBytes ( of: & value) {
82
- guard try eventFd. read ( into: $0) == MemoryLayout< UInt64> . size else {
83
- throw Errno . invalidArgument
84
- }
85
- }
86
-
87
- return value
91
+ #if os(Android)
92
+ try eventFd. read ( ) . rawValue
93
+ #endif
88
94
}
89
95
}
90
96
91
97
/// Increment number of remaining events on eventFd
92
98
func signal( ) throws {
93
- var value = UInt64 ( 1 )
94
- try withUnsafeBytes ( of: & value) { ( pointer) throws -> ( ) in
95
- guard try eventFd. write ( pointer) == MemoryLayout< UInt64> . size else {
96
- throw Errno . outOfRange
97
- }
98
- }
99
+ #if os(Android)
100
+ try eventFd. write ( 1 )
101
+ #endif
99
102
}
100
103
101
104
/// Drain job queue
0 commit comments