trilemma-dev / securexpc Goto Github PK
View Code? Open in Web Editor NEWA simple and secure XPC framework for Swift
License: MIT License
A simple and secure XPC framework for Swift
License: MIT License
SecureXPC/Sources/SecureXPC/Server/XPCMachServer.swift
Lines 204 to 208 in ab4e612
Fatal-error this code path to say that it's untested, and treat it as effectively not-yet-implemented.
...
Let's go with option 2 for now. I'd volunteer to try testing it out, but between end of year busyness at work and heading off on holiday soon I just won't have time before early January. Can revisit it then.
Originally posted by @jakaplan in #23 (comment)
As per title, my helper is crashing in Catalina & Big Sur. It is fine in Monterey. After launching it runs a bunch of route requests fine, but then crashes with EXC_BAD_ACCESS.
It doesn't seem to crash in any of my route handling code nor on any particular route call, so I'm assuming it must be something to do with my route definitions, although I can't see what. I am wondering if anything jumps out to you in the crash log?
Process: com.test.AppHelper.Debug [3028]
Path: /Library/PrivilegedHelperTools/com.test.AppHelper.Debug
Identifier: com.test.AppHelper.Debug
Version: 1.0.0-beta.4 (100)
Code Type: X86-64 (Native)
Parent Process: launchd [1]
Responsible: com.test.AppHelper.Debug [3028]
User ID: 0
Date/Time: 2022-05-01 18:11:55.299 +1200
OS Version: macOS 11.6 (20G165)
Report Version: 12
Anonymous UUID: DDCC3E05-CCE8-DB38-43E5-8378F721024C
Sleep/Wake UUID: C6BAE207-875A-4EB8-BF73-823D7FDC7C8E
Time Awake Since Boot: 15000 seconds
Time Since Wake: 6900 seconds
System Integrity Protection: enabled
Crashed Thread: 1 Dispatch queue: com.apple.root.default-qos.overcommit
Exception Type: EXC_BAD_ACCESS (SIGABRT)
Exception Codes: KERN_INVALID_ADDRESS at 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
VM Regions Near 0:
-->
__TEXT 10027c000-10070c000 [ 4672K] r-x/r-x SM=COW /Library/PrivilegedHelperTools/*.Debug
Application Specific Information:
=================================================================
==3028==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x7fff2ca35dc7 bp 0x7000037a0820 sp 0x7000037a0800 T2)
==3028==The signal is caused by a READ memory access.
==3028==Hint: address points to the zero page.
#0 0x7fff2ca35dc7 in swift::ResolveAsSymbolicReference::operator()(swift::Demangle::__runtime::SymbolicReferenceKind, swift::Demangle::__runtime::Directness, int, void const*)+0x37 (libswiftCore.dylib:x86_64+0x33cdc7)
#1 0x7fff2ca582dc in swift::Demangle::__runtime::Demangler::demangleSymbolicReference(unsigned char)+0x8c (libswiftCore.dylib:x86_64+0x35f2dc)
#2 0x7fff2ca552a7 in swift::Demangle::__runtime::Demangler::demangleType(__swift::__runtime::llvm::StringRef, std::__1::function<swift::Demangle::__runtime::Node* (swift::Demangle::__runtime::SymbolicReferenceKind, swift::Demangle::__runtime::Directness, int, void const*)>)+0xa7 (libswiftCore.dylib:x86_64+0x35c2a7)
#3 0x7fff2ca3b5a3 in swift_getTypeByMangledNameImpl(swift::MetadataRequest, __swift::__runtime::llvm::StringRef, void const* const*, std::__1::function<swift::TargetMetadata<swift::InProcess> const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>)+0x203 (libswiftCore.dylib:x86_64+0x3425a3)
#4 0x7fff2ca38d6c in swift::swift_getTypeByMangledName(swift::MetadataRequest, __swift::__runtime::llvm::StringRef, void const* const*, std::__1::function<swift::TargetMetadata<swift::InProcess> const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>)+0x1dc (libswiftCore.dylib:x86_64+0x33fd6c)
#5 0x7fff2ca38f9a in swift_getTypeByMangledNameInContext+0xaa (libswiftCore.dylib:x86_64+0x33ff9a)
#6 0x100553ee8 in __swift_instantiateConcreteTypeFromMangledName+0x58 (com.test.AppHelper.Debug:x86_64+0x1002d7ee8)
#7 0x7fff201a2805 in _dispatch_client_callout+0x7 (libdispatch.dylib:x86_64+0x3805)
#8 0x7fff201a398b in _dispatch_once_callout+0x13 (libdispatch.dylib:x86_64+0x498b)
#9 0x7fff2ca47169 in swift_once+0x19 (libswiftCore.dylib:x86_64+0x34e169)
#10 0x100678f80 in XPCRequestContext._currentForTask.unsafeMutableAddressor+0x30 (com.test.AppHelper.Debug:x86_64+0x1003fcf80)
#11 0x1006792d3 in $s9SecureXPC17XPCRequestContextC10setForTask10connection7message9operationScTyxq_GSo13OS_xpc_object_p_SoAI_pAHyKXEtKs5ErrorR_r0_lFZ+0x343 (com.test.AppHelper.Debug:x86_64+0x1003fd2d3)
#12 0x1005c8191 in XPCServer.handleMessage(connection:message:)+0x1b91 (com.test.AppHelper.Debug:x86_64+0x10034c191)
#13 0x1005c59e6 in XPCServer.handleEvent(connection:event:)+0x3e6 (com.test.AppHelper.Debug:x86_64+0x1003499e6)
#14 0x1005c4c7a in closure #1 in XPCServer.startClientConnection(_:)+0x1aa (com.test.AppHelper.Debug:x86_64+0x100348c7a)
#15 0x10053cd43 in thunk for @escaping @callee_guaranteed (@guaranteed OS_xpc_object) -> ()+0x93 (com.test.AppHelper.Debug:x86_64+0x1002c0d43)
#16 0x7fff20092c23 in _xpc_connection_call_event_handler+0x37 (libxpc.dylib:x86_64+0xcc23)
#17 0x7fff20091a9a in _xpc_connection_mach_event+0x3a9 (libxpc.dylib:x86_64+0xba9a)
#18 0x7fff201a28a5 in _dispatch_client_callout4+0x8 (libdispatch.dylib:x86_64+0x38a5)
#19 0x7fff201b9a9f in _dispatch_mach_msg_invoke+0x1bb (libdispatch.dylib:x86_64+0x1aa9f)
#20 0x7fff201a8492 in _dispatch_lane_serial_drain+0x106 (libdispatch.dylib:x86_64+0x9492)
#21 0x7fff201ba5e1 in _dispatch_mach_invoke+0x1e3 (libdispatch.dylib:x86_64+0x1b5e1)
#22 0x7fff201b2c0c in _dispatch_workloop_worker_thread+0x32a (libdispatch.dylib:x86_64+0x13c0c)
#23 0x7fff2034945c in _pthread_wqthread+0x139 (libsystem_pthread.dylib:x86_64+0x345c)
#24 0x7fff2034842e in start_wqthread+0xe (libsystem_pthread.dylib:x86_64+0x242e)
==3028==Register values:
rax = 0x0000000000000001 rbx = 0x00007000037a0b88 rcx = 0x000000000003556b rdx = 0x0000000000000000
rdi = 0x00007000037a0b88 rsi = 0x0000000000000000 rbp = 0x00007000037a0820 rsp = 0x00007000037a0800
r8 = 0x00000001006fd075 r9 = 0xffffe1ffff90bed0 r10 = 0x0000000000000001 r11 = 0x0000000000000000
r12 = 0x000000000000000e r13 = 0xffffffffffffffff r14 = 0x0000000000000001 r15 = 0x0000000000000000
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV (libswiftCore.dylib:x86_64+0x33cdc7) in swift::ResolveAsSymbolicReference::operator()(swift::Demangle::__runtime::SymbolicReferenceKind, swift::Demangle::__runtime::Directness, int, void const*)+0x37
Thread T2 created by T0 here:
<empty stack>
==3028==ABORTING
abort() called
Thread 0:
0 libsystem_pthread.dylib 0x00007fff20348420 start_wqthread + 0
Thread 1 Crashed:: Dispatch queue: com.apple.root.default-qos.overcommit
0 libsystem_kernel.dylib 0x00007fff2031d92e __pthread_kill + 10
1 libsystem_pthread.dylib 0x00007fff2034c5bd pthread_kill + 263
2 libsystem_c.dylib 0x00007fff202a1406 abort + 125
3 libclang_rt.asan_osx_dynamic.dylib 0x0000000100bc4916 __sanitizer::Abort() + 70
4 libclang_rt.asan_osx_dynamic.dylib 0x0000000100bc4044 __sanitizer::Die() + 196
5 libclang_rt.asan_osx_dynamic.dylib 0x0000000100baba66 __asan::ScopedInErrorReport::~ScopedInErrorReport() + 422
6 libclang_rt.asan_osx_dynamic.dylib 0x0000000100ba976d __asan::ReportDeadlySignal(__sanitizer::SignalContext const&) + 157
7 libclang_rt.asan_osx_dynamic.dylib 0x0000000100ba8eff __asan::AsanOnDeadlySignal(int, void*, void*) + 95
8 libsystem_platform.dylib 0x00007fff20391d7d _sigtramp + 29
9 ??? 000000000000000000 0 + 0
10 libswiftCore.dylib 0x00007fff2ca582dd swift::Demangle::__runtime::Demangler::demangleSymbolicReference(unsigned char) + 141
11 libswiftCore.dylib 0x00007fff2ca552a8 swift::Demangle::__runtime::Demangler::demangleType(__swift::__runtime::llvm::StringRef, std::__1::function<swift::Demangle::__runtime::Node* (swift::Demangle::__runtime::SymbolicReferenceKind, swift::Demangle::__runtime::Directness, int, void const*)>) + 168
12 libswiftCore.dylib 0x00007fff2ca3b5a4 swift_getTypeByMangledNameImpl(swift::MetadataRequest, __swift::__runtime::llvm::StringRef, void const* const*, std::__1::function<swift::TargetMetadata<swift::InProcess> const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>) + 516
13 libswiftCore.dylib 0x00007fff2ca38d6d swift::swift_getTypeByMangledName(swift::MetadataRequest, __swift::__runtime::llvm::StringRef, void const* const*, std::__1::function<swift::TargetMetadata<swift::InProcess> const* (unsigned int, unsigned int)>, std::__1::function<swift::TargetWitnessTable<swift::InProcess> const* (swift::TargetMetadata<swift::InProcess> const*, unsigned int)>) + 477
14 libswiftCore.dylib 0x00007fff2ca38f9b swift_getTypeByMangledNameInContext + 171
15 com.test.AppHelper.Debug 0x0000000100553ee9 __swift_instantiateConcreteTypeFromMangledName + 89
16 libdispatch.dylib 0x00007fff201a2806 _dispatch_client_callout + 8
17 libdispatch.dylib 0x00007fff201a398c _dispatch_once_callout + 20
18 libswiftCore.dylib 0x00007fff2ca4716a swift_once + 26
19 com.test.AppHelper.Debug 0x0000000100678f81 XPCRequestContext._currentForTask.unsafeMutableAddressor + 49
20 com.test.AppHelper.Debug 0x00000001006792d4 $s9SecureXPC17XPCRequestContextC10setForTask10connection7message9operationScTyxq_GSo13OS_xpc_object_p_SoAI_pAHyKXEtKs5ErrorR_r0_lFZ + 836
21 com.test.AppHelper.Debug 0x00000001005c8192 XPCServer.handleMessage(connection:message:) + 7058
22 com.test.AppHelper.Debug 0x00000001005c59e7 XPCServer.handleEvent(connection:event:) + 999
23 com.test.AppHelper.Debug 0x00000001005c4c7b closure #1 in XPCServer.startClientConnection(_:) + 427
24 com.test.AppHelper.Debug 0x000000010053cd44 thunk for @escaping @callee_guaranteed (@guaranteed OS_xpc_object) -> () + 148
25 libxpc.dylib 0x00007fff20092c24 _xpc_connection_call_event_handler + 56
26 libxpc.dylib 0x00007fff20091a9b _xpc_connection_mach_event + 938
27 libdispatch.dylib 0x00007fff201a28a6 _dispatch_client_callout4 + 9
28 libdispatch.dylib 0x00007fff201b9aa0 _dispatch_mach_msg_invoke + 444
29 libdispatch.dylib 0x00007fff201a8493 _dispatch_lane_serial_drain + 263
30 libdispatch.dylib 0x00007fff201ba5e2 _dispatch_mach_invoke + 484
31 libdispatch.dylib 0x00007fff201b2c0d _dispatch_workloop_worker_thread + 811
32 libsystem_pthread.dylib 0x00007fff2034945d _pthread_wqthread + 314
33 libsystem_pthread.dylib 0x00007fff2034842f start_wqthread + 15
Thread 2:
0 libsystem_kernel.dylib 0x00007fff2031cb0a __sigsuspend_nocancel + 10
1 libdispatch.dylib 0x00007fff201b34e1 _dispatch_sigsuspend + 36
2 libdispatch.dylib 0x00007fff201b34bd _dispatch_sig_thread + 53
Thread 1 crashed with X86 Thread State (64-bit):
rax: 0x0000000000000000 rbx: 0x00007000037a4000 rcx: 0x000070000379f658 rdx: 0x0000000000000000
rdi: 0x0000000000001803 rsi: 0x0000000000000006 rbp: 0x000070000379f680 rsp: 0x000070000379f658
r8: 0x0000000100c96107 r9: 0x0000000000000001 r10: 0x0000000000000000 r11: 0x0000000000000246
r12: 0x0000000000001803 r13: 0xffffffffffffffff r14: 0x0000000000000006 r15: 0x0000000000000016
rip: 0x00007fff2031d92e rfl: 0x0000000000000246 cr2: 0x000010002091bc00
Logical CPU: 0
Error Code: 0x02000148
Trap Number: 133
Thread 1 instruction stream:
89 98 05 00 e9 df fe ff-ff e8 cb d9 fe ff 0f 1f ................
00 f8 ff ff ff d0 ff ff-ff c0 ff ff ff cb ff ff ................
ff c7 ff ff ff 0f 1f 40-00 55 48 89 e5 41 57 41 [email protected]
56 53 50 89 d0 48 89 fb-48 63 d1 4c 01 c2 83 f8 VSP..H..Hc.L....
01 75 0e 40 84 f6 0f 85-8d 00 00 00 48 8b 12 eb [email protected]...
0f 40 84 f6 74 0a 48 8b-3b be 03 01 00 00 eb 21 [email protected].;......!
[8b]02 89 c1 80 e1 1f 80-f9 03 74 0b 80 f9 04 75 ..........t....u <==
1f 66 b8 05 01 eb 04 66-b8 98 00 48 8b 3b 0f b7 .f.....f...H.;..
f0 48 83 c4 08 5b 41 5e-41 5f 5d e9 09 fa 01 00 .H...[A^A_].....
45 31 f6 48 85 d2 74 33-83 e0 10 74 2e 48 8b 3b E1.H..t3...t.H.;
be c3 00 00 00 e8 ef f9-01 00 49 89 c7 48 8b 3b ..........I..H.;
be c2 00 00 00 e8 4f f9-01 00 49 89 c6 48 8b 13 ......O...I..H..
Thread 1 last branch register state not available.
It seems that XPCServer's setErrorHandler
won't be called, until the connection is closed. I believe it's a bug.
One thing that I've noticed in my use for SecureXPC was that all my clients' callbacks followed the same pattern: they all switch
on the result, and called a shared error handling function (in addition to some route-specific error handling).
I think this might be a common use case, especially because changes to the connection state might want to be monitored, and that can happen on every call.
Adding a "global" error handler that applies across all routes of a client would be easy, but then you'd get duplicate reports of errors (once per callback, and then once again). Ideally, having a way to express "if the route callback doesn't handle the error, then give the client error handle a chance to handle it", but I don't know how we could detect if the callback handled the error or not.
Do you have any ideas for how we could improve this?
XPCError/remote(_:)
is an unresolved reference.<# placeholders #>
work in docc. How did you intend for these to work, @jakaplan?With the way the server's routes are looked up today, a type error (e.g. a message is sent expecting a reply, but the server doesn't expect it to need a reply) ends up being diagnosed as routeNotRegistered
.
What do you think about centralizing all route storage into a single table? If we find a route with a different type than expected, then we can emit a more specific "type error".
This also has the (beneficial, IMO) side-effect of making routes unique by solely their names, and not their "kinds" (with/without message, with/without reply)
I ran into this when just trying stuff out and copy-pasting a route on the client and server end, at a time when my client and server code didn't have a "shared" package where I could centralize the route definition. With all duplication like that comes the risk of de-synchronization, and indeed, that's what happened.
Continuing on from #22 (comment) ...
Sorry for going off-topic, but I just wanted to thank you both for your work on this.
Thanks for the kind words! If you have any feedback or questions, don't hesitate to open discussion or topic issues.
I'm hoping to migrate my XPC code to use SecureXPC and really appreciate the effort going into this. I think a lot of people will benefit from this work over time — it's really needed.
If you don't mind me asking, what is your use case for XPC? Always good to understand the diverse ways it's being used to make sure we're considering that as SecureXPC evolves.
My macOS app needs a helper tool to run a bunch of stuff as root — fairly typical stuff I would imagine. I really like your client/server design metaphor particularly the idea of defining routes. I also started to view things this way but only after having written most of my XPC communication code, so it was too late to really integrate the idea.
One area of significant trepidation for me was security — I read lots about this and it was a steep learning curve. It really felt like Apple should be improving their frameworks around all this, or at the very least providing a good solid demo app. As you would know their sample code in this area is ancient and actually insecure (I believe). And given that the accepted wisdom is to never write your own security implementation, there's another reason a package like this, with many more eyes on it, is invaluable.
SO, I was pretty excited when I found your project — in addition to the good work shared by others I believe this will provide a really solid and secure foundation for developers who need it. Actually I don't think it's hyperbolic to say this is likely to measurably increase the quality/security of mac apps (I don't do much iOS right now so I don't really know if it's useful there).
I think we should support the native xpc_data
type
(That said, does it actually end up as a string?).
It's much worse than that. Storing it as a string (with say, Base64 for example), would have a 1.6x overhead (5 bits of payload per 8 bit character). The current encoding has an 8x overhead. It encodes one UInt8
of payload per UInt64
of payload.
Here's a section of one of my long comments
I think this is easy enough to do that it might be worth doing.
In particular, I've noticed that
Data
is really inefficient to encode. As it is today, it bloats sizes by 8x. It tries to encode itself as a[UInt8]
, but each byte gets widened into an 8 byteint64
in order to be passed toxpc_int64_create
. Luckily, there is not per-number object allocation overhead there (that would have been brutal for my use-case haha), because the only possible int64 values here would be 0-255, all of which fit into the tagged pointer representation.You can get a rough of idea of what that might look like, here: amomchilov/SecureXPC@add-intial-tests-for-XPCEncoder...Alex/new-scalar-support . Of course, I only changed the encoder half, and the decoder would need to match.
Originally posted by @amomchilov in #6 (comment)
If either of these types of servers are created, but never started, then when they are deallocated the following is thrown:
XPC API Misuse: Release of last reference on a suspended connection.
After a bunch of debugging and documentation reading I'm pretty sure what's going on is the following:
XPCAnonymousServer
and XPCMachServer
create listener connections as part of their initialization, but do not call xpc_connection_resume(...)
on them as part of initializingxpc_release
is unavailable in Swift, but appears to be called by the Swift runtime (or possibly inserted at compile time?) when the listener connection's retain count reaches zero due to the runtime's builtin reference countingxpc_release
is called on a listener connection the application/executable/test harness experiences a EXC_BAD_INSTRUCTION (SIGILL) due to the aforementioned: "XPC API Misuse: Release of last reference on a suspended connection."Reproducing this issue with a test is trivial, just create a new test and the entire function's body should just be:
XPCServer.makeAnonymous()
For the anonymous and the Mach services server, the connection turned into an endpoint is the listener connection. XPC Services don't have an (exposed) listener connection, so the implementation right now uses the incoming connection passed to xpc_main
which likely does not work. This issue is to verify whether or not this works.
I would like to contribute an integration test suite, which exercises the real client/server messaging functions. I would like to build this prior to making pretty big changes as part of #17 and #20.
As it turns out, this can be done without the need to spin up XPC or Mach services, using an anonymous XPC connection, whose ends both run within the same process. Quinn "The Eskimo!" documents how to do this with the NSXPCConnection APIs in Testing and Debugging XPC Code With an Anonymous Listener.
I found some more information about anonymous XPC connections in the C XPC APIs in WWDC 2013 - Session 702 - Efficient design with XPC (slides), at around 18:33 to 21:50, slides 47-56.
I've split the work into multiple PRs, each one building upon the previous
Hey!
First of all, AMAZING work!
I'm trying to use the framework to talk with a daemon launched by the new SMAppService
api. The daemon launches, launches server but I can't make it to talk with the client. It fails on the first condition. But...
I'm not sure that validateThisProcessIsAnSMAppServiceDaemon
should work at all. It checks for the Bundle
s bundleURL
and it assumes that it'll receive a path with the Contents
folder. It doesn't work in my case, it returns a path, but to the bundle itself (so the last component is *.app
). Am I doing something wrong?
Originally posted by @amomchilov in #6 (comment):
File descriptors and shared memory are intentionally not supported as they are concepts incompatible with Codable which is designed for indefinite serialization and cross-device transfer. As such choosing Codable for SecureXPC had tradeoffs because XPC does not have the same requirements — it can represent live resources on a single device. However, I decided the tradeoff was worth it because of Swift's excellent built in support for Codable (particularly the compiler automatically generating conformance in many cases). If there was a desire to support file descriptors and shared memory, I can envision ways to add them in the future, although it would mean expanding the API quite considerably.
I don't have an immediate need for passing FDs or shared memory regions in my program yet, but my app does need to pass an xpc_endpoint_t
. My main app is sandboxed, so the only way it can use SMBless
is by delegating that work to an unsandboxed XPC helper service. Once the installation is done, it opens the XPC connection to the privileged helper service, and passes the endpoint back to the main app.
This is the approach that's recommended in Apple's EvenBetterAuthorizationSample, so I think supporting xpc_endpoint_t
is pretty crucial. So at a minimum, at least some level of special-casing needs to happen.
After releasing 0.3.0 of SecureXPC I started updating SwiftAuthorizationSample and have run into an unexpected issue:
XPC_ERROR_CONNECTION_INTERRUPTED
.SMJobBless
).send
using the client results in a XPC_ERROR_CONNECTION_INVALID
send
using the client results in the service being launched.XPC_ERROR_CONNECTION_INVALID
in step 4 we perform self.connection = nil
which means we establish a new connection on the next send
.macOS 10.15 and later should support this (at least as of the latest Xcode beta)
👋 Hey there!
I have some things I'd like to ask about, that aren't necessarily issues, per-se. Could you enable GitHub discussions on these repos so I can post there?
P.S. Thanks for open sourcing these. It caught me just at the right time, when I was about to start refactoring a huge chunk of my apps structs into @objc
classes, solely to support NSCoding
for XPC. That would have been a major headache, and I'm really hoping I can leverage this package to stay in swift-struct-land!
This is in contrast to Double.isSignalingNaN
which does encode and decode as expected. The difference being that the Double
type is directly supported by XPC C API, while Float
is not and is wrapped in a Float
.
When there's no "reply" block, there's nowhere to detect errors that were encountered after sending a reply-less message.
dispatchMain()
will crash if called from any other thread
I searched the code repository, and didn't find any code to handle this situation. Is there a Codable FileHandle example?
Thanks!
This library looks great, but I want to use it for sharing a MTLSharedTextureHandle.
Unfortunately, this only seems possible to encode using a NSXPCCoder:
This seems like a huge limitation. Is this library a no-go in this case?
Currently:
forThisXPCService()
always returns the same instanceforThisBlessedHelperTool()
and forThisMachService(...)
return new instances each timeThis inconsistency is undesirable as the server is stateful with routes and an error handler.
However, in practice this probably wouldn't be much of an issue as it's hard to envision why one of these functions would be called multiple times.
I've set my server's target queue like so:
let server = try XPCServer.forThisBlessedHelperTool()
// Either one of:
server.targetQueue = DispatchQueue.global()
server.targetQueue = DispatchQueue(label: "queuename", attributes: .concurrent)
...but calling my test handler multiple times from the client still indicates them running serially.
static func testRouteHandler(withMsg msg: String) throws -> String {
let process = Process()
process.launchPath = "/bin/sleep"
process.arguments = ["4"]
process.launch()
process.waitUntilExit()
return "Finished: \(msg)"
}
I noticed the following in the xpc_connection_set_target_queue
documentation:
If the target queue is a concurrent queue, then XPC still guarantees that there will never be more than one invocation of the connection's event handler block executing concurrently.
Is what I'm trying to do the right approach?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.