From c28623fc315a3f379ca8f747f4ee3a96c8b9812a Mon Sep 17 00:00:00 2001 From: Hank Fox Date: Thu, 16 Nov 2017 11:04:46 -0800 Subject: [PATCH 1/7] Implement libdispatch queue_specific* and expose dispatch_barrier --- .../libdispatch/build/libdispatch.vcxproj | 29 +-- deps/3rdparty/libdispatch/build/project.json | 16 ++ deps/3rdparty/libdispatch/dispatch/queue.h | 215 +++++++++++++++++- deps/3rdparty/libdispatch/src/interop.c | 2 + deps/3rdparty/libdispatch/src/queue.c | 129 +++++++++++ .../3rdparty/libdispatch/src/queue_internal.h | 20 +- deps/3rdparty/libdispatch/src/queue_kevent.c | 2 + 7 files changed, 392 insertions(+), 21 deletions(-) create mode 100644 deps/3rdparty/libdispatch/build/project.json diff --git a/deps/3rdparty/libdispatch/build/libdispatch.vcxproj b/deps/3rdparty/libdispatch/build/libdispatch.vcxproj index 440bf501df..0d0d5ff6fe 100644 --- a/deps/3rdparty/libdispatch/build/libdispatch.vcxproj +++ b/deps/3rdparty/libdispatch/build/libdispatch.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -37,27 +37,27 @@ DynamicLibrary true Unicode - v140 + v141 DynamicLibrary true Unicode - v140 + v141 DynamicLibrary false true Unicode - v140 + v141 DynamicLibrary false true Unicode - v140 + v141 @@ -82,13 +82,11 @@ $(SolutionDir)obj\$(Platform)\$(TargetOsAndVersion)\$(Configuration)\ .;$(IncludePath) - libdispatch $(SolutionDir)obj\$(Platform)\$(TargetOsAndVersion)\$(Configuration)\ .;$(IncludePath) - libdispatch @@ -100,13 +98,11 @@ $(SolutionDir)obj\$(Platform)\$(TargetOsAndVersion)\$(Configuration)\ .;$(IncludePath) - libdispatch $(SolutionDir)obj\$(Platform)\$(TargetOsAndVersion)\$(Configuration)\ .;$(IncludePath) - libdispatch @@ -220,6 +216,7 @@ + @@ -249,19 +246,13 @@ - - $(WINOBJC_SDK_ROOT)\deps\prebuilt\Universal Windows\$(PlatformTarget)\$(Configuration)\\ + $(WINOBJC_SDK_ROOT)\tools\deps\prebuilt\\ + $(PrebuiltRoot)\Universal Windows\$(PlatformTarget)\$(Configuration)\\ - + - - - - - + - - \ No newline at end of file diff --git a/deps/3rdparty/libdispatch/build/project.json b/deps/3rdparty/libdispatch/build/project.json new file mode 100644 index 0000000000..985688eb16 --- /dev/null +++ b/deps/3rdparty/libdispatch/build/project.json @@ -0,0 +1,16 @@ +{ + "dependencies": { + "WinObjC.Language": "*", + }, + "frameworks": { + "uap10.0": { + "imports": "native" + } + }, + "runtimes": { + "win10-arm": {}, + "win10-x86": {}, + "win10-arm-aot": {}, + "win10-x86-aot": {} + } +} diff --git a/deps/3rdparty/libdispatch/dispatch/queue.h b/deps/3rdparty/libdispatch/dispatch/queue.h index 08f5bc3833..e4625e8df4 100644 --- a/deps/3rdparty/libdispatch/dispatch/queue.h +++ b/deps/3rdparty/libdispatch/dispatch/queue.h @@ -179,6 +179,133 @@ dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work); +/*! + * @function dispatch_barrier_async + * + * @abstract + * Submits a barrier block for asynchronous execution on a dispatch queue. + * + * @discussion + * Submits a block to a dispatch queue like dispatch_async(), but marks that + * block as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues). + * + * See dispatch_async() for details. + * + * @param queue + * The target dispatch queue to which the block is submitted. + * The system will hold a reference on the target queue until the block + * has finished. + * The result of passing NULL in this parameter is undefined. + * + * @param block + * The block to submit to the target dispatch queue. This function performs + * Block_copy() and Block_release() on behalf of callers. + * The result of passing NULL in this parameter is undefined. + */ + +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); +#endif + +/*! + * @function dispatch_barrier_async_f + * + * @abstract + * Submits a barrier function for asynchronous execution on a dispatch queue. + * + * @discussion + * Submits a function to a dispatch queue like dispatch_async_f(), but marks + * that function as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT + * queues). + * + * See dispatch_async_f() for details. + * + * @param queue + * The target dispatch queue to which the function is submitted. + * The system will hold a reference on the target queue until the function + * has returned. + * The result of passing NULL in this parameter is undefined. + * + * @param context + * The application-defined context parameter to pass to the function. + * + * @param work + * The application-defined function to invoke on the target queue. The first + * parameter passed to this function is the context provided to + * dispatch_barrier_async_f(). + * The result of passing NULL in this parameter is undefined. + */ + +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_barrier_async_f(dispatch_queue_t queue, + void *_Nullable context, + dispatch_function_t work); + +/*! + * @function dispatch_barrier_sync_f + * + * @abstract + * Submits a barrier function for synchronous execution on a dispatch queue. + * + * @discussion + * Submits a function to a dispatch queue like dispatch_sync_f(), but marks that + * fuction as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues). + * + * See dispatch_sync_f() for details. + * + * @param queue + * The target dispatch queue to which the function is submitted. + * The result of passing NULL in this parameter is undefined. + * + * @param context + * The application-defined context parameter to pass to the function. + * + * @param work + * The application-defined function to invoke on the target queue. The first + * parameter passed to this function is the context provided to + * dispatch_barrier_sync_f(). + * The result of passing NULL in this parameter is undefined. + */ + +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_barrier_sync_f(dispatch_queue_t queue, + void *_Nullable context, + dispatch_function_t work); + +/*! + * @function dispatch_barrier_sync + * + * @abstract + * Submits a barrier block for synchronous execution on a dispatch queue. + * + * @discussion + * Submits a block to a dispatch queue like dispatch_sync(), but marks that + * block as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues). + * + * See dispatch_sync() for details. + * + * @param queue + * The target dispatch queue to which the block is submitted. + * The result of passing NULL in this parameter is undefined. + * + * @param block + * The block to be invoked on the target dispatch queue. + * The result of passing NULL in this parameter is undefined. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); +#endif + /*! * @function dispatch_sync * @@ -592,6 +719,92 @@ DISPATCH_EXPORT DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_queue_t dispatch_get_current_thread_queue(); +/*! +* @function dispatch_queue_set_specific +* +* @abstract +* Sets the key/value data for the specified dispatch queue. +* +* @discussion +* Use this method to associate custom context data with a dispatch queue. +* Blocks executing on the queue can use the dispatch_get_specific function +* to retrieve this data while they are running. +* +* @param queue +* The queue on which to set the specified key/value data. This parameter must not be NULL. +* +* @param key +* The key you want to use to identify the associated context data. Keys are only compared +* as pointers and are never dereferenced. Thus, you can use a pointer to a static variable +* for a specific subsystem or any other value that allows you to identify the value uniquely. +* Specifying a pointer to a string constant is not recommended. NULL is not a valid value +* for the key and attempts to set context data with a NULL key are ignored. +* +* @param context +* The context data to associate with key. This parameter may be NULL. +* +* @param destructor +* A destructor function that you can use to release your context data. This parameter may be +* NULL. If context is NULL, your destructor function is ignored. +*/ + +__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *context, dispatch_function_t destructor); + +/*! +* @function dispatch_queue_get_specific +* +* @abstract +* Gets the value for the key associated with the specified dispatch queue. +* +* @discussion +* You can use this method to get the context data associated with a specific dispatch queue. +* Blocks executing on a queue can use the dispatch_get_specific function to retrieve the +* context associated with that specific queue instead. +* +* @param queue +* The queue containing the desired context data. This parameter must not be NULL. +* +* @param key +* The key that identifies the associated context data. Keys are only compared as +* pointers and are never dereferenced. Thus, you can use a pointer to a static variable +* for a specific subsystem or any other value that allows you to identify the value uniquely. +* Specifying a pointer to a string constant is not recommended. +* +* @result +* The context data associated with key or NULL if no context was found. +*/ +__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_PURE DISPATCH_WARN_RESULT +DISPATCH_NOTHROW +void * dispatch_queue_get_specific(dispatch_queue_t queue, const void *key); + +/*! +* @function dispatch_get_specific +* +* @abstract +* Returns the value for the key associated with the current dispatch queue. +* +* @discussion +* This function is intended to be called from a block executing in a dispatch queue. +* You use it to obtain context data associated with the queue. Calling this method +* from code not running in a dispatch queue returns NULL because there is no queue +* to provide context. +* +* @param key +* The key associated with the dispatch queue on which the current block is executing. +* Keys are only compared as pointers and never dereferenced. Passing a string constant +* directly is not recommended. +* +* @result +* The context value for the specified key; otherwise NULL if the key was not set for +* the queue (or its target queue) or the queue is a global concurrent queue. +*/ +__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +void * dispatch_get_specific(const void *key); + __DISPATCH_END_DECLS -#endif +#endif \ No newline at end of file diff --git a/deps/3rdparty/libdispatch/src/interop.c b/deps/3rdparty/libdispatch/src/interop.c index 6c1b3073e1..7f912726b3 100644 --- a/deps/3rdparty/libdispatch/src/interop.c +++ b/deps/3rdparty/libdispatch/src/interop.c @@ -42,6 +42,8 @@ struct dispatch_queue_s _dispatch_main_q = { /*.dq_items_head = */ 0, /*.dq_serialnum = */ 1, /*.dq_finalizer_ctxt = */ 0, + /*.dq_specific_q = */ NULL, + /*.dq_specific_list = */ NULL, /*.dq_manually_drained = */ 0, /*.dq_is_manually_draining = */ false, /*.dq_label = */ "com.apple.main-thread", diff --git a/deps/3rdparty/libdispatch/src/queue.c b/deps/3rdparty/libdispatch/src/queue.c index 81e8a50544..2089b95829 100644 --- a/deps/3rdparty/libdispatch/src/queue.c +++ b/deps/3rdparty/libdispatch/src/queue.c @@ -262,6 +262,8 @@ struct dispatch_queue_s _dispatch_root_queues[] = { /*.dq_items_head = */ 0, /*.dq_serialnum = */ 4, /*.dq_finalizer_ctxt = */ 0, + /*.dq_specific_q = */ NULL, + /*.dq_specific_list = */ NULL, /*.dq_manually_drained = */ 0, /*.dq_is_manually_draining = */ false, /*.dq_label = */ "com.apple.root.low-priority", @@ -281,6 +283,8 @@ struct dispatch_queue_s _dispatch_root_queues[] = { /*.dq_items_head = */ 0, /*.dq_serialnum = */ 5, /*.dq_finalizer_ctxt = */ 0, + /*.dq_specific_q = */ NULL, + /*.dq_specific_list = */ NULL, /*.dq_manually_drained = */ 0, /*.dq_is_manually_draining = */ false, /*.dq_label = */ "com.apple.root.low-overcommit-priority", @@ -300,6 +304,8 @@ struct dispatch_queue_s _dispatch_root_queues[] = { /*.dq_items_head = */ 0, /*.dq_serialnum = */ 6, /*.dq_finalizer_ctxt = */ 0, + /*.dq_specific_q = */ NULL, + /*.dq_specific_list = */ NULL, /*.dq_manually_drained = */ 0, /*.dq_is_manually_draining = */ false, /*.dq_label = */ "com.apple.root.default-priority", @@ -319,6 +325,8 @@ struct dispatch_queue_s _dispatch_root_queues[] = { /*.dq_items_head = */ 0, /*.dq_serialnum = */ 7, /*.dq_finalizer_ctxt = */ 0, + /*.dq_specific_q = */ NULL, + /*.dq_specific_list = */ NULL, /*.dq_manually_drained = */ 0, /*.dq_is_manually_draining = */ false, /*.dq_label = */ "com.apple.root.default-overcommit-priority", @@ -338,6 +346,8 @@ struct dispatch_queue_s _dispatch_root_queues[] = { /*.dq_items_head = */ 0, /*.dq_serialnum = */ 8, /*.dq_finalizer_ctxt = */ 0, + /*.dq_specific_q = */ NULL, + /*.dq_specific_list = */ NULL, /*.dq_manually_drained = */ 0, /*.dq_is_manually_draining = */ false, /*.dq_label = */ "com.apple.root.high-priority", @@ -357,6 +367,8 @@ struct dispatch_queue_s _dispatch_root_queues[] = { /*.dq_items_head = */ 0, /*.dq_serialnum = */ 9, /*.dq_finalizer_ctxt = */ 0, + /*.dq_specific_q = */ NULL, + /*.dq_specific_list = */ NULL, /*.dq_manually_drained = */ 0, /*.dq_is_manually_draining = */ false, /*.dq_label = */ "com.apple.root.high-overcommit-priority", @@ -609,6 +621,8 @@ _dispatch_queue_init(dispatch_queue_t dq) dq->dq_running = 0; dq->dq_width = 1; dq->dq_serialnum = dispatch_atomic_inc(&_dispatch_queue_serial_numbers) - 1; + dq->dq_specific_q = NULL; + dq->dq_specific_list = NULL, dq->dq_manually_drained = 0; dq->dq_is_manually_draining = false; } @@ -665,6 +679,26 @@ dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) #endif } +void _dispatch_queue_specific_list_release(dispatch_queue_specific_list_t dqsl) { + dispatch_queue_specific_t lastvar = NULL; + dispatch_queue_specific_t var; + + TAILQ_FOREACH(var, &dqsl->context_list, specific) { + if (lastvar) { + free(lastvar); + } + if (var->destructor) { + var->destructor(var->context); + } + lastvar = var; + } + if (lastvar) { + free(lastvar); + } + + free(dqsl); +} + // 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol void _dispatch_queue_dispose(dispatch_queue_t dq) @@ -682,6 +716,13 @@ _dispatch_queue_dispose(dispatch_queue_t dq) } #endif + if (slowpath(dq->dq_specific_q)) { + dispatch_release(dq->dq_specific_q); + } + if (slowpath(dq->dq_specific_list)) { + _dispatch_queue_specific_list_release(dq->dq_specific_list); + } + // trash the tail queue so that use after free will crash dq->dq_items_tail = (void *)(uintptr_t)0x200; @@ -1978,3 +2019,91 @@ dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *ctxt, void dispatch_resume(as_do(ds)); } +static void _dispatch_queue_init_specific_list(dispatch_queue_t dq) { + dispatch_queue_t newQueue = dispatch_queue_create("SpecificQueue", NULL); + dispatch_queue_specific_list_t dqsl; + dqsl = calloc(1, sizeof(struct dispatch_queue_specific_list_s)); + TAILQ_INIT(&dqsl->context_list); + newQueue->dq_specific_list = dqsl; + + if(slowpath(!dispatch_atomic_cmpxchg_pointer(&dq->dq_specific_q, NULL, newQueue))) { + dispatch_release(newQueue); + } +} + +static void _dispatch_queue_insert_specific(dispatch_queue_specific_t dqs) { + dispatch_queue_t dq = _dispatch_queue_get_current(); + dispatch_queue_specific_list_t dqsl = dq->dq_specific_list; + dispatch_queue_specific_t var; + + TAILQ_FOREACH(var, &dqsl->context_list, specific) { + if (var->key == dqs->key) { + if(var->destructor) { + var->destructor(var->context); + } + + // If a specific exists with this key, simply update the context on that specific + if(dqs->context) { + var->context = dqs->context; + var->destructor = dqs->destructor; + } else { + TAILQ_REMOVE(&dqsl->context_list, var, specific); + free(var); + } + free(dqs); + return; + } + } + + if(dqs->context) { + TAILQ_INSERT_TAIL(&dqsl->context_list, dqs, specific); + } +} + +void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *_Nullable context, dispatch_function_t _Nullable destructor) { + if (slowpath(!key)) { + return; + } + dispatch_queue_specific_t specific = calloc(1, sizeof(struct dispatch_queue_specific_s)); + specific->key = key; + specific->context = context; + specific->destructor = destructor; + + // Delayed initialization + if (slowpath(!queue->dq_specific_q)) { + _dispatch_queue_init_specific_list(queue); + } + + dispatch_barrier_sync_f(queue->dq_specific_q, specific, _dispatch_queue_insert_specific); +} + +static void _dispatch_queue_get_specific(void* context) { + dispatch_queue_t dq = _dispatch_queue_get_current(); + dispatch_queue_specific_list_t dqsl = dq->dq_specific_list; + dispatch_queue_specific_t var; + + // The parameter passed in is reference to a void* key + void** ctxt = context; + void* key = *ctxt; + + TAILQ_FOREACH(var, &dqsl->context_list, specific) { + if (var->key == key) { + *ctxt = var->context; + return; + } + } + *ctxt = NULL; +} + +void* _Nullable dispatch_queue_get_specific(dispatch_queue_t queue, const void *key) { + void* context = NULL; + if (queue->dq_specific_q) { + context = key; + dispatch_sync_f(queue->dq_specific_q, &context, _dispatch_queue_get_specific); + } + return context; +} + +void* _Nullable dispatch_get_specific(const void *key) { + return dispatch_queue_get_specific(_dispatch_queue_get_current(), key); +} \ No newline at end of file diff --git a/deps/3rdparty/libdispatch/src/queue_internal.h b/deps/3rdparty/libdispatch/src/queue_internal.h index e1954819e1..e1eef4378f 100644 --- a/deps/3rdparty/libdispatch/src/queue_internal.h +++ b/deps/3rdparty/libdispatch/src/queue_internal.h @@ -46,6 +46,9 @@ extern const struct dispatch_queue_vtable_s _dispatch_queue_vtable; #define DISPATCH_QUEUE_MIN_LABEL_SIZE 64 +DISPATCH_DECL(dispatch_queue_specific_list); +DISPATCH_DECL(dispatch_queue_specific); + #ifndef DISPATCH_NO_LEGACY #define DISPATCH_QUEUE_HEADER \ intptr_t dq_running; \ @@ -54,6 +57,8 @@ extern const struct dispatch_queue_vtable_s _dispatch_queue_vtable; struct dispatch_object_s *volatile dq_items_head; \ intptr_t dq_serialnum; \ void *dq_finalizer_ctxt; \ + struct dispatch_queue_s *dq_specific_q; \ + struct dispatch_queue_specific_list_s *dq_specific_list; \ dispatch_queue_finalizer_function_t dq_finalizer_func #else #define DISPATCH_QUEUE_HEADER \ @@ -63,10 +68,23 @@ extern const struct dispatch_queue_vtable_s _dispatch_queue_vtable; struct dispatch_object_s *volatile dq_items_head; \ intptr_t dq_serialnum; \ void *dq_finalizer_ctxt; \ + struct dispatch_queue_s *dq_specific_q; \ + struct dispatch_queue_specific_list_s *dq_specific_list; \ void* dq_manually_drained; \ bool dq_is_manually_draining #endif +struct dispatch_queue_specific_list_s { + TAILQ_HEAD(dispatch_queue_specific_head_s, dispatch_queue_specific_s) context_list; +}; + +struct dispatch_queue_specific_s { + const void *key; + void *context; + dispatch_function_t destructor; + TAILQ_ENTRY(dispatch_queue_specific_s) specific; +}; + struct dispatch_queue_s { DISPATCH_STRUCT_HEADER(dispatch_queue_s, dispatch_queue_vtable_s); DISPATCH_QUEUE_HEADER; @@ -121,4 +139,4 @@ _dispatch_queue_get_current(void) return _dispatch_thread_getspecific(dispatch_queue_key); } -#endif +#endif \ No newline at end of file diff --git a/deps/3rdparty/libdispatch/src/queue_kevent.c b/deps/3rdparty/libdispatch/src/queue_kevent.c index e903ba413a..3d738edd90 100644 --- a/deps/3rdparty/libdispatch/src/queue_kevent.c +++ b/deps/3rdparty/libdispatch/src/queue_kevent.c @@ -512,6 +512,8 @@ struct dispatch_queue_s _dispatch_mgr_q = { /*.dq_items_head = */ 0, /*.dq_serialnum = */ 2, /*.dq_finalizer_ctxt = */ 0, + /*.dq_specific_q = */ NULL, + /*.dq_specific_list = */ NULL, /*.dq_manually_drained = */ 0, /*.dq_is_manually_draining = */ false, /*.dq_label = */ "com.apple.libdispatch-manager", From 725509948f6cf37e8db196a6bd369e1e565979af Mon Sep 17 00:00:00 2001 From: Hank Fox Date: Thu, 16 Nov 2017 11:05:35 -0800 Subject: [PATCH 2/7] Check in libdispatch binaries with queue_specific* --- msvc/sdk-build.props | 2 +- .../ARM/Debug/libdispatch.dll | 4 +- .../ARM/Debug/libdispatch.lib | 4 +- .../ARM/Debug/libdispatch.pdb | 4 +- .../ARM/Release/libdispatch.dll | 4 +- .../ARM/Release/libdispatch.lib | 4 +- .../ARM/Release/libdispatch.pdb | 4 +- .../x86/Debug/libdispatch.dll | 4 +- .../x86/Debug/libdispatch.lib | 4 +- .../x86/Debug/libdispatch.pdb | 4 +- .../x86/Release/libdispatch.dll | 4 +- .../x86/Release/libdispatch.lib | 4 +- .../x86/Release/libdispatch.pdb | 4 +- tools/deps/prebuilt/include/dispatch/queue.h | 215 +++++++++++++++++- .../include/dispatch/queue_internal.h | 117 ++++++---- 15 files changed, 308 insertions(+), 74 deletions(-) diff --git a/msvc/sdk-build.props b/msvc/sdk-build.props index 30ed61a228..6a07eaa27d 100644 --- a/msvc/sdk-build.props +++ b/msvc/sdk-build.props @@ -97,7 +97,7 @@ true -Werror -Wno-microsoft --system-header-prefix=winrt/ %(AdditionalOptions) _WINOBJC_DO_NOT_USE_NSLOG=;%(PreprocessorDefinitions) - %(InternalSystemIncludePaths);$(IncludePath);$(MSBuildThisFileDirectory)..\include\;$(MSBuildThisFileDirectory)..\include\xplat;$(MSBuildThisFileDirectory)..\deps\prebuilt\include\;$(MSBuildThisFileDirectory)..\deps\prebuilt\include\icu;$([System.IO.Path]::Combine('$(MSBuildThisFileDirectory)..\include\Platform', '$(TargetOsAndVersion)')) + %(InternalSystemIncludePaths);$(IncludePath);$(MSBuildThisFileDirectory)..\include\;$(MSBuildThisFileDirectory)..\include\xplat;$(MSBuildThisFileDirectory)..\deps\prebuilt\include\;$(MSBuildThisFileDirectory)..\tools\deps\prebuilt\include\;$(MSBuildThisFileDirectory)..\deps\prebuilt\include\icu;$([System.IO.Path]::Combine('$(MSBuildThisFileDirectory)..\include\Platform', '$(TargetOsAndVersion)')) %(IncludePaths);$(MSBuildThisFileDirectory)..\Frameworks\include\; %(InternalForceIncludes);StubIncludes.h; diff --git a/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.dll b/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.dll index 177ac32218..bc574e157c 100644 --- a/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.dll +++ b/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:047e427337bca9a2a73e2c462ef7a92aefab1efefda28153ac99948fbdd5c765 -size 190464 +oid sha256:c8ab9ec1d0ab33c38e4cc735d7cd6535149b6743ce43c7b23f96579b8d0b1429 +size 194560 diff --git a/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.lib b/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.lib index 602b72042c..af6458c730 100644 --- a/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.lib +++ b/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:468c6a10c86e545d6ff405aeb08122e179dcaee2d2f6587ec6fdef60ca3b63f5 -size 19764 +oid sha256:7c5b287c058a55c9e9d281bf24caa9ca9f62aacf70e886231929f9f402c060f8 +size 20776 diff --git a/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.pdb b/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.pdb index 78e4e0832c..caa9f98a09 100644 --- a/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.pdb +++ b/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:519d29ec418c22157d047a76b682f2640e8d8cac2de5878c78f1f0222019542c -size 2240512 +oid sha256:466b4a82f9324e33fc63fe8060b64e72beb1d2cc6100b906935534b18da0d126 +size 1110016 diff --git a/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.dll b/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.dll index bc2b1476e6..53a4445f3b 100644 --- a/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.dll +++ b/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:efbc9789aab69064e8dbbebae406717c4d407db5ec50a59216dbdf0c3cf54e5a -size 57344 +oid sha256:53d5544be5148bf3713c9e30e545d7552b0325bdeea81a5a64168ef23b276a3d +size 56320 diff --git a/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.lib b/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.lib index 159f7a983d..af6458c730 100644 --- a/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.lib +++ b/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31b1386a8d8e6b493a10a58359fd19f3d1542b4a079f0333e6227bba8514fda2 -size 19764 +oid sha256:7c5b287c058a55c9e9d281bf24caa9ca9f62aacf70e886231929f9f402c060f8 +size 20776 diff --git a/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.pdb b/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.pdb index e45f082c38..4768a11815 100644 --- a/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.pdb +++ b/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:27d3c365a731678aba136597787384cddd5f16b9d85435d7761b185420d17273 -size 1609728 +oid sha256:2168db43469509c62f7b080bde04014a70e3ee82d4c57d184e77a0e77f3a6087 +size 1626112 diff --git a/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.dll b/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.dll index 2de0107607..6b68e0e187 100644 --- a/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.dll +++ b/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:63944e6091d708d0ae10d93c68cd297da77ed25df62b1686f7d5cb7a3c9b0576 -size 137216 +oid sha256:e97de022f5aeb53689b9cf21dacceaa94e961b0e6edc8d98a74a2bedd9d434ec +size 136192 diff --git a/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.lib b/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.lib index 8994fdba74..10dea5a488 100644 --- a/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.lib +++ b/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab5c2ed2b8f58a24ee41a62ebdf81a2a66423df52fb9eaef5bf73ec4b64ec30e -size 20120 +oid sha256:c076d0174b382f58b43a2cdfcf79d30897b820a532da2fa84d47f7fc37775380 +size 21150 diff --git a/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.pdb b/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.pdb index 476600f56e..2359982132 100644 --- a/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.pdb +++ b/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2807d53d92db40b7d2b80f8732e58eb8b23acd92b6ead882bef1b6e5b6261d3d -size 2207744 +oid sha256:381ba7579b8f37d51d29d7ba10049c73467bf723338d5caa5535036b12d86518 +size 1085440 diff --git a/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.dll b/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.dll index c8d247706f..376fe3b746 100644 --- a/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.dll +++ b/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:72cfcf9acd7aaa92d620d23a81ae56b15c958eb1984652c557fadcee15fc22b4 -size 53248 +oid sha256:899fdbc25acabcbd8c321823f421c15f301cf23e03ff7ff19ee7fe3903da86ce +size 52224 diff --git a/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.lib b/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.lib index f018667225..10dea5a488 100644 --- a/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.lib +++ b/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.lib @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9032c4fd2931519af723de703b0f6cd0cc07cc4ab869f6daaddc901e1a8b43a6 -size 20120 +oid sha256:c076d0174b382f58b43a2cdfcf79d30897b820a532da2fa84d47f7fc37775380 +size 21150 diff --git a/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.pdb b/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.pdb index 00beecd0a5..a9eafb1e1e 100644 --- a/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.pdb +++ b/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7958100ed7fd752b7ce30c7e5cb13364402e6746bb37babf25dc6aef876373a1 -size 1642496 +oid sha256:e047e438ca7cc8efc2b40799c4da059b3921026edae79c5b9586ab27b9fd54db +size 1658880 diff --git a/tools/deps/prebuilt/include/dispatch/queue.h b/tools/deps/prebuilt/include/dispatch/queue.h index 08f5bc3833..e4625e8df4 100644 --- a/tools/deps/prebuilt/include/dispatch/queue.h +++ b/tools/deps/prebuilt/include/dispatch/queue.h @@ -179,6 +179,133 @@ dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work); +/*! + * @function dispatch_barrier_async + * + * @abstract + * Submits a barrier block for asynchronous execution on a dispatch queue. + * + * @discussion + * Submits a block to a dispatch queue like dispatch_async(), but marks that + * block as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues). + * + * See dispatch_async() for details. + * + * @param queue + * The target dispatch queue to which the block is submitted. + * The system will hold a reference on the target queue until the block + * has finished. + * The result of passing NULL in this parameter is undefined. + * + * @param block + * The block to submit to the target dispatch queue. This function performs + * Block_copy() and Block_release() on behalf of callers. + * The result of passing NULL in this parameter is undefined. + */ + +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); +#endif + +/*! + * @function dispatch_barrier_async_f + * + * @abstract + * Submits a barrier function for asynchronous execution on a dispatch queue. + * + * @discussion + * Submits a function to a dispatch queue like dispatch_async_f(), but marks + * that function as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT + * queues). + * + * See dispatch_async_f() for details. + * + * @param queue + * The target dispatch queue to which the function is submitted. + * The system will hold a reference on the target queue until the function + * has returned. + * The result of passing NULL in this parameter is undefined. + * + * @param context + * The application-defined context parameter to pass to the function. + * + * @param work + * The application-defined function to invoke on the target queue. The first + * parameter passed to this function is the context provided to + * dispatch_barrier_async_f(). + * The result of passing NULL in this parameter is undefined. + */ + +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_barrier_async_f(dispatch_queue_t queue, + void *_Nullable context, + dispatch_function_t work); + +/*! + * @function dispatch_barrier_sync_f + * + * @abstract + * Submits a barrier function for synchronous execution on a dispatch queue. + * + * @discussion + * Submits a function to a dispatch queue like dispatch_sync_f(), but marks that + * fuction as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues). + * + * See dispatch_sync_f() for details. + * + * @param queue + * The target dispatch queue to which the function is submitted. + * The result of passing NULL in this parameter is undefined. + * + * @param context + * The application-defined context parameter to pass to the function. + * + * @param work + * The application-defined function to invoke on the target queue. The first + * parameter passed to this function is the context provided to + * dispatch_barrier_sync_f(). + * The result of passing NULL in this parameter is undefined. + */ + +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_barrier_sync_f(dispatch_queue_t queue, + void *_Nullable context, + dispatch_function_t work); + +/*! + * @function dispatch_barrier_sync + * + * @abstract + * Submits a barrier block for synchronous execution on a dispatch queue. + * + * @discussion + * Submits a block to a dispatch queue like dispatch_sync(), but marks that + * block as a barrier (relevant only on DISPATCH_QUEUE_CONCURRENT queues). + * + * See dispatch_sync() for details. + * + * @param queue + * The target dispatch queue to which the block is submitted. + * The result of passing NULL in this parameter is undefined. + * + * @param block + * The block to be invoked on the target dispatch queue. + * The result of passing NULL in this parameter is undefined. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_4_0) +DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); +#endif + /*! * @function dispatch_sync * @@ -592,6 +719,92 @@ DISPATCH_EXPORT DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW dispatch_queue_t dispatch_get_current_thread_queue(); +/*! +* @function dispatch_queue_set_specific +* +* @abstract +* Sets the key/value data for the specified dispatch queue. +* +* @discussion +* Use this method to associate custom context data with a dispatch queue. +* Blocks executing on the queue can use the dispatch_get_specific function +* to retrieve this data while they are running. +* +* @param queue +* The queue on which to set the specified key/value data. This parameter must not be NULL. +* +* @param key +* The key you want to use to identify the associated context data. Keys are only compared +* as pointers and are never dereferenced. Thus, you can use a pointer to a static variable +* for a specific subsystem or any other value that allows you to identify the value uniquely. +* Specifying a pointer to a string constant is not recommended. NULL is not a valid value +* for the key and attempts to set context data with a NULL key are ignored. +* +* @param context +* The context data to associate with key. This parameter may be NULL. +* +* @param destructor +* A destructor function that you can use to release your context data. This parameter may be +* NULL. If context is NULL, your destructor function is ignored. +*/ + +__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_NOTHROW +void dispatch_queue_set_specific(dispatch_queue_t queue, const void *key, void *context, dispatch_function_t destructor); + +/*! +* @function dispatch_queue_get_specific +* +* @abstract +* Gets the value for the key associated with the specified dispatch queue. +* +* @discussion +* You can use this method to get the context data associated with a specific dispatch queue. +* Blocks executing on a queue can use the dispatch_get_specific function to retrieve the +* context associated with that specific queue instead. +* +* @param queue +* The queue containing the desired context data. This parameter must not be NULL. +* +* @param key +* The key that identifies the associated context data. Keys are only compared as +* pointers and are never dereferenced. Thus, you can use a pointer to a static variable +* for a specific subsystem or any other value that allows you to identify the value uniquely. +* Specifying a pointer to a string constant is not recommended. +* +* @result +* The context data associated with key or NULL if no context was found. +*/ +__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_NONNULL1 DISPATCH_PURE DISPATCH_WARN_RESULT +DISPATCH_NOTHROW +void * dispatch_queue_get_specific(dispatch_queue_t queue, const void *key); + +/*! +* @function dispatch_get_specific +* +* @abstract +* Returns the value for the key associated with the current dispatch queue. +* +* @discussion +* This function is intended to be called from a block executing in a dispatch queue. +* You use it to obtain context data associated with the queue. Calling this method +* from code not running in a dispatch queue returns NULL because there is no queue +* to provide context. +* +* @param key +* The key associated with the dispatch queue on which the current block is executing. +* Keys are only compared as pointers and never dereferenced. Passing a string constant +* directly is not recommended. +* +* @result +* The context value for the specified key; otherwise NULL if the key was not set for +* the queue (or its target queue) or the queue is a global concurrent queue. +*/ +__OSX_AVAILABLE_STARTING(__MAC_10_7, __IPHONE_5_0) +DISPATCH_EXPORT DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +void * dispatch_get_specific(const void *key); + __DISPATCH_END_DECLS -#endif +#endif \ No newline at end of file diff --git a/tools/deps/prebuilt/include/dispatch/queue_internal.h b/tools/deps/prebuilt/include/dispatch/queue_internal.h index 3a332dfa80..e1eef4378f 100644 --- a/tools/deps/prebuilt/include/dispatch/queue_internal.h +++ b/tools/deps/prebuilt/include/dispatch/queue_internal.h @@ -2,19 +2,19 @@ * Copyright (c) 2008-2009 Apple Inc. All rights reserved. * * @APPLE_APACHE_LICENSE_HEADER_START@ - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * + * * @APPLE_APACHE_LICENSE_HEADER_END@ */ @@ -32,45 +32,63 @@ #include // for HeaderDoc #endif -#define DISPATCH_OBJ_ASYNC_BIT 0x1 -#define DISPATCH_OBJ_BARRIER_BIT 0x2 -#define DISPATCH_OBJ_GROUP_BIT 0x4 +#define DISPATCH_OBJ_ASYNC_BIT 0x1 +#define DISPATCH_OBJ_BARRIER_BIT 0x2 +#define DISPATCH_OBJ_GROUP_BIT 0x4 // vtables are pointers far away from the low page in memory -#define DISPATCH_OBJ_IS_VTABLE(x) ((uintptr_t)(x)->do_vtable > 127ul) +#define DISPATCH_OBJ_IS_VTABLE(x) ((uintptr_t)(x)->do_vtable > 127ul) struct dispatch_queue_vtable_s { - DISPATCH_VTABLE_HEADER(dispatch_queue_s); + DISPATCH_VTABLE_HEADER(dispatch_queue_s); }; extern const struct dispatch_queue_vtable_s _dispatch_queue_vtable; -#define DISPATCH_QUEUE_MIN_LABEL_SIZE 64 +#define DISPATCH_QUEUE_MIN_LABEL_SIZE 64 + +DISPATCH_DECL(dispatch_queue_specific_list); +DISPATCH_DECL(dispatch_queue_specific); #ifndef DISPATCH_NO_LEGACY -#define DISPATCH_QUEUE_HEADER \ - intptr_t dq_running; \ - intptr_t dq_width; \ - struct dispatch_object_s* dq_items_tail; \ - struct dispatch_object_s* volatile dq_items_head; \ - intptr_t dq_serialnum; \ - void* dq_finalizer_ctxt; \ - dispatch_queue_finalizer_function_t dq_finalizer_func +#define DISPATCH_QUEUE_HEADER \ + intptr_t dq_running; \ + intptr_t dq_width; \ + struct dispatch_object_s *dq_items_tail; \ + struct dispatch_object_s *volatile dq_items_head; \ + intptr_t dq_serialnum; \ + void *dq_finalizer_ctxt; \ + struct dispatch_queue_s *dq_specific_q; \ + struct dispatch_queue_specific_list_s *dq_specific_list; \ + dispatch_queue_finalizer_function_t dq_finalizer_func #else -#define DISPATCH_QUEUE_HEADER \ - intptr_t dq_running; \ - intptr_t dq_width; \ - struct dispatch_object_s* dq_items_tail; \ - struct dispatch_object_s* volatile dq_items_head; \ - intptr_t dq_serialnum; \ - void* dq_finalizer_ctxt; \ - void* dq_manually_drained; \ - bool dq_is_manually_draining +#define DISPATCH_QUEUE_HEADER \ + intptr_t dq_running; \ + intptr_t dq_width; \ + struct dispatch_object_s *dq_items_tail; \ + struct dispatch_object_s *volatile dq_items_head; \ + intptr_t dq_serialnum; \ + void *dq_finalizer_ctxt; \ + struct dispatch_queue_s *dq_specific_q; \ + struct dispatch_queue_specific_list_s *dq_specific_list; \ + void* dq_manually_drained; \ + bool dq_is_manually_draining #endif +struct dispatch_queue_specific_list_s { + TAILQ_HEAD(dispatch_queue_specific_head_s, dispatch_queue_specific_s) context_list; +}; + +struct dispatch_queue_specific_s { + const void *key; + void *context; + dispatch_function_t destructor; + TAILQ_ENTRY(dispatch_queue_specific_s) specific; +}; + struct dispatch_queue_s { - DISPATCH_STRUCT_HEADER(dispatch_queue_s, dispatch_queue_vtable_s); - DISPATCH_QUEUE_HEADER; - char dq_label[DISPATCH_QUEUE_MIN_LABEL_SIZE]; // must be last + DISPATCH_STRUCT_HEADER(dispatch_queue_s, dispatch_queue_vtable_s); + DISPATCH_QUEUE_HEADER; + char dq_label[DISPATCH_QUEUE_MIN_LABEL_SIZE]; // must be last }; extern struct dispatch_queue_s _dispatch_mgr_q; @@ -81,23 +99,25 @@ extern struct dispatch_queue_s _dispatch_root_queues[]; void _dispatch_queue_init(dispatch_queue_t dq); void _dispatch_queue_drain(dispatch_queue_t dq); void _dispatch_queue_dispose(dispatch_queue_t dq); -void _dispatch_queue_push_list_slow(dispatch_queue_t dq, struct dispatch_object_s* obj); +void _dispatch_queue_push_list_slow(dispatch_queue_t dq, struct dispatch_object_s *obj); void _dispatch_queue_serial_drain_till_empty(dispatch_queue_t dq); void _dispatch_force_cache_cleanup(void); DISPATCH_INLINE -static void _dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, dispatch_object_t _tail) { - struct dispatch_object_s *prev, *head = _head._do, *tail = _tail._do; - - tail->do_next = NULL; - prev = fastpath(dispatch_atomic_xchg_pointer(&dq->dq_items_tail, tail)); - if (prev) { - // if we crash here with a value less than 0x1000, then we are at a known bug in client code - // for example, see _dispatch_queue_dispose or _dispatch_atfork_child - prev->do_next = head; - } else { - _dispatch_queue_push_list_slow(dq, head); - } +static void +_dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _head, dispatch_object_t _tail) +{ + struct dispatch_object_s *prev, *head = _head._do, *tail = _tail._do; + + tail->do_next = NULL; + prev = fastpath(dispatch_atomic_xchg_pointer(&dq->dq_items_tail, tail)); + if (prev) { + // if we crash here with a value less than 0x1000, then we are at a known bug in client code + // for example, see _dispatch_queue_dispose or _dispatch_atfork_child + prev->do_next = head; + } else { + _dispatch_queue_push_list_slow(dq, head); + } } #define _dispatch_queue_push(x, y) _dispatch_queue_push_list((x), (y), (y)) @@ -107,15 +127,16 @@ static void _dispatch_queue_push_list(dispatch_queue_t dq, dispatch_object_t _he #if DISPATCH_DEBUG void dispatch_debug_queue(dispatch_queue_t dq, const char* str); #else -static DISPATCH_INLINE void dispatch_debug_queue(dispatch_queue_t dq DISPATCH_UNUSED, const char* str DISPATCH_UNUSED) { -} +static DISPATCH_INLINE void dispatch_debug_queue(dispatch_queue_t dq DISPATCH_UNUSED, const char* str DISPATCH_UNUSED) {} #endif size_t dispatch_queue_debug(dispatch_queue_t dq, char* buf, size_t bufsiz); size_t dispatch_queue_debug_attr(dispatch_queue_t dq, char* buf, size_t bufsiz); -static DISPATCH_INLINE dispatch_queue_t _dispatch_queue_get_current(void) { - return _dispatch_thread_getspecific(dispatch_queue_key); +static DISPATCH_INLINE dispatch_queue_t +_dispatch_queue_get_current(void) +{ + return _dispatch_thread_getspecific(dispatch_queue_key); } -#endif +#endif \ No newline at end of file From be3a82090461b296479f91759f156b66863e2c9e Mon Sep 17 00:00:00 2001 From: Hank Fox Date: Wed, 15 Nov 2017 12:10:07 -0800 Subject: [PATCH 3/7] Implement new tests for dispatch_queue_specific* --- .../Foundation/Foundation.UnitTests.vcxproj | 3 +- .../Foundation.UnitTests.vcxproj.filters | 3 +- tests/UnitTests/Foundation/DispatchTests.mm | 200 ++++++++++++++++++ 3 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 tests/UnitTests/Foundation/DispatchTests.mm diff --git a/build/Tests/UnitTests/Foundation/Foundation.UnitTests.vcxproj b/build/Tests/UnitTests/Foundation/Foundation.UnitTests.vcxproj index e73754a956..84debf020f 100644 --- a/build/Tests/UnitTests/Foundation/Foundation.UnitTests.vcxproj +++ b/build/Tests/UnitTests/Foundation/Foundation.UnitTests.vcxproj @@ -317,6 +317,7 @@ + @@ -349,4 +350,4 @@ - + \ No newline at end of file diff --git a/build/Tests/UnitTests/Foundation/Foundation.UnitTests.vcxproj.filters b/build/Tests/UnitTests/Foundation/Foundation.UnitTests.vcxproj.filters index 3f3df23daf..0a5fe798d5 100644 --- a/build/Tests/UnitTests/Foundation/Foundation.UnitTests.vcxproj.filters +++ b/build/Tests/UnitTests/Foundation/Foundation.UnitTests.vcxproj.filters @@ -152,6 +152,7 @@ + @@ -162,4 +163,4 @@ {1beaf0a2-ff8a-415b-ac31-aeb41c89f25d} - + \ No newline at end of file diff --git a/tests/UnitTests/Foundation/DispatchTests.mm b/tests/UnitTests/Foundation/DispatchTests.mm new file mode 100644 index 0000000000..b23245aaa2 --- /dev/null +++ b/tests/UnitTests/Foundation/DispatchTests.mm @@ -0,0 +1,200 @@ +//****************************************************************************** +// +// Copyright (c) Microsoft. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +#import +#import +#import + +static const char* key = "Specific Key"; +static const char* key2 = "Specific Key2"; +static const char* key3 = "Specific Key3"; +static char* context = "Specific Context 1"; +static char* context2 = "Specific Context 2"; +static char* context3 = "Specific Context 3"; +static int destructorCount = 0; + +// Create a 1 second end time for the test to timeout. +static dispatch_time_t _createEndTestTime() { + return dispatch_time(DISPATCH_TIME_NOW, 1000000000000); +} + +static void destructor(void* x){ + destructorCount++; +} + +TEST(DispatchTests, SimpleSpecific) { + dispatch_queue_t queue = dispatch_queue_create("queue", nullptr); + dispatch_group_t group = dispatch_group_create(); + + dispatch_queue_set_specific(queue, key, context, nullptr); + + dispatch_group_async(group, queue, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); + dispatch_queue_set_specific(queue, key, context2, nullptr); + }); + + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + EXPECT_EQ(context2, dispatch_queue_get_specific(queue, key)); + + dispatch_group_async(group, queue, ^{ + EXPECT_EQ(context2, dispatch_get_specific(key)); + }); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + dispatch_release(group); + dispatch_release(queue); +} + +TEST(DispatchTests, MultipleSpecificKeys) { + dispatch_queue_t queue = dispatch_queue_create("queue", nullptr); + dispatch_group_t group = dispatch_group_create(); + + dispatch_queue_set_specific(queue, key, context, nullptr); + dispatch_queue_set_specific(queue, key2, context2, nullptr); + + dispatch_group_async(group, queue, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); + EXPECT_EQ(context2, dispatch_get_specific(key2)); + dispatch_queue_set_specific(queue, key, context2, nullptr); + dispatch_queue_set_specific(queue, key2, context, nullptr); + }); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + EXPECT_EQ(context2, dispatch_queue_get_specific(queue, key)); + EXPECT_EQ(context, dispatch_queue_get_specific(queue, key2)); + + dispatch_queue_set_specific(queue, key3, context3, nullptr); + + dispatch_group_async(group, queue, ^{ + EXPECT_EQ(context2, dispatch_get_specific(key)); + EXPECT_EQ(context, dispatch_get_specific(key2)); + EXPECT_EQ(context3, dispatch_get_specific(key3)); + }); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + dispatch_release(queue); + dispatch_release(group); +} + +TEST(DispatchTests, SpecificDestructor) { + dispatch_queue_t queue = dispatch_queue_create("queue", nullptr); + dispatch_group_t group = dispatch_group_create(); + + dispatch_queue_set_specific(queue, key, context, destructor); + + EXPECT_EQ(0, destructorCount); + + dispatch_group_async(group, queue, ^{ + EXPECT_EQ(dispatch_get_specific(key), context); + dispatch_queue_set_specific(queue, key, context2, destructor); + EXPECT_EQ(1, destructorCount); + }); + ASSERT_EQ(dispatch_group_wait(group, _createEndTestTime()), 0); + + EXPECT_EQ(context2, dispatch_queue_get_specific(queue, key)); + dispatch_queue_set_specific(queue, key, context, destructor); + EXPECT_EQ(2, destructorCount); + + dispatch_group_async(group, queue, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); + EXPECT_EQ(2, destructorCount); + }); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + dispatch_release(queue); + dispatch_release(group); +} + +TEST(DispatchTests, RemoveSpecific) { + dispatch_queue_t queue = dispatch_queue_create("queue", nullptr); + dispatch_group_t group = dispatch_group_create(); + + dispatch_queue_set_specific(queue, key, context, nullptr); + + dispatch_group_async(group, queue, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); + dispatch_queue_set_specific(queue, key, nullptr, nullptr); + }); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + EXPECT_EQ(nullptr, dispatch_queue_get_specific(queue, key)); + + dispatch_release(queue); + dispatch_release(group); +} + +TEST(DispatchTests, MultipleQueues) { + dispatch_queue_t queue1 = dispatch_queue_create("queue1", nullptr); + dispatch_queue_t queue2 = dispatch_queue_create("queue2", nullptr); + dispatch_queue_t queue3 = dispatch_queue_create("queue3", nullptr); + dispatch_group_t group = dispatch_group_create(); + + dispatch_queue_set_specific(queue1, key, context, nullptr); + dispatch_queue_set_specific(queue2, key, context2, nullptr); + dispatch_queue_set_specific(queue3, key, context3, nullptr); + + dispatch_group_async(group, queue1, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); + dispatch_queue_set_specific(queue1, key, context2, nullptr); + }); + dispatch_group_async(group, queue2, ^{ + EXPECT_EQ(context2, dispatch_get_specific(key)); + dispatch_queue_set_specific(queue2, key, context3, nullptr); + }); + dispatch_group_async(group, queue3, ^{ + EXPECT_EQ(context3, dispatch_get_specific(key)); + dispatch_queue_set_specific(queue3, key, context, nullptr); + }); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + EXPECT_EQ(context2, dispatch_queue_get_specific(queue1, key)); + EXPECT_EQ(context3, dispatch_queue_get_specific(queue2, key)); + EXPECT_EQ(context, dispatch_queue_get_specific(queue3, key)); + + dispatch_group_async(group, queue1, ^{ + EXPECT_EQ(context2, dispatch_get_specific(key)); + }); + dispatch_group_async(group, queue2, ^{ + EXPECT_EQ(context3, dispatch_get_specific(key)); + }); + dispatch_group_async(group, queue3, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); + }); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + dispatch_queue_set_specific(queue1, key2, context, nullptr); + dispatch_queue_set_specific(queue1, key2, context3, nullptr); + dispatch_queue_set_specific(queue3, key3, context3, nullptr); + + dispatch_group_async(group, queue1, ^{ + EXPECT_EQ(context2, dispatch_get_specific(key)); + EXPECT_EQ(context3, dispatch_get_specific(key2)); + }); + dispatch_group_async(group, queue2, ^{ + EXPECT_EQ(context3, dispatch_get_specific(key)); + }); + dispatch_group_async(group, queue3, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); + EXPECT_EQ(context3, dispatch_get_specific(key3)); + }); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + dispatch_release(queue1); + dispatch_release(queue2); + dispatch_release(queue3); + dispatch_release(group); +} \ No newline at end of file From 6db674453361734b31b635229e48bf1b9d290ad8 Mon Sep 17 00:00:00 2001 From: Henry Fox Date: Fri, 1 Dec 2017 10:43:14 -0800 Subject: [PATCH 4/7] Implement OSSpinLock in WOCStdLib (#2820) * Implement OSSpinLock in WoCStdLib * Add a test for OSSpinLock utilizing libdispatch Fixes #2709 * Address PR Feedback * Remove file global variables. * Manage resources more correctly. --- .../WOCStdLib/WOCStdLib.UnitTests.vcxproj | 3 +- tests/UnitTests/WOCStdLib/OSSpinLockTest.mm | 79 +++++++++++++++++++ tools/WOCStdLib/dll/WOCStdLib.def | 3 + tools/WOCStdLib/lib/OSSpinLock.cpp | 35 ++++++++ tools/WOCStdLib/lib/WOCStdLibLib.vcxproj | 6 +- tools/include/WOCStdLib/libkern/OSAtomic.h | 10 +++ 6 files changed, 131 insertions(+), 5 deletions(-) create mode 100644 tests/UnitTests/WOCStdLib/OSSpinLockTest.mm create mode 100644 tools/WOCStdLib/lib/OSSpinLock.cpp diff --git a/build/Tests/UnitTests/WOCStdLib/WOCStdLib.UnitTests.vcxproj b/build/Tests/UnitTests/WOCStdLib/WOCStdLib.UnitTests.vcxproj index a6a8f3c3b5..d707c23e45 100644 --- a/build/Tests/UnitTests/WOCStdLib/WOCStdLib.UnitTests.vcxproj +++ b/build/Tests/UnitTests/WOCStdLib/WOCStdLib.UnitTests.vcxproj @@ -204,7 +204,8 @@ + - + \ No newline at end of file diff --git a/tests/UnitTests/WOCStdLib/OSSpinLockTest.mm b/tests/UnitTests/WOCStdLib/OSSpinLockTest.mm new file mode 100644 index 0000000000..3d3c4fed21 --- /dev/null +++ b/tests/UnitTests/WOCStdLib/OSSpinLockTest.mm @@ -0,0 +1,79 @@ +//****************************************************************************** +// +// Copyright (c) Microsoft. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +#import +#import +#import +#import + +// Create a 1 second end time for the test to timeout. +static dispatch_time_t _createEndTestTime() { + return dispatch_time(DISPATCH_TIME_NOW, 1000000000); +} + +TEST(OSSpinLock, SpinLockTests) { + // Use two queues for concurrency as DISPATCH_QUEUE_CONCURRENT is not yet supported. + dispatch_queue_t queue1 = dispatch_queue_create("queue", nullptr); + dispatch_queue_t queue2 = dispatch_queue_create("queue", nullptr); + dispatch_semaphore_t semaphore1 = dispatch_semaphore_create(0); + dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0); + + __block OSSpinLock lock1 = OS_SPINLOCK_INIT; + __block OSSpinLock lock2 = OS_SPINLOCK_INIT; + __block OSSpinLock lock3 = OS_SPINLOCK_INIT; + __block int spinValue = 0; + + OSSpinLockLock(&lock1); + OSSpinLockLock(&lock2); + OSSpinLockLock(&lock3); + + dispatch_async(queue1, ^{ + dispatch_semaphore_wait(semaphore1, _createEndTestTime()); + + ASSERT_EQ(false, OSSpinLockTry(&lock1)); + ASSERT_EQ(0, spinValue); + spinValue++; + ASSERT_EQ(1, spinValue); + OSSpinLockUnlock(&lock2); + dispatch_semaphore_signal(semaphore2); + }); + + dispatch_async(queue2, ^{ + dispatch_semaphore_signal(semaphore1); + + OSSpinLockLock(&lock2); + ASSERT_EQ(false, OSSpinLockTry(&lock1)); + ASSERT_EQ(1, spinValue); + spinValue++; + ASSERT_EQ(2, spinValue); + OSSpinLockUnlock(&lock1); + OSSpinLockUnlock(&lock2); + OSSpinLockUnlock(&lock3); + dispatch_semaphore_signal(semaphore2); + }); + + dispatch_semaphore_wait(semaphore2, _createEndTestTime()); + dispatch_semaphore_wait(semaphore2, _createEndTestTime()); + + ASSERT_EQ(true, OSSpinLockTry(&lock3)); + ASSERT_EQ(false, OSSpinLockTry(&lock3)); + OSSpinLockUnlock(&lock3); + + dispatch_release(queue1); + dispatch_release(queue2); + dispatch_release(semaphore1); + dispatch_release(semaphore2); +} \ No newline at end of file diff --git a/tools/WOCStdLib/dll/WOCStdLib.def b/tools/WOCStdLib/dll/WOCStdLib.def index 915b9d08cd..f9fcf04695 100644 --- a/tools/WOCStdLib/dll/WOCStdLib.def +++ b/tools/WOCStdLib/dll/WOCStdLib.def @@ -33,6 +33,9 @@ LIBRARY WOCStdLib OSSwapInt16 OSSwapInt32 OSSwapLittleToHostInt32 + OSSpinLockLock + OSSpinLockUnlock + OSSpinLockTry arc4random arc4random_uniform mach_timebase_info diff --git a/tools/WOCStdLib/lib/OSSpinLock.cpp b/tools/WOCStdLib/lib/OSSpinLock.cpp new file mode 100644 index 0000000000..653585e3e7 --- /dev/null +++ b/tools/WOCStdLib/lib/OSSpinLock.cpp @@ -0,0 +1,35 @@ +//****************************************************************************** +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +#include +#include "windows.h" +#ifdef _MSC_VER +#include +#endif + +extern "C" void OSSpinLockLock(volatile OSSpinLock* lock) { + while (_InterlockedCompareExchange((volatile long*)lock, ~0, 0) != 0) { + Sleep(0); + } +} + +extern "C" void OSSpinLockUnlock(volatile OSSpinLock* lock) { + _InterlockedExchange((volatile long*)lock, 0); +} + +extern "C" bool OSSpinLockTry(volatile OSSpinLock* lock) { + return (_InterlockedCompareExchange((volatile long*)lock, ~0, 0) == 0); +} \ No newline at end of file diff --git a/tools/WOCStdLib/lib/WOCStdLibLib.vcxproj b/tools/WOCStdLib/lib/WOCStdLibLib.vcxproj index 586c1c7135..1819a32c68 100644 --- a/tools/WOCStdLib/lib/WOCStdLibLib.vcxproj +++ b/tools/WOCStdLib/lib/WOCStdLibLib.vcxproj @@ -1,6 +1,5 @@  - Debug @@ -18,13 +17,11 @@ Release Win32 - - + StaticLibrary - @@ -40,6 +37,7 @@ + {17D1A01F-08ED-4903-BAE1-D446150CC7D5} diff --git a/tools/include/WOCStdLib/libkern/OSAtomic.h b/tools/include/WOCStdLib/libkern/OSAtomic.h index 97d97a23a0..5b80248c34 100644 --- a/tools/include/WOCStdLib/libkern/OSAtomic.h +++ b/tools/include/WOCStdLib/libkern/OSAtomic.h @@ -19,7 +19,13 @@ #ifndef _OSATOMIC_H_ #define _OSATOMIC_H_ +#define OS_SPINLOCK_INIT 0 + +#include +#include + typedef int __int32_t; +typedef __int32_t OSSpinLock; __BEGIN_DECLS @@ -50,6 +56,10 @@ static __inline __int32_t OSAtomicDecrement32Barrier(volatile __int32_t *val) return OSAtomicAdd32Barrier(-1, val); } +void OSSpinLockLock(volatile OSSpinLock *lock); +void OSSpinLockUnlock(volatile OSSpinLock *lock); +bool OSSpinLockTry(volatile OSSpinLock *lock); + __END_DECLS #endif \ No newline at end of file From d68ea606632c41731d6eb41f294df65ccd4dfb72 Mon Sep 17 00:00:00 2001 From: Henry Fox Date: Fri, 1 Dec 2017 10:43:30 -0800 Subject: [PATCH 5/7] Fix NSNullTests comparison between NSNull null and kCFNull (#2821) --- tests/unittests/Foundation/NSNullTests.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unittests/Foundation/NSNullTests.mm b/tests/unittests/Foundation/NSNullTests.mm index b0b07a28bb..181a2b464f 100644 --- a/tests/unittests/Foundation/NSNullTests.mm +++ b/tests/unittests/Foundation/NSNullTests.mm @@ -18,7 +18,7 @@ #import TEST(NSNull, Bridged) { - ASSERT_EQ([NSNull null], kCFNull); // Pointer equality required. + ASSERT_EQ((void*)[NSNull null], (void*)kCFNull); // Pointer equality required. } TEST(NSNull, Singleton) { From e1fae887c1c833e14accffac3f3c3010fb5b61c8 Mon Sep 17 00:00:00 2001 From: Henry Fox Date: Mon, 4 Dec 2017 14:01:42 -0800 Subject: [PATCH 6/7] Update libdispatch to use asynchronous destructor calls (#2822) * Update libdispatch specific destructor calls to run in async * Update dispatch tests for asynchronous specific destructor * Update destructor test semaphores. --- deps/3rdparty/libdispatch/src/queue.c | 2 +- tests/UnitTests/Foundation/DispatchTests.mm | 208 +++++++++--------- .../ARM/Debug/libdispatch.dll | 2 +- .../ARM/Debug/libdispatch.pdb | 4 +- .../ARM/Release/libdispatch.dll | 2 +- .../ARM/Release/libdispatch.pdb | 2 +- .../x86/Debug/libdispatch.dll | 2 +- .../x86/Debug/libdispatch.pdb | 4 +- .../x86/Release/libdispatch.dll | 4 +- .../x86/Release/libdispatch.pdb | 2 +- 10 files changed, 121 insertions(+), 111 deletions(-) diff --git a/deps/3rdparty/libdispatch/src/queue.c b/deps/3rdparty/libdispatch/src/queue.c index 2089b95829..0d0e8f6add 100644 --- a/deps/3rdparty/libdispatch/src/queue.c +++ b/deps/3rdparty/libdispatch/src/queue.c @@ -2039,7 +2039,7 @@ static void _dispatch_queue_insert_specific(dispatch_queue_specific_t dqs) { TAILQ_FOREACH(var, &dqsl->context_list, specific) { if (var->key == dqs->key) { if(var->destructor) { - var->destructor(var->context); + dispatch_async_f(_dispatch_get_root_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, false), var->context, var->destructor); } // If a specific exists with this key, simply update the context on that specific diff --git a/tests/UnitTests/Foundation/DispatchTests.mm b/tests/UnitTests/Foundation/DispatchTests.mm index b23245aaa2..b47c5c7c9d 100644 --- a/tests/UnitTests/Foundation/DispatchTests.mm +++ b/tests/UnitTests/Foundation/DispatchTests.mm @@ -28,173 +28,183 @@ // Create a 1 second end time for the test to timeout. static dispatch_time_t _createEndTestTime() { - return dispatch_time(DISPATCH_TIME_NOW, 1000000000000); + return dispatch_time(DISPATCH_TIME_NOW, 1000000000); } -static void destructor(void* x){ +static dispatch_semaphore_t destructorSemaphore; +static void destructor(void* x) { destructorCount++; + if (destructorSemaphore) { + dispatch_semaphore_signal(destructorSemaphore); + } } TEST(DispatchTests, SimpleSpecific) { dispatch_queue_t queue = dispatch_queue_create("queue", nullptr); - dispatch_group_t group = dispatch_group_create(); + dispatch_group_t group = dispatch_group_create(); - dispatch_queue_set_specific(queue, key, context, nullptr); + dispatch_queue_set_specific(queue, key, context, nullptr); - dispatch_group_async(group, queue, ^{ - EXPECT_EQ(context, dispatch_get_specific(key)); + dispatch_group_async(group, queue, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); dispatch_queue_set_specific(queue, key, context2, nullptr); }); - - ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); - - EXPECT_EQ(context2, dispatch_queue_get_specific(queue, key)); - + + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + EXPECT_EQ(context2, dispatch_queue_get_specific(queue, key)); + dispatch_group_async(group, queue, ^{ - EXPECT_EQ(context2, dispatch_get_specific(key)); + EXPECT_EQ(context2, dispatch_get_specific(key)); }); - ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); - dispatch_release(group); - dispatch_release(queue); + dispatch_release(group); + dispatch_release(queue); } TEST(DispatchTests, MultipleSpecificKeys) { dispatch_queue_t queue = dispatch_queue_create("queue", nullptr); - dispatch_group_t group = dispatch_group_create(); + dispatch_group_t group = dispatch_group_create(); dispatch_queue_set_specific(queue, key, context, nullptr); - dispatch_queue_set_specific(queue, key2, context2, nullptr); - + dispatch_queue_set_specific(queue, key2, context2, nullptr); + dispatch_group_async(group, queue, ^{ - EXPECT_EQ(context, dispatch_get_specific(key)); - EXPECT_EQ(context2, dispatch_get_specific(key2)); + EXPECT_EQ(context, dispatch_get_specific(key)); + EXPECT_EQ(context2, dispatch_get_specific(key2)); dispatch_queue_set_specific(queue, key, context2, nullptr); - dispatch_queue_set_specific(queue, key2, context, nullptr); + dispatch_queue_set_specific(queue, key2, context, nullptr); }); - ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); - - EXPECT_EQ(context2, dispatch_queue_get_specific(queue, key)); - EXPECT_EQ(context, dispatch_queue_get_specific(queue, key2)); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + EXPECT_EQ(context2, dispatch_queue_get_specific(queue, key)); + EXPECT_EQ(context, dispatch_queue_get_specific(queue, key2)); + + dispatch_queue_set_specific(queue, key3, context3, nullptr); - dispatch_queue_set_specific(queue, key3, context3, nullptr); - dispatch_group_async(group, queue, ^{ - EXPECT_EQ(context2, dispatch_get_specific(key)); - EXPECT_EQ(context, dispatch_get_specific(key2)); - EXPECT_EQ(context3, dispatch_get_specific(key3)); + EXPECT_EQ(context2, dispatch_get_specific(key)); + EXPECT_EQ(context, dispatch_get_specific(key2)); + EXPECT_EQ(context3, dispatch_get_specific(key3)); }); - ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); - dispatch_release(queue); - dispatch_release(group); + dispatch_release(queue); + dispatch_release(group); } TEST(DispatchTests, SpecificDestructor) { - dispatch_queue_t queue = dispatch_queue_create("queue", nullptr); - dispatch_group_t group = dispatch_group_create(); + dispatch_queue_t queue = dispatch_queue_create("queue", nullptr); + dispatch_group_t group = dispatch_group_create(); + destructorSemaphore = dispatch_semaphore_create(0); dispatch_queue_set_specific(queue, key, context, destructor); - EXPECT_EQ(0, destructorCount); - + EXPECT_EQ(0, destructorCount); + dispatch_group_async(group, queue, ^{ - EXPECT_EQ(dispatch_get_specific(key), context); + EXPECT_EQ(dispatch_get_specific(key), context); dispatch_queue_set_specific(queue, key, context2, destructor); - EXPECT_EQ(1, destructorCount); - }); - ASSERT_EQ(dispatch_group_wait(group, _createEndTestTime()), 0); - - EXPECT_EQ(context2, dispatch_queue_get_specific(queue, key)); - dispatch_queue_set_specific(queue, key, context, destructor); - EXPECT_EQ(2, destructorCount); - + // Destructor can be called asynchrounously. Wait for deestructor to be called. + dispatch_semaphore_wait(destructorSemaphore, _createEndTestTime()); + EXPECT_EQ(1, destructorCount); + }); + ASSERT_EQ(dispatch_group_wait(group, _createEndTestTime()), 0); + + EXPECT_EQ(context2, dispatch_queue_get_specific(queue, key)); + dispatch_queue_set_specific(queue, key, context, destructor); + dispatch_semaphore_wait(destructorSemaphore, _createEndTestTime()); + EXPECT_EQ(2, destructorCount); + dispatch_group_async(group, queue, ^{ - EXPECT_EQ(context, dispatch_get_specific(key)); - EXPECT_EQ(2, destructorCount); + EXPECT_EQ(context, dispatch_get_specific(key)); + EXPECT_EQ(2, destructorCount); }); - ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); - dispatch_release(queue); - dispatch_release(group); + dispatch_release(queue); + dispatch_release(group); + dispatch_release(destructorSemaphore); + destructorSemaphore = nullptr; } TEST(DispatchTests, RemoveSpecific) { - dispatch_queue_t queue = dispatch_queue_create("queue", nullptr); - dispatch_group_t group = dispatch_group_create(); + dispatch_queue_t queue = dispatch_queue_create("queue", nullptr); + dispatch_group_t group = dispatch_group_create(); dispatch_queue_set_specific(queue, key, context, nullptr); - + dispatch_group_async(group, queue, ^{ - EXPECT_EQ(context, dispatch_get_specific(key)); + EXPECT_EQ(context, dispatch_get_specific(key)); dispatch_queue_set_specific(queue, key, nullptr, nullptr); }); - ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); - EXPECT_EQ(nullptr, dispatch_queue_get_specific(queue, key)); + EXPECT_EQ(nullptr, dispatch_queue_get_specific(queue, key)); - dispatch_release(queue); - dispatch_release(group); + dispatch_release(queue); + dispatch_release(group); } TEST(DispatchTests, MultipleQueues) { dispatch_queue_t queue1 = dispatch_queue_create("queue1", nullptr); - dispatch_queue_t queue2 = dispatch_queue_create("queue2", nullptr); - dispatch_queue_t queue3 = dispatch_queue_create("queue3", nullptr); - dispatch_group_t group = dispatch_group_create(); + dispatch_queue_t queue2 = dispatch_queue_create("queue2", nullptr); + dispatch_queue_t queue3 = dispatch_queue_create("queue3", nullptr); + dispatch_group_t group = dispatch_group_create(); - dispatch_queue_set_specific(queue1, key, context, nullptr); - dispatch_queue_set_specific(queue2, key, context2, nullptr); - dispatch_queue_set_specific(queue3, key, context3, nullptr); + dispatch_queue_set_specific(queue1, key, context, nullptr); + dispatch_queue_set_specific(queue2, key, context2, nullptr); + dispatch_queue_set_specific(queue3, key, context3, nullptr); - dispatch_group_async(group, queue1, ^{ - EXPECT_EQ(context, dispatch_get_specific(key)); + dispatch_group_async(group, queue1, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); dispatch_queue_set_specific(queue1, key, context2, nullptr); }); - dispatch_group_async(group, queue2, ^{ - EXPECT_EQ(context2, dispatch_get_specific(key)); + dispatch_group_async(group, queue2, ^{ + EXPECT_EQ(context2, dispatch_get_specific(key)); dispatch_queue_set_specific(queue2, key, context3, nullptr); }); - dispatch_group_async(group, queue3, ^{ - EXPECT_EQ(context3, dispatch_get_specific(key)); + dispatch_group_async(group, queue3, ^{ + EXPECT_EQ(context3, dispatch_get_specific(key)); dispatch_queue_set_specific(queue3, key, context, nullptr); }); - ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); - - EXPECT_EQ(context2, dispatch_queue_get_specific(queue1, key)); - EXPECT_EQ(context3, dispatch_queue_get_specific(queue2, key)); - EXPECT_EQ(context, dispatch_queue_get_specific(queue3, key)); - + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + + EXPECT_EQ(context2, dispatch_queue_get_specific(queue1, key)); + EXPECT_EQ(context3, dispatch_queue_get_specific(queue2, key)); + EXPECT_EQ(context, dispatch_queue_get_specific(queue3, key)); + dispatch_group_async(group, queue1, ^{ - EXPECT_EQ(context2, dispatch_get_specific(key)); + EXPECT_EQ(context2, dispatch_get_specific(key)); }); - dispatch_group_async(group, queue2, ^{ - EXPECT_EQ(context3, dispatch_get_specific(key)); + dispatch_group_async(group, queue2, ^{ + EXPECT_EQ(context3, dispatch_get_specific(key)); }); - dispatch_group_async(group, queue3, ^{ - EXPECT_EQ(context, dispatch_get_specific(key)); + dispatch_group_async(group, queue3, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); }); - ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); - dispatch_queue_set_specific(queue1, key2, context, nullptr); - dispatch_queue_set_specific(queue1, key2, context3, nullptr); - dispatch_queue_set_specific(queue3, key3, context3, nullptr); + dispatch_queue_set_specific(queue1, key2, context, nullptr); + dispatch_queue_set_specific(queue1, key2, context3, nullptr); + dispatch_queue_set_specific(queue3, key3, context3, nullptr); - dispatch_group_async(group, queue1, ^{ - EXPECT_EQ(context2, dispatch_get_specific(key)); - EXPECT_EQ(context3, dispatch_get_specific(key2)); + dispatch_group_async(group, queue1, ^{ + EXPECT_EQ(context2, dispatch_get_specific(key)); + EXPECT_EQ(context3, dispatch_get_specific(key2)); }); - dispatch_group_async(group, queue2, ^{ - EXPECT_EQ(context3, dispatch_get_specific(key)); + dispatch_group_async(group, queue2, ^{ + EXPECT_EQ(context3, dispatch_get_specific(key)); }); - dispatch_group_async(group, queue3, ^{ - EXPECT_EQ(context, dispatch_get_specific(key)); - EXPECT_EQ(context3, dispatch_get_specific(key3)); + dispatch_group_async(group, queue3, ^{ + EXPECT_EQ(context, dispatch_get_specific(key)); + EXPECT_EQ(context3, dispatch_get_specific(key3)); }); - ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); + ASSERT_EQ(0, dispatch_group_wait(group, _createEndTestTime())); - dispatch_release(queue1); - dispatch_release(queue2); - dispatch_release(queue3); - dispatch_release(group); + dispatch_release(queue1); + dispatch_release(queue2); + dispatch_release(queue3); + dispatch_release(group); } \ No newline at end of file diff --git a/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.dll b/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.dll index bc574e157c..19d8789f29 100644 --- a/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.dll +++ b/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8ab9ec1d0ab33c38e4cc735d7cd6535149b6743ce43c7b23f96579b8d0b1429 +oid sha256:c1075c6a3bec34145967a1383296b7ec59196234fdb3612e766811613a0cc113 size 194560 diff --git a/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.pdb b/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.pdb index caa9f98a09..ea11e6d2b0 100644 --- a/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.pdb +++ b/tools/deps/prebuilt/Universal Windows/ARM/Debug/libdispatch.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:466b4a82f9324e33fc63fe8060b64e72beb1d2cc6100b906935534b18da0d126 -size 1110016 +oid sha256:d1788f4ad5521ad3a857bca4014265df6df60e7d3ca24ad918037eb0cbcaef51 +size 1634304 diff --git a/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.dll b/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.dll index 53a4445f3b..5c94578c7e 100644 --- a/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.dll +++ b/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:53d5544be5148bf3713c9e30e545d7552b0325bdeea81a5a64168ef23b276a3d +oid sha256:e323881420bc89aec34878e7e23623b6f0f2330ea39ae424c8669c30c3c3c8df size 56320 diff --git a/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.pdb b/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.pdb index 4768a11815..d4ab968124 100644 --- a/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.pdb +++ b/tools/deps/prebuilt/Universal Windows/ARM/Release/libdispatch.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2168db43469509c62f7b080bde04014a70e3ee82d4c57d184e77a0e77f3a6087 +oid sha256:3e1145d21fd9a613b9dd619782b6482e9866bbcc9a028002f4d3a7d7e9f32ea4 size 1626112 diff --git a/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.dll b/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.dll index 6b68e0e187..f369516af4 100644 --- a/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.dll +++ b/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e97de022f5aeb53689b9cf21dacceaa94e961b0e6edc8d98a74a2bedd9d434ec +oid sha256:d83fd6b9fa8c9477dd0d38b83c65af29307ea67b64a1b79d74c5bdb88a819ae3 size 136192 diff --git a/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.pdb b/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.pdb index 2359982132..3a4f1a2401 100644 --- a/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.pdb +++ b/tools/deps/prebuilt/Universal Windows/x86/Debug/libdispatch.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:381ba7579b8f37d51d29d7ba10049c73467bf723338d5caa5535036b12d86518 -size 1085440 +oid sha256:646dbcc1614f9572c3acbf33ee917b2c654e00a5d4dfd376929d40380d6d92d3 +size 1617920 diff --git a/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.dll b/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.dll index 376fe3b746..8f9c599534 100644 --- a/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.dll +++ b/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.dll @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:899fdbc25acabcbd8c321823f421c15f301cf23e03ff7ff19ee7fe3903da86ce -size 52224 +oid sha256:4e6413e8b8df9a36a49c42243c654543778cb19f8645a087b6d32059b412c52c +size 52736 diff --git a/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.pdb b/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.pdb index a9eafb1e1e..1cf8ba5cdb 100644 --- a/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.pdb +++ b/tools/deps/prebuilt/Universal Windows/x86/Release/libdispatch.pdb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e047e438ca7cc8efc2b40799c4da059b3921026edae79c5b9586ab27b9fd54db +oid sha256:f6b58c29d9da77f542b4aa55383260b7a538af962d120d8c31e7c61a1eb68d01 size 1658880 From 22fb297b6920bf1c1cea0d66e9a1952c3e766d14 Mon Sep 17 00:00:00 2001 From: Henry Fox Date: Fri, 15 Dec 2017 21:39:22 -0800 Subject: [PATCH 7/7] Fix NSLocalizedString and Add Localization Tests (#2824) * Fix NSLocalizedString and add new tests * Address CR Feedback. * Forgot to change to StringByAppendingPath * Nevermind. ASSERT_OBJCEQ does indeed work. * Address feedback. * Address feedback. --- .../PlugIn.subproj/CFBundle_Locale.c | 63 ++++++++++++++ .../Foundation.WindowsOnly.UnitTests.vcxproj | 3 +- ...tion.WindowsOnly.UnitTests.vcxproj.filters | 3 +- .../Base.lproj/Localizable.strings | 11 +++ .../WOCCatalog-WinStore10/WOCCatalog.vcxproj | 30 +++++-- .../WOCCatalog.vcxproj.filters | 27 +++++- .../de.lproj/Localizable.strings | 11 +++ .../es_MX.lproj/Localizable.strings | 11 +++ .../zh-Hant-TW.lproj/Localizable.strings | 11 +++ .../zh.lproj/Localizable.strings | 11 +++ .../WOCCatalog/LocalizationViewController.h | 21 +++++ .../WOCCatalog/LocalizationViewController.m | 82 +++++++++++++++++++ .../WOCCatalog/MainViewController.m | 6 +- .../WindowsOnly/BundleInternalTests.mm | 77 +++++++++++++++++ 14 files changed, 357 insertions(+), 10 deletions(-) create mode 100644 samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/Base.lproj/Localizable.strings create mode 100644 samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/de.lproj/Localizable.strings create mode 100644 samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/es_MX.lproj/Localizable.strings create mode 100644 samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/zh-Hant-TW.lproj/Localizable.strings create mode 100644 samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/zh.lproj/Localizable.strings create mode 100644 samples/WOCCatalog/WOCCatalog/LocalizationViewController.h create mode 100644 samples/WOCCatalog/WOCCatalog/LocalizationViewController.m create mode 100644 tests/UnitTests/Foundation/WindowsOnly/BundleInternalTests.mm diff --git a/Frameworks/CoreFoundation/PlugIn.subproj/CFBundle_Locale.c b/Frameworks/CoreFoundation/PlugIn.subproj/CFBundle_Locale.c index 6ca3a0d0c3..546673b52c 100644 --- a/Frameworks/CoreFoundation/PlugIn.subproj/CFBundle_Locale.c +++ b/Frameworks/CoreFoundation/PlugIn.subproj/CFBundle_Locale.c @@ -531,6 +531,10 @@ CF_PRIVATE CFArrayRef _CFBundleCopyUserLanguages() { static CFArrayRef _CFBundleUserLanguages = NULL; static dispatch_once_t once = 0; dispatch_once(&once, ^{ +// WINOBJC: __CFAppleLanguages does not exist on Windows +#if DEPLOYMENT_TARGET_WINDOWS + _CFBundleUserLanguages = CFLocaleCopyPreferredLanguages(); +#else CFArrayRef preferencesArray = NULL; if (__CFAppleLanguages) { CFDataRef data; @@ -555,6 +559,7 @@ CF_PRIVATE CFArrayRef _CFBundleCopyUserLanguages() { _CFBundleUserLanguages = NULL; } if (preferencesArray) CFRelease(preferencesArray); +#endif }); if (_CFBundleUserLanguages) { @@ -663,8 +668,66 @@ static CFStringRef _CFBundleCopyLanguageFoundInLocalizations(CFArrayRef localiza return NULL; } +// WINOBJC: Helper functions for workaround in _CFBundleCreateMutableArrayOfFallbackLanguages +static CFStringRef _copyStringTruncated(CFStringRef localization, CFRange cutoff) { + return CFStringCreateWithSubstring(NULL, localization, CFRangeMake(0, cutoff.location)); +} + +static CFStringRef _copyStringWithUnderscores(CFStringRef localization) { + CFMutableStringRef underscoredString = CFStringCreateMutableCopy(NULL, 0, localization); + CFStringFindAndReplace(underscoredString, CFSTR("-"), CFSTR("_"), CFRangeMake(0, CFStringGetLength(underscoredString)), 0); + return underscoredString; +} + // Given a list of localizations (e.g., provided as argument to API, or present as .lproj directories), return a mutable array of localizations in preferred order. Returns NULL if nothing is found. static CFMutableArrayRef _CFBundleCreateMutableArrayOfFallbackLanguages(CFArrayRef availableLocalizations, CFArrayRef preferredLocalizations) { +// WINOBJC: The API that performs the work described below does not exist in our 3rd party libraries. ualoc_ is an Apple ICU addition. +#if DEPLOYMENT_TARGET_WINDOWS + // Here we need to intersect the preferred languages with the available localizations + // We know the user languages are in preferred order, add to this list in this order + // Prefer the full language locale, attempt to convert any hyphens to underscores as + // Windows language settings are retrieved with hyphens while underscores are commonly used for localization. + // Finally, attempt to truncate any underscores from the language to find a base localization. + // For example, an english locale will appear as "en-US" and a German locale will appear as "de-DE". + // A localization for "en-US" may be set up as "en-US", "en_US", or even just "en". + + CFMutableArrayRef resultArray = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks); + + for (CFIndex i = 0, preferredCount = CFArrayGetCount(preferredLocalizations); i < preferredCount; i++) { + CFStringRef preferredLocalization = (CFStringRef)CFArrayGetValueAtIndex(preferredLocalizations, i); + for (CFIndex j = 0, availableCount = CFArrayGetCount(availableLocalizations); j < availableCount; j++) { + CFStringRef availableLocalization = (CFStringRef)CFArrayGetValueAtIndex(availableLocalizations, j); + if(CFStringCompare(preferredLocalization, availableLocalization, 0) == kCFCompareEqualTo) { + CFArrayAppendValue(resultArray, preferredLocalization); + } + CFRange hyphenation; + if (CFStringFindWithOptions(preferredLocalization, CFSTR("-"), CFRangeMake(0, CFStringGetLength(preferredLocalization)), kCFCompareCaseInsensitive, &hyphenation) == true) { + CFStringRef underscoreNotationLocalization = _copyStringWithUnderscores(preferredLocalization); + if (CFStringCompare(underscoreNotationLocalization, availableLocalization, 0) == kCFCompareEqualTo) { + CFArrayAppendValue(resultArray, underscoreNotationLocalization); + } + + CFStringRef truncatedLocalization = _copyStringTruncated(underscoreNotationLocalization, hyphenation); + if (CFStringCompare(truncatedLocalization, availableLocalization, 0) == kCFCompareEqualTo) { + CFArrayAppendValue(resultArray, truncatedLocalization); + } + + CFRelease(underscoreNotationLocalization); + CFRelease(truncatedLocalization); + } else if (CFStringFindWithOptions(preferredLocalization, CFSTR("_"), CFRangeMake(0, CFStringGetLength(preferredLocalization)), kCFCompareCaseInsensitive, &hyphenation) == true) { + CFStringRef truncatedLocalization = _copyStringTruncated(preferredLocalization, hyphenation); + if (CFStringCompare(truncatedLocalization, availableLocalization, 0) == kCFCompareEqualTo) { + CFArrayAppendValue(resultArray, truncatedLocalization); + } + CFRelease(truncatedLocalization); + } + } + } + if (CFArrayGetCount(resultArray) > 0) { + return resultArray; + } + return NULL; +#endif // stringPointers must be the length of list char * (^makeBuffer)(CFArrayRef, char **) = ^(CFArrayRef list, char *stringPointers[]) { #if !__HAS_APPLE_ICU__ diff --git a/build/Tests/UnitTests/Foundation.WindowsOnly/Foundation.WindowsOnly.UnitTests.vcxproj b/build/Tests/UnitTests/Foundation.WindowsOnly/Foundation.WindowsOnly.UnitTests.vcxproj index 3841c533be..377163b715 100644 --- a/build/Tests/UnitTests/Foundation.WindowsOnly/Foundation.WindowsOnly.UnitTests.vcxproj +++ b/build/Tests/UnitTests/Foundation.WindowsOnly/Foundation.WindowsOnly.UnitTests.vcxproj @@ -237,10 +237,11 @@ + - + \ No newline at end of file diff --git a/build/Tests/UnitTests/Foundation.WindowsOnly/Foundation.WindowsOnly.UnitTests.vcxproj.filters b/build/Tests/UnitTests/Foundation.WindowsOnly/Foundation.WindowsOnly.UnitTests.vcxproj.filters index a1a6943b2e..3e01426aff 100644 --- a/build/Tests/UnitTests/Foundation.WindowsOnly/Foundation.WindowsOnly.UnitTests.vcxproj.filters +++ b/build/Tests/UnitTests/Foundation.WindowsOnly/Foundation.WindowsOnly.UnitTests.vcxproj.filters @@ -27,5 +27,6 @@ + - + \ No newline at end of file diff --git a/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/Base.lproj/Localizable.strings b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/Base.lproj/Localizable.strings new file mode 100644 index 0000000000..2fe0fcfe5a --- /dev/null +++ b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/Base.lproj/Localizable.strings @@ -0,0 +1,11 @@ +"a" = "z"; +"b" = "y"; +"c" = "x"; +"d" = "w"; +"e" = "v"; +"f" = "u"; +"g" = "t"; +"h" = "s"; +"i" = "r"; +"j" = "q"; +"Hello World" = "English: Hello World"; \ No newline at end of file diff --git a/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/WOCCatalog.vcxproj b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/WOCCatalog.vcxproj index 29f397cd22..d7622a3a4d 100644 --- a/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/WOCCatalog.vcxproj +++ b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/WOCCatalog.vcxproj @@ -1,4 +1,4 @@ - + @@ -212,6 +212,7 @@ + @@ -248,6 +249,7 @@ + @@ -274,6 +276,26 @@ WOCCatalog-Debug-xcvars.txt WOCCatalog-Release-xcvars.txt + + Document + Base.lproj + + + Document + de.lproj + + + Document + zh.lproj + + + Document + zh-Hant-TW.lproj + + + Document + es_MX.lproj + @@ -303,7 +325,6 @@ - uap10.0 @@ -316,6 +337,5 @@ - - - + + \ No newline at end of file diff --git a/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/WOCCatalog.vcxproj.filters b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/WOCCatalog.vcxproj.filters index c4f5fe296f..344291f793 100644 --- a/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/WOCCatalog.vcxproj.filters +++ b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/WOCCatalog.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -99,6 +99,9 @@ WOCCatalog + + WOCCatalog + WOCCatalog @@ -207,6 +210,9 @@ WOCCatalog + + WOCCatalog + WOCCatalog @@ -332,4 +338,21 @@ WOCCatalog - + + + + + + + + + + + WOCCatalog + + + + + + + \ No newline at end of file diff --git a/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/de.lproj/Localizable.strings b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/de.lproj/Localizable.strings new file mode 100644 index 0000000000..07ae7d03e6 --- /dev/null +++ b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/de.lproj/Localizable.strings @@ -0,0 +1,11 @@ +"a" = "1"; +"b" = "2"; +"c" = "3"; +"d" = "4"; +"e" = "5"; +"f" = "6"; +"g" = "7"; +"h" = "8"; +"i" = "9"; +"j" = "10"; +"Hello World" = "German: Hallo Welt"; \ No newline at end of file diff --git a/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/es_MX.lproj/Localizable.strings b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/es_MX.lproj/Localizable.strings new file mode 100644 index 0000000000..014451d3e7 --- /dev/null +++ b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/es_MX.lproj/Localizable.strings @@ -0,0 +1,11 @@ +"a" = "z"; +"b" = "x"; +"c" = "c"; +"d" = "v"; +"e" = "b"; +"f" = "n"; +"g" = "m"; +"h" = ","; +"i" = "."; +"j" = "/"; +"Hello World" = "Spanish _: Hola Mundo"; \ No newline at end of file diff --git a/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/zh-Hant-TW.lproj/Localizable.strings b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/zh-Hant-TW.lproj/Localizable.strings new file mode 100644 index 0000000000..4978c25d48 --- /dev/null +++ b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/zh-Hant-TW.lproj/Localizable.strings @@ -0,0 +1,11 @@ +"a" = "q"; +"b" = "w"; +"c" = "e"; +"d" = "r"; +"e" = "t"; +"f" = "y"; +"g" = "u"; +"h" = "i"; +"i" = "o"; +"j" = "p"; +"Hello World" = "zh-Hant-TW: 世界您好"; \ No newline at end of file diff --git a/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/zh.lproj/Localizable.strings b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/zh.lproj/Localizable.strings new file mode 100644 index 0000000000..5a8405fe6f --- /dev/null +++ b/samples/WOCCatalog/WOCCatalog.vsimporter/WOCCatalog-WinStore10/zh.lproj/Localizable.strings @@ -0,0 +1,11 @@ +"a" = "a"; +"b" = "s"; +"c" = "d"; +"d" = "f"; +"e" = "g"; +"f" = "h"; +"g" = "j"; +"h" = "k"; +"i" = "l"; +"j" = ";"; +"Hello World" = "zh-Hans-CN truncated: 世界您好"; \ No newline at end of file diff --git a/samples/WOCCatalog/WOCCatalog/LocalizationViewController.h b/samples/WOCCatalog/WOCCatalog/LocalizationViewController.h new file mode 100644 index 0000000000..1e9b1143b5 --- /dev/null +++ b/samples/WOCCatalog/WOCCatalog/LocalizationViewController.h @@ -0,0 +1,21 @@ +//****************************************************************************** +// +// Copyright (c) Microsoft. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +#import +#import + +@interface LocalizationViewController : UITableViewController +@end \ No newline at end of file diff --git a/samples/WOCCatalog/WOCCatalog/LocalizationViewController.m b/samples/WOCCatalog/WOCCatalog/LocalizationViewController.m new file mode 100644 index 0000000000..209a3c3e29 --- /dev/null +++ b/samples/WOCCatalog/WOCCatalog/LocalizationViewController.m @@ -0,0 +1,82 @@ +//****************************************************************************** +// +// Copyright (c) Microsoft. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +#import "LocalizationViewController.h" + +static const CGFloat c_originX = 5; +static const CGFloat c_originY = 8; +static const CGFloat c_width = 260; +static const CGFloat c_height = 50; + +@implementation LocalizationViewController { +@private + UITableViewCell* _cell; + UITextField* _copyTextField; + UILabel* _pasteTextLabel; +} +- (void)viewDidLoad { + [super viewDidLoad]; + self.tableView.allowsSelection = YES; +} + +- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section { + return 2; +} + +- (CGFloat)tableView:(UITableView*)tableView heightForRowAtIndexPath:(NSIndexPath*)indexPath { + return 70; +} + +- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { + _cell = [tableView dequeueReusableCellWithIdentifier:@"MenuCell"]; + if (nil == _cell) { + _cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"MenuCell"]; + _cell.selectionStyle = UITableViewCellSelectionStyleNone; + } + + CGRect frame = CGRectMake(c_originX, c_originY, c_width, c_height); + + if (indexPath.row == 0) { + _copyTextField = [[UITextField alloc] initWithFrame:frame]; + _copyTextField.textColor = [UIColor blackColor]; + _copyTextField.font = [UIFont systemFontOfSize:17.0]; + _copyTextField.text = @"Hello World"; + [_copyTextField addTarget:self action:@selector(onCopyTextChanged:) forControlEvents:UIControlEventEditingChanged]; + + _cell.accessoryView = _copyTextField; + _cell.textLabel.text = @"Enter Text"; + } else if (indexPath.row == 1) { + _pasteTextLabel = [[UILabel alloc] initWithFrame:frame]; + _pasteTextLabel.textColor = [UIColor blackColor]; + _pasteTextLabel.font = [UIFont systemFontOfSize:17.0]; + _pasteTextLabel.textAlignment = NSTextAlignmentLeft; + _pasteTextLabel.text = NSLocalizedString(_copyTextField.text, nil); + _cell.accessoryView = _pasteTextLabel; + _cell.textLabel.text = @"Localized Text"; + } + return _cell; +} + +- (void)viewDidLayoutSubviews { + [(UIScrollView*)self.view setContentSize:[self.view.subviews[0] bounds].size]; +} + +- (void)onCopyTextChanged:(UITextField*)textField { + _pasteTextLabel.text = NSLocalizedString(textField.text, nil); + [[self tableView] setNeedsLayout]; +} + +@end diff --git a/samples/WOCCatalog/WOCCatalog/MainViewController.m b/samples/WOCCatalog/WOCCatalog/MainViewController.m index 781ac814ad..ed371d655e 100644 --- a/samples/WOCCatalog/WOCCatalog/MainViewController.m +++ b/samples/WOCCatalog/WOCCatalog/MainViewController.m @@ -44,6 +44,7 @@ #import "GeocodingViewController.h" #import "CoreLocationViewController.h" #import "GesturesViewController.h" +#import "LocalizationViewController.h" #ifdef WINOBJC #import "XamlViewController.h" @@ -138,12 +139,15 @@ - (void)viewDidLoad { // Accelerate 2 [self addMenuItemViewController:[[AccelerateViewController2 alloc] init] andTitle:@"Accelerate 2"]; - + // Shadow [self addMenuItemViewController:[[ShadowViewController alloc] init] andTitle:@"Shadow"]; // UIPasteboard [self addMenuItemViewController:[[UIPasteboardViewController alloc] init] andTitle:@"Copy And Paste"]; + + // Localization + [self addMenuItemViewController:[[LocalizationViewController alloc] init] andTitle:@"Localization"]; } - (void)didReceiveMemoryWarning { diff --git a/tests/UnitTests/Foundation/WindowsOnly/BundleInternalTests.mm b/tests/UnitTests/Foundation/WindowsOnly/BundleInternalTests.mm new file mode 100644 index 0000000000..bbe9c2e026 --- /dev/null +++ b/tests/UnitTests/Foundation/WindowsOnly/BundleInternalTests.mm @@ -0,0 +1,77 @@ +//****************************************************************************** +// +// Copyright (c) Microsoft. All rights reserved. +// +// This code is licensed under the MIT License (MIT). +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +//****************************************************************************** + +#import +#import +#import + +class NSBundleLocalization : public ::testing::Test { +public: + explicit NSBundleLocalization() : ::testing::Test() { + } + +protected: + virtual void SetUp() { + NSString* _subDirectory = @"en.lproj"; + NSString* localizationResourceName = @"Localizable.strings"; + // Create a unique test directory + _playground = [@"./tmp_TestFoundation" stringByAppendingPathComponent:[NSUUID UUID].UUIDString]; + _bundlePath = [_playground stringByAppendingPathComponent:@"MyBundle.bundle"]; + NSError* error = nil; + + NSString* localizationPath = [NSString stringWithFormat:@"%@/%@", _bundlePath.get(), _subDirectory]; + [[NSFileManager defaultManager] createDirectoryAtPath:localizationPath + withIntermediateDirectories:true + attributes:nil + error:&error]; + ASSERT_EQ(nil, error); + + // Full path to the file name + NSString* filepath = [NSString stringWithFormat:@"%@/%@", localizationPath, localizationResourceName]; + [[NSFileManager defaultManager] createFileAtPath:filepath contents:nil attributes:nil]; + + // As translated by Bing translate using unicode escapes to properly write to file + NSString* localization = @"\"Hello World\" = \"Hallo Welt\";\n\"Coding\" = \"\u7f16\u7801\";"; + [localization writeToFile:filepath atomically:YES encoding:NSUnicodeStringEncoding error:&error]; + } + + virtual void TearDown() { + NSError* error; + [[NSFileManager defaultManager] removeItemAtPath:_playground error:&error]; + if (error) { + // Oh well, temporary directories in taef are cleaned up automatically anyway + } + } + + StrongId _playground; + StrongId _bundlePath; +}; + +TEST_F(NSBundleLocalization, LocalizedString) { + if (!_playground) { + ASSERT_TRUE_MSG(false, @"Unable to create bundle resources"); + } + + NSBundle* bundle = [NSBundle bundleWithPath:_bundlePath]; + ASSERT_OBJCNE(bundle, nil); + + NSString* helloWorld = @"Hello World"; + NSString* coding = @"Coding"; + NSString* helloWorldInGerman = NSLocalizedStringFromTableInBundle(helloWorld, nil, bundle, nil); + NSString* codingInChinese = NSLocalizedStringFromTableInBundle(coding, nil, bundle, nil); + ASSERT_OBJCEQ(@"Hallo Welt", helloWorldInGerman); + ASSERT_OBJCEQ(@"编码", codingInChinese); +} \ No newline at end of file