From 0ab744477f85289745b7136840ee7a1990440919 Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 15 Sep 2009 01:00:29 +0000 Subject: [PATCH 1/1] libdispatch-84.5.tar.gz --- LICENSE | 202 ++ .../project.pbxproj | 609 ++++ examples/Dispatch Samples/ReadMe.txt | 93 + examples/Dispatch Samples/apply.c | 123 + examples/Dispatch Samples/nWide.c | 127 + examples/Dispatch Samples/netcat.c | 596 ++++ examples/Dispatch Samples/proc.c | 209 ++ examples/Dispatch Samples/readFile.c | 115 + examples/Dispatch Samples/readFileF.c | 117 + examples/Dispatch Samples/timers.c | 85 + examples/DispatchLife/DispatchLife.c | 392 +++ .../DispatchLife.xcodeproj/project.pbxproj | 252 ++ examples/DispatchLife/DispatchLifeGLView.h | 59 + examples/DispatchLife/DispatchLifeGLView.m | 203 ++ .../English.lproj/InfoPlist.strings | Bin 0 -> 92 bytes .../English.lproj/MainMenu.nib/designable.nib | 2651 +++++++++++++++++ .../MainMenu.nib/keyedobjects.nib | Bin 0 -> 19575 bytes examples/DispatchLife/Info.plist | 28 + examples/DispatchLife/ReadMe.txt | 37 + examples/DispatchLife/main.m | 49 + .../DispatchWebServer/DispatchWebServer.c | 956 ++++++ .../project.pbxproj | 203 ++ examples/DispatchWebServer/ReadMe.txt | 44 + libdispatch.xcodeproj/project.pbxproj | 497 +++ man/dispatch.3 | 38 + man/dispatch_after.3 | 57 + man/dispatch_api.3 | 44 + man/dispatch_apply.3 | 80 + man/dispatch_async.3 | 234 ++ man/dispatch_benchmark.3 | 55 + man/dispatch_group_create.3 | 149 + man/dispatch_object.3 | 99 + man/dispatch_once.3 | 44 + man/dispatch_queue_create.3 | 318 ++ man/dispatch_semaphore_create.3 | 114 + man/dispatch_source_create.3 | 456 +++ man/dispatch_time.3 | 110 + src/apply.c | 178 ++ src/base.h | 113 + src/benchmark.c | 114 + src/benchmark.h | 83 + src/dispatch.h | 52 + src/group.h | 273 ++ src/hw_shims.h | 72 + src/internal.h | 299 ++ src/legacy.c | 444 +++ src/legacy.h | 748 +++++ src/object.c | 200 ++ src/object.h | 195 ++ src/object_internal.h | 110 + src/once.c | 104 + src/once.h | 77 + src/os_shims.h | 152 + src/private.h | 114 + src/protocol.defs | 91 + src/queue.c | 2080 +++++++++++++ src/queue.h | 568 ++++ src/queue_internal.h | 136 + src/queue_private.h | 122 + src/semaphore.c | 532 ++++ src/semaphore.h | 112 + src/semaphore_internal.h | 51 + src/shims.c | 65 + src/source.c | 1995 +++++++++++++ src/source.h | 583 ++++ src/source_internal.h | 102 + src/source_private.h | 129 + src/time.c | 183 ++ src/time.h | 113 + 69 files changed, 19635 insertions(+) create mode 100644 LICENSE create mode 100644 examples/Dispatch Samples/Dispatch Samples.xcodeproj/project.pbxproj create mode 100644 examples/Dispatch Samples/ReadMe.txt create mode 100644 examples/Dispatch Samples/apply.c create mode 100644 examples/Dispatch Samples/nWide.c create mode 100644 examples/Dispatch Samples/netcat.c create mode 100644 examples/Dispatch Samples/proc.c create mode 100644 examples/Dispatch Samples/readFile.c create mode 100644 examples/Dispatch Samples/readFileF.c create mode 100644 examples/Dispatch Samples/timers.c create mode 100644 examples/DispatchLife/DispatchLife.c create mode 100644 examples/DispatchLife/DispatchLife.xcodeproj/project.pbxproj create mode 100644 examples/DispatchLife/DispatchLifeGLView.h create mode 100644 examples/DispatchLife/DispatchLifeGLView.m create mode 100644 examples/DispatchLife/English.lproj/InfoPlist.strings create mode 100644 examples/DispatchLife/English.lproj/MainMenu.nib/designable.nib create mode 100644 examples/DispatchLife/English.lproj/MainMenu.nib/keyedobjects.nib create mode 100644 examples/DispatchLife/Info.plist create mode 100644 examples/DispatchLife/ReadMe.txt create mode 100644 examples/DispatchLife/main.m create mode 100644 examples/DispatchWebServer/DispatchWebServer.c create mode 100644 examples/DispatchWebServer/DispatchWebServer.xcodeproj/project.pbxproj create mode 100644 examples/DispatchWebServer/ReadMe.txt create mode 100644 libdispatch.xcodeproj/project.pbxproj create mode 100644 man/dispatch.3 create mode 100644 man/dispatch_after.3 create mode 100644 man/dispatch_api.3 create mode 100644 man/dispatch_apply.3 create mode 100644 man/dispatch_async.3 create mode 100644 man/dispatch_benchmark.3 create mode 100644 man/dispatch_group_create.3 create mode 100644 man/dispatch_object.3 create mode 100644 man/dispatch_once.3 create mode 100644 man/dispatch_queue_create.3 create mode 100644 man/dispatch_semaphore_create.3 create mode 100644 man/dispatch_source_create.3 create mode 100644 man/dispatch_time.3 create mode 100644 src/apply.c create mode 100644 src/base.h create mode 100644 src/benchmark.c create mode 100644 src/benchmark.h create mode 100644 src/dispatch.h create mode 100644 src/group.h create mode 100644 src/hw_shims.h create mode 100644 src/internal.h create mode 100644 src/legacy.c create mode 100644 src/legacy.h create mode 100644 src/object.c create mode 100644 src/object.h create mode 100644 src/object_internal.h create mode 100644 src/once.c create mode 100644 src/once.h create mode 100644 src/os_shims.h create mode 100644 src/private.h create mode 100644 src/protocol.defs create mode 100644 src/queue.c create mode 100644 src/queue.h create mode 100644 src/queue_internal.h create mode 100644 src/queue_private.h create mode 100644 src/semaphore.c create mode 100644 src/semaphore.h create mode 100644 src/semaphore_internal.h create mode 100644 src/shims.c create mode 100644 src/source.c create mode 100644 src/source.h create mode 100644 src/source_internal.h create mode 100644 src/source_private.h create mode 100644 src/time.c create mode 100644 src/time.h diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/examples/Dispatch Samples/Dispatch Samples.xcodeproj/project.pbxproj b/examples/Dispatch Samples/Dispatch Samples.xcodeproj/project.pbxproj new file mode 100644 index 0000000..15482f3 --- /dev/null +++ b/examples/Dispatch Samples/Dispatch Samples.xcodeproj/project.pbxproj @@ -0,0 +1,609 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXAggregateTarget section */ + 4C96F87F0F8288070051687B /* Samples */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 4C96F88F0F8288290051687B /* Build configuration list for PBXAggregateTarget "Samples" */; + buildPhases = ( + ); + dependencies = ( + 4C96F88B0F82881B0051687B /* PBXTargetDependency */, + 4C96F8890F8288190051687B /* PBXTargetDependency */, + 4C96F8870F8288170051687B /* PBXTargetDependency */, + 4C96F8850F8288140051687B /* PBXTargetDependency */, + 4C96F8830F82880E0051687B /* PBXTargetDependency */, + ); + name = Samples; + productName = Samples; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 4CBAB02C0F780242006D97F1 /* apply.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CBAB02A0F780242006D97F1 /* apply.c */; }; + 4CBAB04C0F7802DA006D97F1 /* netcat.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CBAB04A0F7802DA006D97F1 /* netcat.c */; }; + 4CBAB0530F7802F1006D97F1 /* proc.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CBAB0510F7802F1006D97F1 /* proc.c */; }; + 4CBAB0560F780314006D97F1 /* readFile.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CBAB0540F780314006D97F1 /* readFile.c */; }; + 4CBAB0590F780327006D97F1 /* timers.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CBAB0570F780327006D97F1 /* timers.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 4C96F8820F82880E0051687B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CBAB0240F7801C6006D97F1; + remoteInfo = "dispatch-apply"; + }; + 4C96F8840F8288140051687B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CBAB0300F780272006D97F1; + remoteInfo = "dispatch-netcat"; + }; + 4C96F8860F8288170051687B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CBAB0370F78028E006D97F1; + remoteInfo = "dispatch-proc"; + }; + 4C96F8880F8288190051687B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CBAB03E0F7802A6006D97F1; + remoteInfo = "dispatch-readFile"; + }; + 4C96F88A0F82881B0051687B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 08FB7793FE84155DC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4CBAB0450F7802BA006D97F1; + remoteInfo = "dispatch-timers"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 4CBAB0250F7801C6006D97F1 /* dispatch-apply */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dispatch-apply"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CBAB02A0F780242006D97F1 /* apply.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = apply.c; sourceTree = ""; }; + 4CBAB0310F780272006D97F1 /* dispatch-netcat */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dispatch-netcat"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CBAB0380F78028E006D97F1 /* dispatch-proc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dispatch-proc"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CBAB03F0F7802A6006D97F1 /* dispatch-readFile */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dispatch-readFile"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CBAB0460F7802BA006D97F1 /* dispatch-timers */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "dispatch-timers"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4CBAB04A0F7802DA006D97F1 /* netcat.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = netcat.c; sourceTree = ""; }; + 4CBAB0510F7802F1006D97F1 /* proc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = proc.c; sourceTree = ""; }; + 4CBAB0540F780314006D97F1 /* readFile.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = readFile.c; sourceTree = ""; }; + 4CBAB0570F780327006D97F1 /* timers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = timers.c; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4CBAB0230F7801C6006D97F1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CBAB02F0F780272006D97F1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CBAB0360F78028E006D97F1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CBAB03D0F7802A6006D97F1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CBAB0440F7802BA006D97F1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* Dispatch Samples */ = { + isa = PBXGroup; + children = ( + 08FB7795FE84155DC02AAC07 /* Source */, + C6A0FF2B0290797F04C91782 /* Documentation */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = "Dispatch Samples"; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 4CBAB0570F780327006D97F1 /* timers.c */, + 4CBAB0540F780314006D97F1 /* readFile.c */, + 4CBAB0510F7802F1006D97F1 /* proc.c */, + 4CBAB04A0F7802DA006D97F1 /* netcat.c */, + 4CBAB02A0F780242006D97F1 /* apply.c */, + ); + name = Source; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 4CBAB0250F7801C6006D97F1 /* dispatch-apply */, + 4CBAB0310F780272006D97F1 /* dispatch-netcat */, + 4CBAB0380F78028E006D97F1 /* dispatch-proc */, + 4CBAB03F0F7802A6006D97F1 /* dispatch-readFile */, + 4CBAB0460F7802BA006D97F1 /* dispatch-timers */, + ); + name = Products; + sourceTree = ""; + }; + C6A0FF2B0290797F04C91782 /* Documentation */ = { + isa = PBXGroup; + children = ( + ); + name = Documentation; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4CBAB0240F7801C6006D97F1 /* dispatch-apply */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4CBAB0290F7801E5006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-apply" */; + buildPhases = ( + 4CBAB0220F7801C6006D97F1 /* Sources */, + 4CBAB0230F7801C6006D97F1 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "dispatch-apply"; + productName = "dispatch-apply"; + productReference = 4CBAB0250F7801C6006D97F1 /* dispatch-apply */; + productType = "com.apple.product-type.tool"; + }; + 4CBAB0300F780272006D97F1 /* dispatch-netcat */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4CBAB04D0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-netcat" */; + buildPhases = ( + 4CBAB02E0F780272006D97F1 /* Sources */, + 4CBAB02F0F780272006D97F1 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "dispatch-netcat"; + productName = "dispatch-netcat"; + productReference = 4CBAB0310F780272006D97F1 /* dispatch-netcat */; + productType = "com.apple.product-type.tool"; + }; + 4CBAB0370F78028E006D97F1 /* dispatch-proc */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4CBAB04E0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-proc" */; + buildPhases = ( + 4CBAB0350F78028E006D97F1 /* Sources */, + 4CBAB0360F78028E006D97F1 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "dispatch-proc"; + productName = "dispatch-proc"; + productReference = 4CBAB0380F78028E006D97F1 /* dispatch-proc */; + productType = "com.apple.product-type.tool"; + }; + 4CBAB03E0F7802A6006D97F1 /* dispatch-readFile */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4CBAB04F0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-readFile" */; + buildPhases = ( + 4CBAB03C0F7802A6006D97F1 /* Sources */, + 4CBAB03D0F7802A6006D97F1 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "dispatch-readFile"; + productName = "dispatch-readFile"; + productReference = 4CBAB03F0F7802A6006D97F1 /* dispatch-readFile */; + productType = "com.apple.product-type.tool"; + }; + 4CBAB0450F7802BA006D97F1 /* dispatch-timers */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4CBAB0500F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-timers" */; + buildPhases = ( + 4CBAB0430F7802BA006D97F1 /* Sources */, + 4CBAB0440F7802BA006D97F1 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "dispatch-timers"; + productName = "dispatch-timers"; + productReference = 4CBAB0460F7802BA006D97F1 /* dispatch-timers */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "Dispatch Samples" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* Dispatch Samples */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4C96F87F0F8288070051687B /* Samples */, + 4CBAB0240F7801C6006D97F1 /* dispatch-apply */, + 4CBAB0300F780272006D97F1 /* dispatch-netcat */, + 4CBAB0370F78028E006D97F1 /* dispatch-proc */, + 4CBAB03E0F7802A6006D97F1 /* dispatch-readFile */, + 4CBAB0450F7802BA006D97F1 /* dispatch-timers */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 4CBAB0220F7801C6006D97F1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CBAB02C0F780242006D97F1 /* apply.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CBAB02E0F780272006D97F1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CBAB04C0F7802DA006D97F1 /* netcat.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CBAB0350F78028E006D97F1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CBAB0530F7802F1006D97F1 /* proc.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CBAB03C0F7802A6006D97F1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CBAB0560F780314006D97F1 /* readFile.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4CBAB0430F7802BA006D97F1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CBAB0590F780327006D97F1 /* timers.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 4C96F8830F82880E0051687B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CBAB0240F7801C6006D97F1 /* dispatch-apply */; + targetProxy = 4C96F8820F82880E0051687B /* PBXContainerItemProxy */; + }; + 4C96F8850F8288140051687B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CBAB0300F780272006D97F1 /* dispatch-netcat */; + targetProxy = 4C96F8840F8288140051687B /* PBXContainerItemProxy */; + }; + 4C96F8870F8288170051687B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CBAB0370F78028E006D97F1 /* dispatch-proc */; + targetProxy = 4C96F8860F8288170051687B /* PBXContainerItemProxy */; + }; + 4C96F8890F8288190051687B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CBAB03E0F7802A6006D97F1 /* dispatch-readFile */; + targetProxy = 4C96F8880F8288190051687B /* PBXContainerItemProxy */; + }; + 4C96F88B0F82881B0051687B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4CBAB0450F7802BA006D97F1 /* dispatch-timers */; + targetProxy = 4C96F88A0F82881B0051687B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 1DEB928A08733DD80010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + SDKROOT = macosx10.6; + }; + name = Debug; + }; + 1DEB928B08733DD80010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = macosx10.6; + }; + name = Release; + }; + 4C96F8800F8288080051687B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = Samples; + }; + name = Debug; + }; + 4C96F8810F8288080051687B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = Samples; + ZERO_LINK = NO; + }; + name = Release; + }; + 4CBAB0270F7801C7006D97F1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = "dispatch-apply"; + }; + name = Debug; + }; + 4CBAB0280F7801C7006D97F1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = "dispatch-apply"; + ZERO_LINK = NO; + }; + name = Release; + }; + 4CBAB0330F780273006D97F1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = "dispatch-netcat"; + }; + name = Debug; + }; + 4CBAB0340F780273006D97F1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = "dispatch-netcat"; + ZERO_LINK = NO; + }; + name = Release; + }; + 4CBAB03A0F78028F006D97F1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = "dispatch-proc"; + }; + name = Debug; + }; + 4CBAB03B0F78028F006D97F1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = "dispatch-proc"; + ZERO_LINK = NO; + }; + name = Release; + }; + 4CBAB0410F7802A7006D97F1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = "dispatch-readFile"; + }; + name = Debug; + }; + 4CBAB0420F7802A7006D97F1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = "dispatch-readFile"; + ZERO_LINK = NO; + }; + name = Release; + }; + 4CBAB0480F7802BB006D97F1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = "dispatch-timers"; + }; + name = Debug; + }; + 4CBAB0490F7802BB006D97F1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = "dispatch-timers"; + ZERO_LINK = NO; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "Dispatch Samples" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB928A08733DD80010E9CD /* Debug */, + 1DEB928B08733DD80010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4C96F88F0F8288290051687B /* Build configuration list for PBXAggregateTarget "Samples" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4C96F8800F8288080051687B /* Debug */, + 4C96F8810F8288080051687B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4CBAB0290F7801E5006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-apply" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4CBAB0270F7801C7006D97F1 /* Debug */, + 4CBAB0280F7801C7006D97F1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4CBAB04D0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-netcat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4CBAB0330F780273006D97F1 /* Debug */, + 4CBAB0340F780273006D97F1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4CBAB04E0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-proc" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4CBAB03A0F78028F006D97F1 /* Debug */, + 4CBAB03B0F78028F006D97F1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4CBAB04F0F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-readFile" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4CBAB0410F7802A7006D97F1 /* Debug */, + 4CBAB0420F7802A7006D97F1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4CBAB0500F7802DA006D97F1 /* Build configuration list for PBXNativeTarget "dispatch-timers" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4CBAB0480F7802BB006D97F1 /* Debug */, + 4CBAB0490F7802BB006D97F1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/examples/Dispatch Samples/ReadMe.txt b/examples/Dispatch Samples/ReadMe.txt new file mode 100644 index 0000000..3214db1 --- /dev/null +++ b/examples/Dispatch Samples/ReadMe.txt @@ -0,0 +1,93 @@ +### DispatchProcessMonitor ### + +=========================================================================== +DESCRIPTION: + +Sample code showing how to: monitor process, do file and network I/O, +create and manage timers, and use dispatch_apply + +=========================================================================== +BUILD REQUIREMENTS: + +Mac OS X version 10.6 Snow Leopard + +=========================================================================== +RUNTIME REQUIREMENTS: + +Mac OS X version 10.6 Snow Leopard + +=========================================================================== +PACKAGING LIST: + +apply.c - dispatch_apply examples +netcat.c - network I/O examples +nWide.c - use of dispatch_semaphore to limit number of in-flight blocks +proc.c - process monitoring example +readFile.c - file I/O examples +readFileF.c - file I/O examples without Blocks +timers.c - create and manage timers + +=========================================================================== +SAMPLE USAGE: + +dispatch-apply + +dispatch-apply takes no arguments. When run it will display some status +messages and timing information. + +dispatch-netcat + +Open two terminal windows. In one window run the "server": + +cat ReadMe.txt | dispatch-netcat -l localhost 5050 + +In the other run the "client": + +dispatch-netcat localhost 5050 + +Your server will send the contents of ReadMe.txt to the client, the server +will close it's connection and exit. The client will display whatever +the server sent (the ReadMe.txt file). See the main function in netcat.c +for more options. + +dispatch-nWide + +dispatch-nWide takes no arguments. When run it will display explanatory +text. + +dispatch-proc + +dispatch-proc takes no arguments. When run it will display output from +some processes it runs, and it will display information from the +process lifecycle events dispatch generates. + +dispatch-readFile + +Run dispatch-readFile with a filename as an argument: + +dispatch-readFile ReadMe.txt + +It will read the file 10 (or fewer) bytes at a time and display how many +bytes dispatch thinks are remaining to read. + +dispatch-readFileF + +Exactly the same as dispatch-readFile, but written without the use of Blocks. + +dispatch-timers + +dispatch-timers takes no arguments, running it display timer ticks for +a timer with an initial interval of one second, changing to one half second +after the first three events. It will exit after six events. + +=========================================================================== +CHANGES FROM PREVIOUS VERSIONS: + +Version 1.1 +- Updated to current libdispatch API, and added samples readFileF.c and +nWide.c +Version 1.0 +- First version + +=========================================================================== +Copyright (C) 2009 Apple Inc. All rights reserved. diff --git a/examples/Dispatch Samples/apply.c b/examples/Dispatch Samples/apply.c new file mode 100644 index 0000000..3eb39a5 --- /dev/null +++ b/examples/Dispatch Samples/apply.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include + +#define kIT 10 + +uint64_t elapsed_time; + +void timer_start() { + elapsed_time = mach_absolute_time(); +} + +double timer_milePost() { + static dispatch_once_t justOnce; + static double scale; + + dispatch_once(&justOnce, ^{ + mach_timebase_info_data_t tbi; + mach_timebase_info(&tbi); + scale = tbi.numer; + scale = scale/tbi.denom; + printf("Scale is %10.4f Just computed once courtesy of dispatch_once()\n", scale); + }); + + uint64_t now = mach_absolute_time()-elapsed_time; + double fTotalT = now; + fTotalT = fTotalT * scale; // convert this to nanoseconds... + fTotalT = fTotalT / 1000000000.0; + return fTotalT; +} + +int +main(void) +{ + dispatch_queue_t myQueue = dispatch_queue_create("myQueue", NULL); + dispatch_group_t myGroup = dispatch_group_create(); + +// dispatch_apply on a serial queue finishes each block in order so the following code will take a little more than a second + timer_start(); + dispatch_apply(kIT, myQueue, ^(size_t current){ + printf("Block #%ld of %d is being run\n", + current+1, // adjusting the zero based current iteration we get passed in + kIT); + usleep(USEC_PER_SEC/10); + }); + printf("and dispatch_apply( serial queue ) returned after %10.4lf seconds\n",timer_milePost()); + +// dispatch_apply on a concurrent queue returns after all blocks are finished, however it can execute them concurrently with each other +// so this will take quite a bit less time + timer_start(); + dispatch_apply(kIT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t current){ + printf("Block #%ld of %d is being run\n",current+1, kIT); + usleep(USEC_PER_SEC/10); + }); + printf("and dispatch_apply( concurrent queue) returned after %10.4lf seconds\n",timer_milePost()); + +// To execute all blocks in a dispatch_apply asynchonously, you will need to perform the dispatch_apply +// asynchonously, like this (NOTE the nested dispatch_apply inside of the async block.) +// Also note the use of the dispatch_group so that we can ultimatly know when the work is +// all completed + + timer_start(); + dispatch_group_async(myGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + dispatch_apply(kIT, myQueue, ^(size_t current){ + printf("Block #%ld of %d is being run\n",current+1, kIT); + usleep(USEC_PER_SEC/10); + }); + }); + + printf("and dispatch_group_async( dispatch_apply( )) returned after %10.4lf seconds\n",timer_milePost()); + printf("Now to wait for the dispatch group to finish...\n"); + dispatch_group_wait(myGroup, UINT64_MAX); + printf("and we are done with dispatch_group_async( dispatch_apply( )) after %10.4lf seconds\n",timer_milePost()); + return 0; +} + diff --git a/examples/Dispatch Samples/nWide.c b/examples/Dispatch Samples/nWide.c new file mode 100644 index 0000000..92914a9 --- /dev/null +++ b/examples/Dispatch Samples/nWide.c @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2009 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ + +/* + * nWide.c + * Samples project + * + * Created by Mensch on 5/1/09. + * Copyright 2009 Apple, Inc. All rights reserved. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#import +#include + + +/* + * Demonstrate using dispatch_semaphore to create a concurrent queue that + * allows only a fixed number of blocks to be in flight at any given time + */ + +int main (int argc, const char * argv[]) { + dispatch_group_t mg = dispatch_group_create(); + dispatch_semaphore_t ds; + __block int numRunning = 0; + int qWidth = 5; + int numWorkBlocks = 100; + + if (argc >= 2) { + qWidth = atoi(argv[1]); // use the command 1st line parameter as the queue width + if (qWidth==0) qWidth==1; // protect against bad values + } + + if (argc >=3) { + numWorkBlocks = atoi(argv[2]); // use the 2nd command line parameter as the queue width + if (numWorkBlocks==0) numWorkBlocks==1; // protect against bad values + } + + printf("Starting dispatch semaphore test to simulate a %d wide dispatch queue\n", qWidth ); + ds = dispatch_semaphore_create(qWidth); + + int i; + for (i=0; i +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #define DEBUG 1 + +#if DEBUG +#define dlog(a) dispatch_debug(a, #a) +#else +#define dlog(a) do { } while(0) +#endif + +void usage(void); +void *run_block(void *); +void setup_fd_relay(int netfd /* bidirectional */, + int infd /* local input */, + int outfd /* local output */, + void (^finalizer_block)(void)); +void doreadwrite(int fd1, int fd2, char *buffer, size_t len); + +#define BUFFER_SIZE 1099 + +int main(int argc, char *argv[]) { + + int ch; + bool use_v4_only = false, use_v6_only = false; + bool debug = false, no_stdin = false; + bool keep_listening = false, do_listen = false; + bool do_loookups = true, verbose = false; + bool do_udp = false, do_bind_ip = false, do_bind_port = false; + const char *hostname, *servname; + int ret; + struct addrinfo hints, *aires, *aires0; + const char *bind_hostname, *bind_servname; + + dispatch_queue_t dq; + dispatch_group_t listen_group = NULL; + + while ((ch = getopt(argc, argv, "46Ddhklnvup:s:")) != -1) { + switch (ch) { + case '4': + use_v4_only = true; + break; + case '6': + use_v6_only = true; + break; + case 'D': + debug = true; + break; + case 'd': + no_stdin = true; + break; + case 'h': + usage(); + break; + case 'k': + keep_listening = true; + break; + case 'l': + do_listen = true; + break; + case 'n': + do_loookups = false; + break; + case 'v': + verbose = true; + break; + case 'u': + do_udp = true; + break; + case 'p': + do_bind_port = true; + bind_servname = optarg; + break; + case 's': + do_bind_ip = true; + bind_hostname = optarg; + break; + case '?': + default: + usage(); + break; + } + } + + argc -= optind; + argv += optind; + + if (use_v4_only && use_v6_only) { + errx(EX_USAGE, "-4 and -6 specified"); + } + + if (keep_listening && !do_listen) { + errx(EX_USAGE, "-k specified but no -l"); + } + + if (do_listen && (do_bind_ip || do_bind_port)) { + errx(EX_USAGE, "-p or -s option with -l"); + } + + if (do_listen) { + if (argc >= 2) { + hostname = argv[0]; + servname = argv[1]; + } else if (argc >= 1) { + hostname = NULL; + servname = argv[0]; + } else { + errx(EX_USAGE, "No service name provided"); + } + } else { + if (argc >= 2) { + hostname = argv[0]; + servname = argv[1]; + } else { + errx(EX_USAGE, "No hostname and service name provided"); + } + } + + if (do_bind_ip || do_bind_port) { + if (!do_bind_ip) { + bind_hostname = NULL; + } + if (!do_bind_port) { + bind_servname = NULL; + } + } + + openlog(getprogname(), LOG_PERROR|LOG_CONS, LOG_DAEMON); + setlogmask(debug ? LOG_UPTO(LOG_DEBUG) : verbose ? LOG_UPTO(LOG_INFO) : LOG_UPTO(LOG_ERR)); + + dq = dispatch_queue_create("netcat", NULL); + listen_group = dispatch_group_create(); + + bzero(&hints, sizeof(hints)); + hints.ai_family = use_v4_only ? PF_INET : (use_v6_only ? PF_INET6 : PF_UNSPEC); + hints.ai_socktype = do_udp ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_protocol = do_udp ? IPPROTO_UDP : IPPROTO_TCP; + hints.ai_flags = (!do_loookups ? AI_NUMERICHOST | AI_NUMERICSERV : 0) | (do_listen ? AI_PASSIVE : 0); + + ret = getaddrinfo(hostname, servname, &hints, &aires0); + if (ret) { + errx(1, "getaddrinfo(%s, %s): %s", hostname, servname, gai_strerror(ret)); + } + + for (aires = aires0; aires; aires = aires->ai_next) { + if (do_listen) { + // asynchronously set up the socket + dispatch_retain(dq); + dispatch_group_async(listen_group, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), + ^{ + int s, val = 1; + dispatch_source_t ds; + + s = socket(aires->ai_family, aires->ai_socktype, aires->ai_protocol); + if (s < 0) { + warn("socket"); + return; + } + + if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)) < 0) { + warn("Could not set SO_REUSEADDR"); + } + + if(setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (const char *)&val, sizeof(val)) < 0) { + warn("Could not set SO_REUSEPORT"); + } + + if(setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)) < 0) { + warn("Could not set SO_NOSIGPIPE"); + } + + if (bind(s, aires->ai_addr, aires->ai_addrlen) < 0) { + warn("bind"); + close(s); + return; + } + + listen(s, 2); + syslog(LOG_DEBUG, "listening on socket %d", s); + ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, s, 0, dq); + dispatch_source_set_event_handler(ds, ^{ + // got an incoming connection + int s2, lfd = dispatch_source_get_handle(ds); + dispatch_queue_t listen_queue = dispatch_get_current_queue(); + + // prevent further accept(2)s across multiple sources + dispatch_retain(listen_queue); + dispatch_suspend(listen_queue); + + if (do_udp) { + // lfd is our socket, but let's connect in the reverse + // direction to set up the connection fully + char udpbuf[4]; + struct sockaddr_storage sockin; + socklen_t socklen; + ssize_t peeklen; + int cret; + + socklen = sizeof(sockin); + peeklen = recvfrom(lfd, udpbuf, sizeof(udpbuf), + MSG_PEEK, (struct sockaddr *)&sockin, &socklen); + if (peeklen < 0) { + warn("recvfrom"); + dispatch_resume(listen_queue); + dispatch_release(listen_queue); + return; + } + + cret = connect(lfd, (struct sockaddr *)&sockin, socklen); + if (cret < 0) { + warn("connect"); + dispatch_resume(listen_queue); + dispatch_release(listen_queue); + return; + } + + s2 = lfd; + syslog(LOG_DEBUG, "accepted socket %d", s2); + } else { + s2 = accept(lfd, NULL, NULL); + if (s2 < 0) { + warn("accept"); + dispatch_resume(listen_queue); + dispatch_release(listen_queue); + return; + } + syslog(LOG_DEBUG, "accepted socket %d -> %d", lfd, s2); + } + + + setup_fd_relay(s2, no_stdin ? -1 : STDIN_FILENO, STDOUT_FILENO, ^{ + if (!do_udp) { + close(s2); + } + dispatch_resume(listen_queue); + dispatch_release(listen_queue); + if (!keep_listening) { + exit(0); + } + }); + }); + dispatch_resume(ds); + dispatch_release(dq); + }); + } else { + // synchronously try each address to try to connect + __block bool did_connect = false; + + dispatch_sync(dq, ^{ + int s, val = 1; + + s = socket(aires->ai_family, aires->ai_socktype, aires->ai_protocol); + if (s < 0) { + warn("socket"); + return; + } + + if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (const char *)&val, sizeof(val)) < 0) { + warn("Could not set SO_REUSEADDR"); + } + + if(setsockopt(s, SOL_SOCKET, SO_REUSEPORT, (const char *)&val, sizeof(val)) < 0) { + warn("Could not set SO_REUSEPORT"); + } + + if(setsockopt(s, SOL_SOCKET, SO_NOSIGPIPE, &val, sizeof(val)) < 0) { + warn("Could not set SO_NOSIGPIPE"); + } + + if (do_bind_port || do_bind_ip) { + struct addrinfo bhints, *bind_aires; + int bret; + in_port_t bport; + + bzero(&bhints, sizeof(bhints)); + bhints.ai_family = aires->ai_family; + bhints.ai_socktype = aires->ai_socktype; + bhints.ai_protocol = aires->ai_protocol; + bhints.ai_flags = (do_bind_ip ? AI_NUMERICHOST : 0) | (do_bind_port ? AI_NUMERICSERV : 0) | AI_PASSIVE; + + bret = getaddrinfo(bind_hostname, bind_servname, &bhints, &bind_aires); + if (bret) { + warnx("getaddrinfo(%s, %s): %s", bind_hostname, bind_servname, gai_strerror(bret)); + close(s); + freeaddrinfo(bind_aires); + return; + } + + switch(bind_aires->ai_family) { + case PF_INET: + bport = ((struct sockaddr_in *)bind_aires->ai_addr)->sin_port; + break; + case PF_INET6: + bport = ((struct sockaddr_in6 *)bind_aires->ai_addr)->sin6_port; + break; + default: + bport = htons(0); + break; + } + + if (ntohs(bport) > 0 && ntohs(bport) < IPPORT_RESERVED) { + bret = bindresvport_sa(s, (struct sockaddr *)bind_aires->ai_addr); + } else { + bret = bind(s, bind_aires->ai_addr, bind_aires->ai_addrlen); + } + + if (bret < 0) { + warn("bind"); + close(s); + freeaddrinfo(bind_aires); + return; + } + + freeaddrinfo(bind_aires); + } + + if (connect(s, aires->ai_addr, aires->ai_addrlen) < 0) { + syslog(LOG_INFO, "connect to %s port %s (%s) failed: %s", + hostname, + servname, + aires->ai_protocol == IPPROTO_TCP ? "tcp" : aires->ai_protocol == IPPROTO_UDP ? "udp" : "unknown", + strerror(errno)); + close(s); + return; + } + + syslog(LOG_INFO, "Connection to %s %s port [%s] succeeded!", + hostname, + servname, + aires->ai_protocol == IPPROTO_TCP ? "tcp" : aires->ai_protocol == IPPROTO_UDP ? "udp" : "unknown"); + did_connect = true; + + if (do_udp) { + // netcat sends a few bytes to set up the connection + doreadwrite(-1, s, "XXXX", 4); + } + + setup_fd_relay(s, no_stdin ? -1 : STDIN_FILENO, STDOUT_FILENO, ^{ + close(s); + exit(0); + }); + }); + + if (did_connect) { + break; + } + } + } + + dispatch_group_wait(listen_group, DISPATCH_TIME_FOREVER); + freeaddrinfo(aires0); + + if (!do_listen && aires == NULL) { + // got to the end of the address list without connecting + exit(1); + } + + dispatch_main(); + + return 0; +} + +void usage(void) +{ + fprintf(stderr, "Usage: %s [-4] [-6] [-D] [-d] [-h] [-k] [-l] [-n] [-v]\n", getprogname()); + fprintf(stderr, " \t[-u] [-p ] [-s ]\n"); + exit(EX_USAGE); +} + +void *run_block(void *arg) +{ + void (^b)(void) = (void (^)(void))arg; + + b(); + + _Block_release(arg); + + return NULL; +} + +/* + * Read up-to as much as is requested, and write + * that to the other fd, taking into account exceptional + * conditions and re-trying + */ +void doreadwrite(int fd1, int fd2, char *buffer, size_t len) { + ssize_t readBytes, writeBytes, totalWriteBytes; + + if (fd1 != -1) { + syslog(LOG_DEBUG, "trying to read %ld bytes from fd %d", len, fd1); + readBytes = read(fd1, buffer, len); + if (readBytes < 0) { + if (errno == EINTR || errno == EAGAIN) { + /* can't do anything now, hope we get called again */ + syslog(LOG_DEBUG, "error read fd %d: %s (%d)", fd1, strerror(errno), errno); + return; + } else { + err(1, "read fd %d", fd1); + } + } else if (readBytes == 0) { + syslog(LOG_DEBUG, "EOF on fd %d", fd1); + return; + } + syslog(LOG_DEBUG, "read %ld bytes from fd %d", readBytes, fd1); + } else { + readBytes = len; + syslog(LOG_DEBUG, "read buffer has %ld bytes", readBytes); + } + + totalWriteBytes = 0; + do { + writeBytes = write(fd2, buffer+totalWriteBytes, readBytes-totalWriteBytes); + if (writeBytes < 0) { + if (errno == EINTR || errno == EAGAIN) { + continue; + } else { + err(1, "write fd %d", fd2); + } + } + syslog(LOG_DEBUG, "wrote %ld bytes to fd %d", writeBytes, fd2); + totalWriteBytes += writeBytes; + + } while (totalWriteBytes < readBytes); + + return; +} + +/* + * We set up dispatch sources for netfd and infd. + * Since only one callback is called at a time per-source, + * we don't need any additional serialization, and the network + * and infd could be read from at the same time. + */ +void setup_fd_relay(int netfd /* bidirectional */, + int infd /* local input */, + int outfd /* local output */, + void (^finalizer_block)(void)) +{ + dispatch_source_t netsource = NULL, insource = NULL; + + dispatch_queue_t teardown_queue = dispatch_queue_create("teardown_queue", NULL); + + void (^finalizer_block_copy)(void) = _Block_copy(finalizer_block); // release after calling + void (^cancel_hander)(dispatch_source_t source) = ^(dispatch_source_t source){ + dlog(source); + dlog(teardown_queue); + + /* + * allowing the teardown queue to become runnable will get + * the teardown block scheduled, which will cancel all other + * sources and call the client-supplied finalizer + */ + dispatch_resume(teardown_queue); + dispatch_release(teardown_queue); + }; + void (^event_handler)(dispatch_source_t source, int wfd) = ^(dispatch_source_t source, int wfd) { + int rfd = dispatch_source_get_handle(source); + size_t bytesAvail = dispatch_source_get_data(source); + char *buffer; + + syslog(LOG_DEBUG, "dispatch source %d -> %d has %lu bytes available", + rfd, wfd, bytesAvail); + if (bytesAvail == 0) { + dlog(source); + dispatch_source_cancel(source); + return; + } + buffer = malloc(BUFFER_SIZE); + doreadwrite(rfd,wfd, buffer, MIN(BUFFER_SIZE, bytesAvail+2)); + free(buffer); + }; + + /* + * Suspend this now twice so that neither source can accidentally resume it + * while we're still setting up the teardown block. When either source + * gets an EOF, the queue is resumed so that it can teardown the other source + * and call the client-supplied finalizer + */ + dispatch_suspend(teardown_queue); + dispatch_suspend(teardown_queue); + + if (infd != -1) { + dispatch_retain(teardown_queue); // retain so that we can resume in this block later + + dlog(teardown_queue); + + // since the event handler serializes, put this on a concurrent queue + insource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, infd, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); + dispatch_source_set_event_handler(insource, ^{ event_handler(insource, netfd); }); + dispatch_source_set_cancel_handler(insource, ^{ cancel_hander(insource); }); + dispatch_resume(insource); + dlog(insource); + } + + dispatch_retain(teardown_queue); // retain so that we can resume in this block later + + dlog(teardown_queue); + + // since the event handler serializes, put this on a concurrent queue + netsource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, netfd, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); + dispatch_source_set_event_handler(netsource, ^{ event_handler(netsource, outfd); }); + dispatch_source_set_cancel_handler(netsource, ^{ cancel_hander(netsource); }); + dispatch_resume(netsource); + dlog(netsource); + + dispatch_async(teardown_queue, ^{ + syslog(LOG_DEBUG, "Closing connection on fd %d -> %d -> %d", infd, netfd, outfd); + + if (insource) { + dlog(insource); + dispatch_source_cancel(insource); + dispatch_release(insource); // matches initial create + dlog(insource); + } + + dlog(netsource); + dispatch_source_cancel(netsource); + dispatch_release(netsource); // matches initial create + dlog(netsource); + + dlog(teardown_queue); + + finalizer_block_copy(); + _Block_release(finalizer_block_copy); + }); + + /* Resume this once so their either source can do the second resume + * to start the teardown block running + */ + dispatch_resume(teardown_queue); + dispatch_release(teardown_queue); // matches initial create + dlog(teardown_queue); +} + diff --git a/examples/Dispatch Samples/proc.c b/examples/Dispatch Samples/proc.c new file mode 100644 index 0000000..511b42f --- /dev/null +++ b/examples/Dispatch Samples/proc.c @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +extern char **environ; +dispatch_queue_t qpf; +volatile int exitcount = 0; + +// maximum value for exitcount before we quit +#define proccount 2 + + +struct qp_msg { + FILE *f; + char *str; +}; + +void qpf_puts(void *m_) { + struct qp_msg *m = m_; + fputs(m->str, m->f); + free(m->str); + free(m); +} + +void qfprintf(FILE *f, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + struct qp_msg *m = malloc(sizeof(struct qp_msg)); + assert(m); + vasprintf(&m->str, fmt, ap); + m->f = f; + dispatch_async(qpf, ^(void) { qpf_puts(m); }); + va_end(ap); +} + +#define qprintf(fmt...) qfprintf(stdout, ## fmt) + +/* context structure, contains a process id and the + * command line arguments used to launch it. Used to + * provide context info to the block associated + * with a process event source. + */ +struct pinfo { + pid_t pid; + dispatch_source_t source; + char **argv; +}; + +/* pid_finalize() is called when the dispatch source is released. + * this block is attached to the attribute that is passed to dispatch_source_proc_create(), + * and is thus associated with the dispatch source. */ +void pid_finalize(struct pinfo *pi) { + qprintf("process %d is done watching %s (%d)\n", getpid(), pi->argv[0], pi->pid); + dispatch_release(pi->source); + if (OSAtomicIncrement32(&exitcount) == proccount) { + qprintf("both processes exited\n"); + dispatch_sync(qpf,^{}); + exit(0); + } +} + + +/* pid_event() is called from a block that is associated with a process event + * source for a specific process id (via dispatch_source_proc_create()). When + * such an event occurs, pid_event() calls dispatch_source_get_context() to + * gain access to the pid and process name that were stored in the context at + * the time the block was attached to the event source. + */ +#define FLAG(X) ((dispatch_source_get_data(src) & DISPATCH_PROC_##X) ? #X" " : "") + +void pid_event(struct pinfo *pi) { + dispatch_source_t src = pi->source; + + qprintf("process %d %s, flags: %x %s%s%s%s\n", pi->pid, pi->argv[0], dispatch_source_get_data(src), FLAG(EXIT), FLAG(FORK), FLAG(EXEC), FLAG(SIGNAL)); + if (dispatch_source_get_data(src) & DISPATCH_PROC_EXIT) { + int s; + waitpid(dispatch_source_get_handle(src), &s, WNOHANG); + qprintf(" %s exit status %d\n", pi->argv[0], s); + dispatch_source_cancel(src); + } +} + +/* proc_start() takes a context pointer (ppi), and a dispatch queue (pq), + * and spawns the process named in ppi->argv[0]. The resulting process id + * is stored in the context (ppi->pid). On successfully spawning the process, + * it creates a dispatch source for the purpose of executing the routine pid_event(pi,ev) + * when certain events (exit, fork, exec, reap, or signal) occur to the process. + */ +void proc_start(void *ppi, dispatch_queue_t pq) { + struct pinfo *pi = ppi; + + int rc = posix_spawnp(&pi->pid, pi->argv[0], NULL, NULL, pi->argv, environ); + if (rc) { + int e = errno; + qprintf("Can't spawn %s (rc=%d, e=%d %s)\n", pi->argv[0], rc, e, strerror(e)); + } else { + + dispatch_source_t dsp = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pi->pid, DISPATCH_PROC_EXIT|DISPATCH_PROC_FORK|DISPATCH_PROC_EXEC|DISPATCH_PROC_SIGNAL, pq); + dispatch_source_set_event_handler_f(dsp, (dispatch_function_t)pid_event); + dispatch_source_set_cancel_handler_f(dsp, (dispatch_function_t)pid_finalize); + pi->source = dsp; + dispatch_set_context(dsp, pi); + dispatch_resume(dsp); + + qprintf("process %d spawned %s: %d, watching with event source: %p\n", getpid(), pi->argv[0], pi->pid, dsp); + + } +} + +int main(int argc, char *argv[]) { + struct pinfo pi, pi2, pi3; + struct pinfo *ppi2 = & pi2, *ppi3 = &pi3; + + char *av[] = {argv[0], NULL}; // set up context info (struct pinfo) for this process. + pi.pid = getpid(); + pi.argv = av; + + char *av2[] = {"sleep", "3", NULL}; // set up context info (struct pinfo) for the sleep tool + pi2.argv = av2; + + char *av3[] = {"script", "/tmp/LOG", "banner", "-w80", "!", NULL}; // set up context info (struct pinfo) for the script tool + pi3.argv = av3; + + dispatch_queue_t pq = dispatch_queue_create("PQ", NULL); // create our main processing queue + + qpf = dispatch_queue_create("qprintf", NULL); // create a separate queue for printf + + /* create a dispatch source that will call the routine pid_event(pi,ev) + * when certain events occur to the specified process (pi->pid). The dispatch source is + * associated with the dispatch queue that was created in this routine (pq). This example + * requests the block be executed whenever one of the following events occurs: + * exit, fork, exec, reap, or signal. + */ + dispatch_source_t procSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pi.pid, DISPATCH_PROC_EXIT|DISPATCH_PROC_FORK|DISPATCH_PROC_EXEC|DISPATCH_PROC_SIGNAL, pq); + + dispatch_source_set_event_handler_f(procSource, (dispatch_function_t)pid_event); + dispatch_source_set_cancel_handler_f(procSource, (dispatch_function_t)pid_finalize); + pi.source = procSource; + dispatch_set_context(procSource, &pi); + dispatch_resume(procSource); + + /* create a block (which simply calls proc_start()), and dispatch it to the queue. + * proc_start() will spawn the process named by ppiX->argv[0], and set up + * another block (containing a call to pid_event()) on an event source that + * will recieve process events... + */ + dispatch_async(pq, ^(void) { proc_start( ppi2, pq ); }); // launch the sleep tool, and create the process watcher for it + dispatch_async(pq, ^(void) { proc_start( ppi3, pq ); }); // launch the script tool, and create the process watcher for it + + + dispatch_main(); // wait for all the queued and spawned items to finish... +} diff --git a/examples/Dispatch Samples/readFile.c b/examples/Dispatch Samples/readFile.c new file mode 100644 index 0000000..9c537c5 --- /dev/null +++ b/examples/Dispatch Samples/readFile.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc, char* argv[]) +{ + int infd; + dispatch_source_t fileSource; + + if (argc != 2) { + fprintf(stderr, "usage: %s file ...\n", argv[0]); + exit(1); + } + + + infd = open(argv[1], O_RDONLY); + if (infd == -1) { + perror(argv[1]); + exit(1); + } + + if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) { + perror(argv[1]); + exit(1); + } + + fileSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, infd, 0, dispatch_queue_create("read source queue",NULL)); + + dispatch_source_set_event_handler( fileSource, ^{ + char buffer[10]; + size_t estimated = dispatch_source_get_data(fileSource); + printf("Estimated bytes available: %ld\n", estimated); + ssize_t actual = read(infd, buffer, sizeof(buffer)); + if (actual == -1) { + if (errno != EAGAIN) { + perror("read"); + exit(-1); + } + } else { + if (estimated>actual) { + printf(" bytes read: %ld\n", actual); + } else { + // end of file has been reached. + printf(" last bytes read: %ld\n", actual); + dispatch_source_cancel(fileSource); + } + } + }); + + dispatch_source_set_cancel_handler( fileSource, ^{ + // release all our associated dispatch data structures + dispatch_release(fileSource); + dispatch_release(dispatch_get_current_queue()); + // close the file descriptor because we are done reading it + close(infd); + // and since we have nothing left to do, exit the tool + exit(0); + + }); + + dispatch_resume(fileSource); + + dispatch_main(); + + return 0; +} diff --git a/examples/Dispatch Samples/readFileF.c b/examples/Dispatch Samples/readFileF.c new file mode 100644 index 0000000..6546714 --- /dev/null +++ b/examples/Dispatch Samples/readFileF.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + + +void readFileData(void* theSource) { + char buffer[10]; + size_t estimated = dispatch_source_get_data(theSource); + printf("Estimated bytes available: %ld\n", estimated); + ssize_t actual = read(dispatch_source_get_handle(theSource), buffer, sizeof(buffer)); + if (actual == -1) { + if (errno != EAGAIN) { + perror("read"); + exit(-1); + } + } else { + if (estimated>actual) { + printf(" bytes read: %ld\n", actual); + } else { + // end of file has been reached. + printf(" last bytes read: %ld\n", actual); + dispatch_source_cancel(theSource); + } + } +} + +void cancelSource(void* theSource) { + close(dispatch_source_get_handle(theSource)); + dispatch_release(theSource); + dispatch_release(dispatch_get_current_queue()); + printf("Everything is finished, goodbye.\n"); + exit(0); +} + +int main(int argc, char* argv[]) +{ + int infd; + dispatch_source_t fileSource; + + if (argc != 2) { + fprintf(stderr, "usage: %s file ...\n", argv[0]); + exit(1); + } + + + infd = open(argv[1], O_RDONLY); + if (infd == -1) { + perror(argv[1]); + exit(1); + } + + if (fcntl(infd, F_SETFL, O_NONBLOCK) != 0) { + perror(argv[1]); + exit(1); + } + + fileSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, infd, 0, dispatch_queue_create("read source queue",NULL)); + dispatch_source_set_event_handler_f( fileSource, readFileData); + dispatch_source_set_cancel_handler_f( fileSource, cancelSource); + // setting the context pointer to point to the source itself means the functions will get the source + // as a paremeter, from there they can get all the information they need. + dispatch_set_context(fileSource, fileSource); + dispatch_resume(fileSource); + + dispatch_main(); + + return 0; +} diff --git a/examples/Dispatch Samples/timers.c b/examples/Dispatch Samples/timers.c new file mode 100644 index 0000000..7dc9f8c --- /dev/null +++ b/examples/Dispatch Samples/timers.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ + +#include +#include +#include +#include +#include +#include + +#include + +int main(int argc, char* argv[]) +{ + dispatch_source_t theTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_queue_create("timer queue",NULL)); + + __block int i = 0; + + printf("Starting to count by seconds\n"); + + dispatch_source_set_event_handler(theTimer, ^{ + printf("%d\n", ++i); + if (i >= 6) { + printf("i>6\n"); + dispatch_source_cancel(theTimer); + } + if (i == 3) { + printf("switching to half seconds\n"); + dispatch_source_set_timer(theTimer, DISPATCH_TIME_NOW, NSEC_PER_SEC / 2, 0); + } + }); + + dispatch_source_set_cancel_handler(theTimer, ^{ + printf("dispatch source canceled OK\n"); + dispatch_release(theTimer); + exit(0); + }); + + dispatch_source_set_timer(theTimer, dispatch_time(DISPATCH_TIME_NOW,NSEC_PER_SEC) , NSEC_PER_SEC, 0); + + dispatch_resume(theTimer); + dispatch_main(); + + return 0; +} diff --git a/examples/DispatchLife/DispatchLife.c b/examples/DispatchLife/DispatchLife.c new file mode 100644 index 0000000..0871e4a --- /dev/null +++ b/examples/DispatchLife/DispatchLife.c @@ -0,0 +1,392 @@ +/* + * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ +/*! + @header Life + An asynchronous variation of Conway's Game of Life implemented with + GCD. Like the classic version, the game board consists of a grid of + cells that can live, die or multiply by the following rules[1]: + + 1. Survivals. Every [living cell] with two or three neighboring + [living cells] survives for the next generation. + 2. Deaths. Each [living cell] wiht four or more neighbors dies (is + removed) from overpopulation. Every [living cell] with one + neighbor or none dies from isolation. + 3. Births. Each empty cell adjacent to exactly three neighbors--no + more, no fewer--is a birth cell. A [living cell] is placed on it + at the next move. + + However, unlike the classic version, not all deaths and births occur + simultaneously in a single, synchronous, "move" of the game board. + Instead the rules are applies to each cell independently based on its + observations of the cells around it. + + Each cell is backed by a GCD queue which manages the synchronization + of the cells internal state (living or dead). When a cell's state + changes, a notification in the form of a dispatch_call() of the + cell_needs_update() work function is sent to all adjacent cells so + that the state of those cells may be re-evaluated. + + To re-evaluate the state of a cell, a request of the current state of + all adjecent cells is sent in the form of a dispatch_call() of the + _cell_is_alive() work function. The state of the adjacent cells is + returned to the requestor via the _cell_is_alive_callback() completion + callback. Once all outstanding completion callbacks have been + received, the cell updates its state according to the aforementioned + rules. If the application of these rules results in another state + change, the update_cell() notification is once again sent out, + repeating the process. + + Due to the highly asynchronous nature of this implementation, the + simulation's results may differ from the classic version for the same + set of initial conditions. In particular, due to non-deterministic + scheduling factors, the same set of initial condiitions is likely to + produce dramatically different results on subsequent simulations. + + [1] Martin Gardner. "MATHEMATICAL GAMES: The fantastic combinations of + John Conway's new solitaire game 'life'" Scientific American 223 + (October 1970): 120-123. + + @copyright Copyright (c) 2008-2009 Apple Inc. All rights reserved. + @updated 2009-03-31 +*/ +//////////////////////////////////////////////////////////////////////////////// + +// Adjustable parameters +unsigned long grid_x_size = 40; +unsigned long grid_y_size = 20; + +int use_curses = 1; + +//////////////////////////////////////////////////////////////////////////////// +#include +#include +#include +#include +#include +#include +#include + +#define CELL_MAX_NEIGHBORS 8 + +struct cell { + dispatch_queue_t q; + int alive; + char display; + + // tracks whether a update_cell() notification arrived while + // an update was already in progress + int needs_update; + int living_neighbors; + int queries_outstanding; + + struct cell* neighbors[CELL_MAX_NEIGHBORS]; + char* label; +} __attribute__((aligned(64))); + +//////////////////////////////////////////////////////////////////////////////// + +/*! @function init_grid + Initializes the grid data structure based on the global variables + grid_x_size and grid_y_size. Must be called before any calls to + cell_set_alive. */ +struct cell* init_grid(size_t grid_x_size, size_t grid_y_size); + +/*! @function init_display + Initializes the display subsystem. Starts a periodic timer to update the + display based on the current contents of the cell grid. + */ +void init_display(struct cell* grid); + +//////////////////////////////////////////////////////////////////////////////// + +// Macro to test whether x,y coordinates are within bounds of the grid +#define GRID_VALID(u,v) (((u) >= 0) && ((v) >= 0) && \ + ((u) < grid_x_size) && ((v) < grid_y_size)) +// Macro to translate from 2d grid coordinates to array offest +#define GRID_OFF(u,v) ((v) * grid_x_size + (u)) + +#if !defined(DISPATCH_LIFE_GL) +int main(int argc, char* argv[]) { + + struct ttysize tsz; + int res; + + res = ioctl(STDIN_FILENO, TIOCGWINSZ, &tsz); + if (res == 0) { + grid_x_size = tsz.ts_cols; + grid_y_size = tsz.ts_lines; + } + + int dispflag = 1; + int ch; + + while ((ch = getopt(argc, argv, "x:y:q")) != -1) { + char* endptr; + switch (ch) { + case 'x': + grid_x_size = strtol(optarg, &endptr, 10); + if (grid_x_size < 0 || (endptr && *endptr != 0)) { + fprintf(stderr, "life: invalid x size\n"); + exit(1); + } + break; + case 'y': + grid_y_size = strtol(optarg, &endptr, 10); + if (grid_y_size < 0 || (endptr && *endptr != 0)) { + fprintf(stderr, "life: invalid y size\n"); + exit(1); + } + break; + case 'q': + dispflag = 0; + break; + case '?': + default: + fprintf(stderr, "usage: life [-q] [-x size] [-y size]\n"); + fprintf(stderr, "\t-x: grid x size (default is terminal columns)\n"); + fprintf(stderr, "\t-y: grid y size (default is terminal rows)\n"); + fprintf(stderr, "\t-q: suppress display output\n"); + exit(1); + } + } + + struct cell* grid = init_grid(grid_x_size, grid_y_size); + + if (dispflag) { + init_display(grid); + if (use_curses) { + initscr(); cbreak(); noecho(); + nonl(); + intrflush(stdscr, FALSE); + keypad(stdscr, TRUE); + } + } + + dispatch_main(); + + if (dispflag && use_curses) { + endwin(); + } + + return 0; +} +#endif /* defined(DISPATCH_LIFE_GL) */ + +//////////////////////////////////////////////////////////////////////////////// + +static void cell_set_alive(struct cell*, int alive); + +/*! @function update_cell + GCD work function. Begins the update process for a cell by + sending cell_is_alive() messages with cell_is_alive_callback() + completion callbacks to all adjacent cells. If an update is already + in progress, simply sets the needs_update flag of the cell. */ +static void update_cell(struct cell*); + +/*! @function cell_is_alive_callback + GCD completion callback. Receives the result from cell_is_alive. When + all _cell_is_alive_callback() completion callbacks have been received + from an update, recalculates the internal state of the cell. If the + state changes, sends update_cell() to all adjacent cells. */ +static void update_cell_response(struct cell*, int); + +//////////////////////////////////////////////////////////////////////////////// + +void +foreach_neighbor(struct cell* self, void (^action)(struct cell* other)) { + int i; + for (i = 0; i < CELL_MAX_NEIGHBORS; ++i) { + struct cell* other = self->neighbors[i]; + if (other) { + action(other); + } + } +} + + +// Change cell state, update the screen, and update neighbors. +void +cell_set_alive(struct cell* self, int alive) { + if (alive == self->alive) return; // nothing to do + + dispatch_async(self->q, ^{ + self->alive = alive; + self->display = (self->alive) ? '#' : ' '; + + foreach_neighbor(self, ^(struct cell* other) { + dispatch_async(other->q, ^{ update_cell(other); }); + }); + }); +} + +void +update_cell(struct cell* self) { + if (self->queries_outstanding == 0) { + self->needs_update = 0; + self->living_neighbors = 0; + + foreach_neighbor(self, ^(struct cell* other) { + ++self->queries_outstanding; + dispatch_async(other->q, ^{ + dispatch_async(self->q, ^{ update_cell_response(self, other->alive); }); + }); + }); + + // '.' indicates the cell is not alive but needs an update + if (!self->alive) self->display = '.'; + } else { + self->needs_update = 1; + } +} + +void +update_cell_response(struct cell* self, int response) { + if (response) ++self->living_neighbors; + --self->queries_outstanding; + + // when all neighbors have replied with their state, + // recalculate our internal state + if (self->queries_outstanding == 0) { + const int living_neighbors = self->living_neighbors; + int alive = self->alive; + + // Conway's Game of Life + if (living_neighbors < 2 || living_neighbors > 3) { + alive = 0; + } else if (living_neighbors == 3) { + alive = 1; + } + + // Notify neighbors of state change + cell_set_alive(self, alive); + + // if a request for an update came in while we were + // already processing one, kick off the next update + if (self->needs_update) { + dispatch_async(self->q, ^{ update_cell(self); }); + } else { + // otherwise clear the '.' character that was + // displayed during the update + if (!self->alive) { + self->display = ' '; + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////// + +struct cell* +init_grid(size_t grid_x_size, size_t grid_y_size) { + struct cell* grid = calloc(sizeof(struct cell),grid_x_size*grid_y_size); + + int i,j; + for (i = 0; i < grid_x_size; ++i) { + for (j = 0; j < grid_y_size; ++j) { + struct cell* ptr = &grid[GRID_OFF(i,j)]; + + asprintf(&ptr->label, "x%dy%d", i, j); + + ptr->q = dispatch_queue_create(ptr->label, NULL); + dispatch_set_target_queue(ptr->q, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)); + dispatch_queue_set_context(ptr->q, ptr); + + ptr->neighbors[0] = GRID_VALID(i ,j-1) ? + &grid[GRID_OFF(i ,j-1)] : NULL; // N + ptr->neighbors[1] = GRID_VALID(i+1,j-1) ? + &grid[GRID_OFF(i+1,j-1)] : NULL; // NE + ptr->neighbors[2] = GRID_VALID(i+1,j ) ? + &grid[GRID_OFF(i+1,j )] : NULL; // E + ptr->neighbors[3] = GRID_VALID(i+1,j+1) ? + &grid[GRID_OFF(i+1,j+1)] : NULL; // SE + ptr->neighbors[4] = GRID_VALID(i ,j+1) ? + &grid[GRID_OFF(i ,j+1)] : NULL; // S + ptr->neighbors[5] = GRID_VALID(i-1,j+1) ? + &grid[GRID_OFF(i-1,j+1)] : NULL; // SW + ptr->neighbors[6] = GRID_VALID(i-1,j ) ? + &grid[GRID_OFF(i-1,j )] : NULL; // W + ptr->neighbors[7] = GRID_VALID(i-1,j-1) ? + &grid[GRID_OFF(i-1,j-1)] : NULL; // NW + } + } + + srandomdev(); + for (i = 0; i < grid_x_size; ++i) { + for (j = 0; j < grid_y_size; ++j) { + if (random() & 1) { + cell_set_alive(&grid[GRID_OFF(i,j)], 1); + } + } + } + + return grid; +} + +#if defined(DISPATCH_LIFE_GL) +char +get_grid_display_char(struct cell* grid, size_t x, size_t y) { + return grid[GRID_OFF(x,y)].display; +} +#endif /* defined(DISPATCH_LIFE_GL) */ + +#if !defined(DISPATCH_LIFE_GL) +void +init_display(struct cell* grid) +{ + dispatch_source_t timer; + + timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); + dispatch_source_set_timer(dispatch_time(DISPATCH_TIME_NOW, 0). 10000000, 1000); + dispatch_source_set_event_handler(^{ + int x,y; + x = 0; + for (x = 0; x < grid_x_size; ++x) { + for (y = 0; y < grid_y_size; ++y) { + mvaddnstr(y, x, &grid[GRID_OFF(x,y)].display, 1); + } + } + refresh(); + }); + dispatch_resume(timer); +} +#endif /* defined(DISPATCH_LIFE_GL) */ diff --git a/examples/DispatchLife/DispatchLife.xcodeproj/project.pbxproj b/examples/DispatchLife/DispatchLife.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6897278 --- /dev/null +++ b/examples/DispatchLife/DispatchLife.xcodeproj/project.pbxproj @@ -0,0 +1,252 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = 29B97318FDCFA39411CA2CEA /* MainMenu.nib */; }; + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; + 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; + FC0615200DF53162002BF852 /* DispatchLifeGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = FC06151F0DF53162002BF852 /* DispatchLifeGLView.m */; }; + FC0615450DF535BD002BF852 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC0615440DF535BD002BF852 /* OpenGL.framework */; }; + FC787BF60DF67AAF009415DA /* DispatchLife.c in Sources */ = {isa = PBXBuildFile; fileRef = FC787BF50DF67AAF009415DA /* DispatchLife.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 29B97319FDCFA39411CA2CEA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/MainMenu.nib; sourceTree = ""; }; + 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D1107320486CEB800E47090 /* DispatchLife.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DispatchLife.app; sourceTree = BUILT_PRODUCTS_DIR; }; + FC06151E0DF53162002BF852 /* DispatchLifeGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DispatchLifeGLView.h; sourceTree = ""; }; + FC06151F0DF53162002BF852 /* DispatchLifeGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DispatchLifeGLView.m; sourceTree = ""; }; + FC0615440DF535BD002BF852 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = ""; }; + FC787BF50DF67AAF009415DA /* DispatchLife.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DispatchLife.c; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D11072E0486CEB800E47090 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, + FC0615450DF535BD002BF852 /* OpenGL.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + FC06151E0DF53162002BF852 /* DispatchLifeGLView.h */, + FC06151F0DF53162002BF852 /* DispatchLifeGLView.m */, + ); + name = Classes; + sourceTree = ""; + }; + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, + FC0615440DF535BD002BF852 /* OpenGL.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 29B97324FDCFA39411CA2CEA /* AppKit.framework */, + 29B97325FDCFA39411CA2CEA /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D1107320486CEB800E47090 /* DispatchLife.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* DispatchLife */ = { + isa = PBXGroup; + children = ( + 080E96DDFE201D6D7F000001 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = DispatchLife; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + FC787BF50DF67AAF009415DA /* DispatchLife.c */, + 29B97316FDCFA39411CA2CEA /* main.m */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 8D1107310486CEB800E47090 /* Info.plist */, + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, + 29B97318FDCFA39411CA2CEA /* MainMenu.nib */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D1107260486CEB800E47090 /* DispatchLife */ = { + isa = PBXNativeTarget; + buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "DispatchLife" */; + buildPhases = ( + 8D1107290486CEB800E47090 /* Resources */, + 8D11072C0486CEB800E47090 /* Sources */, + 8D11072E0486CEB800E47090 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DispatchLife; + productInstallPath = "$(HOME)/Applications"; + productName = DispatchLife; + productReference = 8D1107320486CEB800E47090 /* DispatchLife.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "DispatchLife" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 29B97314FDCFA39411CA2CEA /* DispatchLife */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D1107260486CEB800E47090 /* DispatchLife */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D1107290486CEB800E47090 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072A0486CEB800E47090 /* MainMenu.nib in Resources */, + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D11072C0486CEB800E47090 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072D0486CEB800E47090 /* main.m in Sources */, + FC0615200DF53162002BF852 /* DispatchLifeGLView.m in Sources */, + FC787BF60DF67AAF009415DA /* DispatchLife.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C165DFE840E0CC02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 29B97318FDCFA39411CA2CEA /* MainMenu.nib */ = { + isa = PBXVariantGroup; + children = ( + 29B97319FDCFA39411CA2CEA /* English */, + ); + name = MainMenu.nib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C01FCF4C08A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INFOPLIST_FILE = Info.plist; + PRODUCT_NAME = DispatchLife; + }; + name = Release; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + OTHER_CFLAGS = ( + "-DDISPATCH_LIFE_GL", + "-fblocks", + ); + PREBINDING = NO; + SDKROOT = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "DispatchLife" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4C08A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "DispatchLife" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/examples/DispatchLife/DispatchLifeGLView.h b/examples/DispatchLife/DispatchLifeGLView.h new file mode 100644 index 0000000..7ed6bbd --- /dev/null +++ b/examples/DispatchLife/DispatchLifeGLView.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2009-2009 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ + +#import + +struct cell; + +// From DispatchLife.c +extern struct cell* init_grid(size_t grid_x_size, size_t grid_y_size); +extern char get_grid_display_char(struct cell* grid, size_t x, size_t y); + +@interface DispatchLifeGLView : NSOpenGLView { + struct cell* grid; + uint32_t* image; +} + +- (void)adjustGLViewBounds; + +@end diff --git a/examples/DispatchLife/DispatchLifeGLView.m b/examples/DispatchLife/DispatchLifeGLView.m new file mode 100644 index 0000000..5aa843b --- /dev/null +++ b/examples/DispatchLife/DispatchLifeGLView.m @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2009-2009 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ + +#import "DispatchLifeGLView.h" + +#import + +#include +#include +#include + +#include +#include + +extern size_t grid_x_size; +extern size_t grid_y_size; + +@implementation DispatchLifeGLView + +#define CELL_WIDTH 8 +#define CELL_HEIGHT 8 + +- (void)goFullScreen:(NSOpenGLView*)view { + NSOpenGLPixelFormatAttribute attrs[] = + { + NSOpenGLPFAFullScreen, + + NSOpenGLPFAScreenMask, CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay), + + NSOpenGLPFAAccelerated, + NSOpenGLPFANoRecovery, + NSOpenGLPFADoubleBuffer, + 0 + }; + NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + + NSOpenGLContext* screen = [[NSOpenGLContext alloc] initWithFormat:pixFmt shareContext:[view openGLContext]]; + + CGDisplayErr err = CGCaptureAllDisplays(); + if (err != CGDisplayNoErr) { + [screen release]; + return; + } + + [screen setFullScreen]; + [screen makeCurrentContext]; + + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + [screen flushBuffer]; + glClear(GL_COLOR_BUFFER_BIT); + [screen flushBuffer]; +} + + +- (id)initWithFrame:(NSRect)frame { + NSOpenGLPixelFormatAttribute attrs[] = + { + NSOpenGLPFAAccelerated, + NSOpenGLPFANoRecovery, + NSOpenGLPFADoubleBuffer, + 0 + }; + NSOpenGLPixelFormat* pixFmt = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + + self = [super initWithFrame:frame pixelFormat:pixFmt]; + if (self) { + + [[self openGLContext] makeCurrentContext]; + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); + glClearColor(1.0, 1.0, 1.0, 1.0); + glColor4f(1.0, 1.0, 1.0, 1.0); + glEnable(GL_RASTER_POSITION_UNCLIPPED_IBM); + glDisable(GL_DITHER); + + grid_x_size = 128; + grid_y_size = 96; + + self->grid = init_grid(grid_x_size, grid_y_size); + size_t image_size = grid_x_size * grid_y_size * sizeof(uint32_t); + self->image = malloc(image_size); + memset(self->image, 0xFF, image_size); + + [self adjustGLViewBounds]; + + [[NSTimer scheduledTimerWithTimeInterval: (1.0f / 15.0) target: self selector:@selector(drawRect:) userInfo:self repeats:true] retain]; + + } + return self; +} + +- (void)drawRect:(NSRect)rect { + [[self openGLContext] makeCurrentContext]; + + glClear(GL_COLOR_BUFFER_BIT); + + NSRect bounds = [self bounds]; + glRasterPos2f(-bounds.size.width/2, -bounds.size.height/2); + glPixelZoom(bounds.size.width/grid_x_size, bounds.size.height/grid_y_size); + + const int width = grid_x_size; + const int height = grid_y_size; + + int x, y; + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + int i = y * width + x; + switch (get_grid_display_char(grid, x, y)) { + case '.': + image[i] = 0xCCCCCCFF; + break; + case '#': + image[i] = 0x000000FF; + break; + case ' ': + image[i] = 0xFFFFFFFF; + break; + default: + image[i] = 0x0000FFFF; + break; + } + } + } + + glDrawPixels(width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, image); + + glFinish(); + + [[self openGLContext] flushBuffer]; + +} + +- (void)adjustGLViewBounds +{ + [[self openGLContext] makeCurrentContext]; + [[self openGLContext] update]; + + NSRect rect = [self bounds]; + + glViewport(0, 0, (GLint) rect.size.width, (GLint) rect.size.height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluOrtho2D(-(rect.size.width/2), rect.size.width/2, -(rect.size.height/2), rect.size.height/2); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + [self setNeedsDisplay:true]; +} + +- (void)update // moved or resized +{ + [super update]; + [self adjustGLViewBounds]; +} + +- (void)reshape // scrolled, moved or resized +{ + [super reshape]; + [self adjustGLViewBounds]; +} + +@end diff --git a/examples/DispatchLife/English.lproj/InfoPlist.strings b/examples/DispatchLife/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..5e45963c382ba690b781b953a00585212b898ac5 GIT binary patch literal 92 zcmW-XQ3`+{5C!MkQ~2$No+IcIkqMDxWCV8j>LCj|yTg2Mz+o9F%uHlf9u}h9EuK`F a!Y*1dX%G66ZqL#C$|bw0ZoP5@jOGW1ArT7z literal 0 HcmV?d00001 diff --git a/examples/DispatchLife/English.lproj/MainMenu.nib/designable.nib b/examples/DispatchLife/English.lproj/MainMenu.nib/designable.nib new file mode 100644 index 0000000..0cdc4e1 --- /dev/null +++ b/examples/DispatchLife/English.lproj/MainMenu.nib/designable.nib @@ -0,0 +1,2651 @@ + + + + 0 + 10A219 + 708 + 994.4 + 404.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 708 + + + YES + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + YES + + + YES + + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + AMainMenu + + YES + + + DispatchLife + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + DispatchLife + + YES + + + About DispatchLife + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + UHJlZmVyZW5jZXPigKY + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + Services + + YES + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide DispatchLife + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit DispatchLife + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 1048576 + 2147483647 + + + submenuAction: + + File + + YES + + + New + n + 1048576 + 2147483647 + + + + + + T3BlbuKApg + o + 1048576 + 2147483647 + + + + + + Open Recent + + 1048576 + 2147483647 + + + submenuAction: + + Open Recent + + YES + + + Clear Menu + + 1048576 + 2147483647 + + + + + _NSRecentDocumentsMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Close + w + 1048576 + 2147483647 + + + + + + Save + s + 1048576 + 2147483647 + + + + + + U2F2ZSBBc+KApg + S + 1179648 + 2147483647 + + + + + + Revert to Saved + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Page Setup... + P + 1179648 + 2147483647 + + + + + + + UHJpbnTigKY + p + 1048576 + 2147483647 + + + + + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + Edit + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1179648 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Find + + 1048576 + 2147483647 + + + submenuAction: + + Find + + YES + + + RmluZOKApg + f + 1048576 + 2147483647 + + + 1 + + + + Find Next + g + 1048576 + 2147483647 + + + 2 + + + + Find Previous + G + 1179648 + 2147483647 + + + 3 + + + + Use Selection for Find + e + 1048576 + 2147483647 + + + 7 + + + + Jump to Selection + j + 1048576 + 2147483647 + + + + + + + + + Spelling and Grammar + + 1048576 + 2147483647 + + + submenuAction: + + Spelling and Grammar + + YES + + + U2hvdyBTcGVsbGluZ+KApg + : + 1048576 + 2147483647 + + + + + + Check Spelling + ; + 1048576 + 2147483647 + + + + + + Check Spelling While Typing + + 1048576 + 2147483647 + + + + + + Check Grammar With Spelling + + 1048576 + 2147483647 + + + + + + + + + Substitutions + + 1048576 + 2147483647 + + + submenuAction: + + Substitutions + + YES + + + Smart Copy/Paste + f + 1048576 + 2147483647 + + + 1 + + + + Smart Quotes + g + 1048576 + 2147483647 + + + 2 + + + + Smart Links + G + 1179648 + 2147483647 + + + 3 + + + + + + + Speech + + 1048576 + 2147483647 + + + submenuAction: + + Speech + + YES + + + Start Speaking + + 1048576 + 2147483647 + + + + + + Stop Speaking + + 1048576 + 2147483647 + + + + + + + + + + + + Format + + 1048576 + 2147483647 + + + submenuAction: + + Format + + YES + + + Show Fonts + t + 1048576 + 2147483647 + + + + + + Show Colors + C + 1179648 + 2147483647 + + + + + + + + + View + + 1048576 + 2147483647 + + + submenuAction: + + View + + YES + + + Show Toolbar + t + 1572864 + 2147483647 + + + + + + Q3VzdG9taXplIFRvb2xiYXLigKY + + 1048576 + 2147483647 + + + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + Window + + YES + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 1048576 + 2147483647 + + + submenuAction: + + Help + + YES + + + DispatchLife Help + ? + 1048576 + 2147483647 + + + + + + + + _NSMainMenu + + + 15 + 2 + {{384, 348}, {512, 384}} + 1954021376 + DispatchLife + NSWindow + + {3.40282e+38, 3.40282e+38} + + + 256 + + YES + + + 4415 + {512, 384} + + + DispatchLifeGLView + + + {512, 384} + + + + {{0, 0}, {1280, 1002}} + {3.40282e+38, 3.40282e+38} + + + + + YES + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + print: + + + + 86 + + + + runPageLayout: + + + + 87 + + + + clearRecentDocuments: + + + + 127 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + performClose: + + + + 193 + + + + toggleContinuousSpellChecking: + + + + 222 + + + + undo: + + + + 223 + + + + copy: + + + + 224 + + + + checkSpelling: + + + + 225 + + + + paste: + + + + 226 + + + + stopSpeaking: + + + + 227 + + + + cut: + + + + 228 + + + + showGuessPanel: + + + + 230 + + + + redo: + + + + 231 + + + + selectAll: + + + + 232 + + + + startSpeaking: + + + + 233 + + + + delete: + + + + 235 + + + + performZoom: + + + + 240 + + + + performFindPanelAction: + + + + 241 + + + + centerSelectionInVisibleArea: + + + + 245 + + + + toggleGrammarChecking: + + + + 347 + + + + toggleSmartInsertDelete: + + + + 355 + + + + toggleAutomaticQuoteSubstitution: + + + + 356 + + + + toggleAutomaticLinkDetection: + + + + 357 + + + + showHelp: + + + + 360 + + + + orderFrontColorPanel: + + + + 361 + + + + saveDocument: + + + + 362 + + + + saveDocumentAs: + + + + 363 + + + + revertDocumentToSaved: + + + + 364 + + + + runToolbarCustomizationPalette: + + + + 365 + + + + toggleToolbarShown: + + + + 366 + + + + hide: + + + + 367 + + + + hideOtherApplications: + + + + 368 + + + + terminate: + + + + 369 + + + + unhideAllApplications: + + + + 370 + + + + newDocument: + + + + 373 + + + + openDocument: + + + + 374 + + + + + YES + + 0 + + YES + + + + + + -2 + + + RmlsZSdzIE93bmVyA + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + YES + + + + + + + + + + MainMenu + + + 19 + + + YES + + + + + + 56 + + + YES + + + + + + 103 + + + YES + + + + 1 + + + 217 + + + YES + + + + + + 83 + + + YES + + + + + + 81 + + + YES + + + + + + + + + + + + + + + + 75 + + + 3 + + + 80 + + + 8 + + + 78 + + + 6 + + + 72 + + + + + 82 + + + 9 + + + 124 + + + YES + + + + + + 77 + + + 5 + + + 73 + + + 1 + + + 79 + + + 7 + + + 112 + + + 10 + + + 74 + + + 2 + + + 125 + + + YES + + + + + + 126 + + + + + 205 + + + YES + + + + + + + + + + + + + + + + + + 202 + + + + + 198 + + + + + 207 + + + + + 214 + + + + + 199 + + + + + 203 + + + + + 197 + + + + + 206 + + + + + 215 + + + + + 218 + + + YES + + + + + + 216 + + + YES + + + + + + 200 + + + YES + + + + + + + + + 219 + + + + + 201 + + + + + 204 + + + + + 220 + + + YES + + + + + + + + + + 213 + + + + + 210 + + + + + 221 + + + + + 208 + + + + + 209 + + + + + 106 + + + YES + + + + 2 + + + 111 + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + 1111 + + + 144 + + + + + 129 + + + 121 + + + 143 + + + + + 236 + + + + + 131 + + + YES + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + YES + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 295 + + + YES + + + + + + 296 + + + YES + + + + + + + 297 + + + + + 298 + + + + + 299 + + + YES + + + + + + 300 + + + YES + + + + + + + 344 + + + + + 345 + + + + + 211 + + + YES + + + + + + 212 + + + YES + + + + + + + 195 + + + + + 196 + + + + + 346 + + + + + 348 + + + YES + + + + + + 349 + + + YES + + + + + + + + 350 + + + + + 351 + + + + + 354 + + + + + 371 + + + YES + + + + + + 372 + + + YES + + + + + + 377 + + + + + + + YES + + YES + 103.IBPluginDependency + 103.ImportedFromIB2 + 106.IBEditorWindowLastContentRect + 106.IBPluginDependency + 106.ImportedFromIB2 + 106.editorWindowContentRectSynchronizationRect + 111.IBPluginDependency + 111.ImportedFromIB2 + 112.IBPluginDependency + 112.ImportedFromIB2 + 124.IBPluginDependency + 124.ImportedFromIB2 + 125.IBPluginDependency + 125.ImportedFromIB2 + 125.editorWindowContentRectSynchronizationRect + 126.IBPluginDependency + 126.ImportedFromIB2 + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBPluginDependency + 130.ImportedFromIB2 + 130.editorWindowContentRectSynchronizationRect + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 19.IBPluginDependency + 19.ImportedFromIB2 + 195.IBPluginDependency + 195.ImportedFromIB2 + 196.IBPluginDependency + 196.ImportedFromIB2 + 197.IBPluginDependency + 197.ImportedFromIB2 + 198.IBPluginDependency + 198.ImportedFromIB2 + 199.IBPluginDependency + 199.ImportedFromIB2 + 200.IBPluginDependency + 200.ImportedFromIB2 + 200.editorWindowContentRectSynchronizationRect + 201.IBPluginDependency + 201.ImportedFromIB2 + 202.IBPluginDependency + 202.ImportedFromIB2 + 203.IBPluginDependency + 203.ImportedFromIB2 + 204.IBPluginDependency + 204.ImportedFromIB2 + 205.IBEditorWindowLastContentRect + 205.IBPluginDependency + 205.ImportedFromIB2 + 205.editorWindowContentRectSynchronizationRect + 206.IBPluginDependency + 206.ImportedFromIB2 + 207.IBPluginDependency + 207.ImportedFromIB2 + 208.IBPluginDependency + 208.ImportedFromIB2 + 209.IBPluginDependency + 209.ImportedFromIB2 + 210.IBPluginDependency + 210.ImportedFromIB2 + 211.IBPluginDependency + 211.ImportedFromIB2 + 212.IBPluginDependency + 212.ImportedFromIB2 + 212.editorWindowContentRectSynchronizationRect + 213.IBPluginDependency + 213.ImportedFromIB2 + 214.IBPluginDependency + 214.ImportedFromIB2 + 215.IBPluginDependency + 215.ImportedFromIB2 + 216.IBPluginDependency + 216.ImportedFromIB2 + 217.IBPluginDependency + 217.ImportedFromIB2 + 218.IBPluginDependency + 218.ImportedFromIB2 + 219.IBPluginDependency + 219.ImportedFromIB2 + 220.IBPluginDependency + 220.ImportedFromIB2 + 220.editorWindowContentRectSynchronizationRect + 221.IBPluginDependency + 221.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 236.IBPluginDependency + 236.ImportedFromIB2 + 239.IBPluginDependency + 239.ImportedFromIB2 + 24.IBEditorWindowLastContentRect + 24.IBPluginDependency + 24.ImportedFromIB2 + 24.editorWindowContentRectSynchronizationRect + 29.IBEditorWindowLastContentRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 29.WindowOrigin + 29.editorWindowContentRectSynchronizationRect + 295.IBPluginDependency + 296.IBEditorWindowLastContentRect + 296.IBPluginDependency + 296.editorWindowContentRectSynchronizationRect + 297.IBPluginDependency + 298.IBPluginDependency + 299.IBPluginDependency + 300.IBEditorWindowLastContentRect + 300.IBPluginDependency + 300.editorWindowContentRectSynchronizationRect + 344.IBPluginDependency + 345.IBPluginDependency + 346.IBPluginDependency + 346.ImportedFromIB2 + 348.IBPluginDependency + 348.ImportedFromIB2 + 349.IBPluginDependency + 349.ImportedFromIB2 + 349.editorWindowContentRectSynchronizationRect + 350.IBPluginDependency + 350.ImportedFromIB2 + 351.IBPluginDependency + 351.ImportedFromIB2 + 354.IBPluginDependency + 354.ImportedFromIB2 + 371.IBEditorWindowLastContentRect + 371.IBPluginDependency + 371.IBWindowTemplateEditedContentRect + 371.NSWindowTemplate.visibleAtLaunch + 371.editorWindowContentRectSynchronizationRect + 371.windowTemplate.maxSize + 372.IBPluginDependency + 377.IBPluginDependency + 377.IBViewIntegration.shadowBlurRadius + 377.IBViewIntegration.shadowColor + 377.IBViewIntegration.shadowOffsetHeight + 377.IBViewIntegration.shadowOffsetWidth + 5.IBPluginDependency + 5.ImportedFromIB2 + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBEditorWindowLastContentRect + 57.IBPluginDependency + 57.ImportedFromIB2 + 57.editorWindowContentRectSynchronizationRect + 58.IBPluginDependency + 58.ImportedFromIB2 + 72.IBPluginDependency + 72.ImportedFromIB2 + 73.IBPluginDependency + 73.ImportedFromIB2 + 74.IBPluginDependency + 74.ImportedFromIB2 + 75.IBPluginDependency + 75.ImportedFromIB2 + 77.IBPluginDependency + 77.ImportedFromIB2 + 78.IBPluginDependency + 78.ImportedFromIB2 + 79.IBPluginDependency + 79.ImportedFromIB2 + 80.IBPluginDependency + 80.ImportedFromIB2 + 81.IBEditorWindowLastContentRect + 81.IBPluginDependency + 81.ImportedFromIB2 + 81.editorWindowContentRectSynchronizationRect + 82.IBPluginDependency + 82.ImportedFromIB2 + 83.IBPluginDependency + 83.ImportedFromIB2 + 92.IBPluginDependency + 92.ImportedFromIB2 + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + {{394, 713}, {191, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{596, 852}, {216, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{522, 812}, {146, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{436, 809}, {64, 6}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {275, 83}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{163, 493}, {240, 243}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{187, 434}, {243, 243}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {167, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {241, 103}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{323, 663}, {194, 73}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{525, 802}, {197, 73}} + {{0, 736}, {455, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + {74, 862} + {{6, 978}, {478, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + {{273, 693}, {231, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + {{475, 832}, {234, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{207, 693}, {173, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + {{231, 634}, {176, 43}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{608, 612}, {215, 63}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{314, 134}, {512, 384}} + com.apple.InterfaceBuilder.CocoaPlugin + {{314, 134}, {512, 384}} + + {{33, 99}, {480, 360}} + {3.40282e+38, 3.40282e+38} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + 3 + MAA + + + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{12, 553}, {220, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{23, 794}, {245, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{121, 533}, {196, 203}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{145, 474}, {199, 203}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + + + + YES + + YES + + + YES + + + + + YES + + YES + + + YES + + + + 377 + + + + YES + + DispatchLifeGLView + NSOpenGLView + + IBProjectSource + DispatchLifeGLView.h + + + + + YES + + NSApplication + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSApplication.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSApplicationScripting.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSColorPanel.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSHelpManager.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSPageLayout.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSUserInterfaceItemSearching.h + + + + NSBrowser + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSBrowser.h + + + + NSControl + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSControl.h + + + + NSDocument + NSObject + + YES + + YES + printDocument: + revertDocumentToSaved: + runPageLayout: + saveDocument: + saveDocumentAs: + saveDocumentTo: + + + YES + id + id + id + id + id + id + + + + IBFrameworkSource + AppKit.framework/Headers/NSDocument.h + + + + NSDocument + + IBFrameworkSource + AppKit.framework/Headers/NSDocumentScripting.h + + + + NSDocumentController + NSObject + + YES + + YES + clearRecentDocuments: + newDocument: + openDocument: + saveAllDocuments: + + + YES + id + id + id + id + + + + IBFrameworkSource + AppKit.framework/Headers/NSDocumentController.h + + + + NSFormatter + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFormatter.h + + + + NSMatrix + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSMatrix.h + + + + NSMenu + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenu.h + + + + NSMenuItem + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenuItem.h + + + + NSMovieView + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSMovieView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSAccessibility.h + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDictionaryController.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDragging.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontManager.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontPanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSKeyValueBinding.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSNibLoading.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSOutlineView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSPasteboard.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSSavePanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSTableView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSToolbarItem.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSView.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSError.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFileManager.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueObserving.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyedArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObject.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObjectScripting.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSPortCoder.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSRunLoop.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptObjectSpecifiers.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptWhoseTests.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSThread.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURL.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLConnection.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLDownload.h + + + + NSOpenGLView + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSOpenGLView.h + + + + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSInterfaceStyle.h + + + + NSResponder + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSResponder.h + + + + NSTableView + NSControl + + + + NSText + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSText.h + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSClipView.h + + + + NSView + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSRulerView.h + + + + NSView + NSResponder + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSDrawer.h + + + + NSWindow + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSWindow.h + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSWindowScripting.h + + + + + 0 + ../DispatchLifeGL.xcodeproj + 3 + + diff --git a/examples/DispatchLife/English.lproj/MainMenu.nib/keyedobjects.nib b/examples/DispatchLife/English.lproj/MainMenu.nib/keyedobjects.nib new file mode 100644 index 0000000000000000000000000000000000000000..05cdecfbf52574b74d6f15bb75a4b6cfa9159b00 GIT binary patch literal 19575 zcmb7r2YgfI*Z=d}o7{Vwj@+#7CTY`|Cf!Yw6qLPJX(?sajzC+w>;THVSN28(1OZvf z5(E(tkRKvj5Rkn$$QBh*hQR;aqzx(U@BP1T{b24q-*e7$p0l3k=9U*1R#wHuokAE9 zh(sbJW@?yW%|1Hu@FaN57$e&@=QLy}$@F*b{qUDOTV>ti{1N1nY1Fj>GXd0Vm=V+zdCz zEpSWR7I(+#I0N^Kqh!}-{T3vmUm#8tQ&*Wi!v7(5n_!;|nVJQsh8=i&Ky5&jad zz$@`rcoW`+x8v{e5BMPd2_M3T@iBZ3pT`&QMSK%ZL@kH^gRQ3-KMXmDoY-BlZ&qh=at> z#A)ITah5noTq150zY=$d2gF~*Q{r#p1xb=L$&hZOg!CePNMF*A3?joxJsC|JNFy0f zTFF$h3E7NnPj)7|kX^}ivM>`!Ksd1MJ$N|ur3k!DrzHYD{3d|D(WrjBkC*46_toeMP;HYQMIT>G)y#BG($8~ z^s#8MXpQJw(I(Mm(Js+p(a)kIq7$OiqBElFqKBeKqGzJ#q8DODtPm^3DzTr~UmPZm z6Q_!sh?|OAh&zfqiF=B(#J$A*#f9P`@gQ-9c%*ogc#?Rsc#3$sc!v06@m;DJHIJH4 zeMT*y7E+6-&#A@K5^5>6j9N~8L48TBpjJ|=sMXXO>MLq3wT}9lT2F1DzM(c!-%^{X z&D0j^J8CPnjoMCqPwk-oL+zw~pmtF|QoE@=)Lv>IwVygb9i)Du4pE1xpQ$6%QR*1= z3w4}2L7k*dQKzXh)LH5rb)LFFU8F8im#Hh%Rq7gbow`BYq;65Ssb8r()LrTxb)R}b zJ)|B{zf(`>3i=QFPx=Y{7yXp}oBoG>Mn9)t&@btK=~wh?`VE5^%n%I8h!`A#Iwq6}W5SsT zCX$I_^h`8kV2n%*6U)Rg@k|1f$RshzjEPBM%#4MxGO0`xrYZ9t^FGszY0k7@S~9Je z)=V3wEz^!^&valqGM$*tOc$mr(~U`Ex-;oa2GfJdWO_1LOfRN4(}(HH^ke!n*~|w_ z4wK8|G5L&*8Ndu=3YbEsh#ABbGbKzpQ^^eO5n5VZTzr+4BL=x4335js$P;-XDPoZf z$&msnkqUVuALNVtkUt7QfhY*6kp^i|FbY9B6pF%7IEp}#C<^IObe}HiU2;lnl^tz0 zmHoP;w=B-7taO~goi-J4Ye-qeplsH!OM3IXJX>XDVQyh@VO34n!u-n2oZ@O*W$!NO z!ihj2o0YIJ;n zhE@UfY?keko>>Us#kQV>rTJw;1ZM4wJ8^1ph4=e9SaRIT49ydSvh!9ep5sPCp=y~c ztElAhdpw@QiFv|@$Di`JDTlZKp77=f86M72Jbs`1mB;gV!k5Q$c|vspG;TyOC>FG4 z2N8JVL>vihHZO-Dk(VRLd;%q)M3jV*vm8u;_8bSL@c1&6j7J4)Cy~rT+YrF* znbD>=XJ92LQ@CKqVWpfe=f+7mh9x*3&Ykm+R}Dpo^W#u9>pOgSqB+T+O-wS6G-!t> z$0xwCIcem`zO4!?%X6yo3OW@Iuz@liyk)cg!xLkY;u6dWw&+ANJp1-!IVcz9p?qXRIiTwF>fFJ=l*6C|`|NJ3EH5j~w^abU>DA@73gI?j4X%^H zK4O9%oeXeZoF}J}VZ?cJO3u?Uk_D(}DJtZooZ>71*RhZdc2x|+l!nSsIj~Z}v786z zA(Oq&$$^Nh_giVRS-GpYZ956dbC}#{pTwRp|Q?GeNMNUmmuxR1e1=#ThhlVb9AY`+u;ltw$+Bjh_;uFkpn37^1 zIr4whyMxGBG;SFhhfMYvUu$%>lJer5Dx2eh325SSG!ad*%Tdc$x~-}Q=-yW4P~;R; zw4RMXQ_(au9nC;9(Z^^O`UK5JbI@G$DVm4oqtDO+v=A*qpQFWS30jJlq2=fc^d(w> zR-#pCHCltdLTk}F^fg+KHlT0NMlO)kbFo|!XXW1KT5;{UE?hd7#r5ZMxq)0U_aRr! z)o`P@kGP54G;S6*k6XwsS5RcFM`M6@z`GmsMBf*|K*o=TciqS!rQjr?NczR0c2D`orqN!JW(Ug{cex_Nfa~t7~cd67a=f$aT+v zJ#;P{YRk7@=hX7iZ{EC-Lp(v8h70DjTo@N|3V1jS_&=i~=qNgdegR~`!-3fd479JZ zm4M4k;IWB&&f_F7#1<7W`L)f9i?vl{+BOw1+q<1ar+|bJoknNSS@3gUaCTwsu2RQ^ zav{K`oe8@e5eT;|u;mRZ$*CCR82gLp(lT_Z);JtCTt}pi_!WUT7cNkL+?jBacge@B0yKVcaE5{9v!&JCb*qoDJw&V{9gCEzvEO0ro6 zxRn8A6(z#8oT};yxNJRzUe+3s4!r`nH(&>?Y?W1orS@3E5nLj`$DOrXk<*4S#snto zEQpJ@4>y<4V5G4dmY`$UgU9YX4iJXb6Na^UZdrAe*44{ovq5DQ;QZ`b10|Q{=Tzj| zAM2J=YAd$ZDg?7=E0%K>;3&C)La-96us8NW`>~&0Ato0iOaa!+f$X{)ll>JkVoPUKKP4(HwjR!b`wr7rP5{tF06+&|j%SpW6=xKdb9HKz!(46yU~L63 zPTQ{=H~=lU%j)u&n3%L~{VS?Vg-e}sYG8Doa%BQ=7S|boJ2WO&AKVxB1K8{aa&-i# zP6E^bNH8i;Au2^xFg)d%<=_ zRzcE=9t>TZLU zM3vyC1uKM9+q=SkrB23-GW&HW)+)eS!wmqeyoRE##q03bc)b%Z-$hhg9jG&!;LUgo zh`W`?!8{HXB-ZtAS!~Ox&Z@a>LTRnf4R?wpWt~tg18BmUL<$zMs7|L;c0-pqw(+yBc z0i~=KC9S%!zSm6quy>WMqC{}jZ=o&$)D^A@peh=Jx{hz)n+*i=1q-dL6R3|}oUT;` z&~k!-6a=xKxRfAo^#>seKjMZ0+~CIGe#d`cAw=0TjoLWj(BF`{fy_z=LllBMU{#k2 zXmH#p)(gP;mm3LK!y00}#%~BjU?7p?hj!NW=I|7{*}UM-I)_75OCDo z1i%^70Ef^L!9)n5L;Hy^yP=GA5pf*gjCXMXL==b^&E4aka}P3FmX+6}<@LyeA16{A zASG~90clcWNXdkWNC6~E1Ek4-G(|w_oo*|JbVV4tw4C0R_7gaEA~XYp7Tm{xFugH^ z)r@pKw4`5JRxVSJ9kQy58wm11pq$3A-I+(B#MYZ zPAhO7ozDQ=?kY1|ftsppJ@f59PQsNyxSCr6gcmhNcqmar3xMd<_8W z1ppSju00(jHi9v1;vR!B{9%JOhag@)h&KS*OSS1*8|WC>1#E6_JURK<2K;=_Z3cce zHWYIev6J|L*ah6|wu|Y?#kau4CV>kGARVep%I(3^K8c{mlU4*Nu@JFr6^>a3*V>oX zHm{mj*m9isKLP$>Zad(A*AQPz93_qszX1M;dTJIDpsj$v&93I|Hc)f#9u@H8Bzqpn zUgUNH*&PkZW)N41tHd>@sk_dz{{Y$AtRSHNw9@u0C@i*VGiu6Vj%Bmzsv$mD(N@xv`x$sR)CdnGOUg*OQ@%PLgpBfVoqT1` z$ZZKLJi~6vl>XzYk6?gy@kWD+mX>A3Wfhfa zEg>;`Tk_d2)^i_Z3@{PLodG6JHDH3sAQQsZO_80 z0;iLQY{M}swRhh&JhCY;@IH4D7&zC6qamA+Lu;BHth%%PD6pnM|S6~0Y8@;=cfmmNkZ%+ z?XeGasmIS1;OD9^GJPG$Y1L&_w#qb^hQSWp=GD+H25ZQ(-+}fdv;eBAg?6)(fe(Oz zT<$h7aHDYsY~%oPATUrU7zSDFWI!;Do4~*=fdQ86<6xmvVdohMgB;r%dULb6quig|T^>j9(6_k3#c^vnHIMzdv0OekhR4xdDvuQ$&0}va zo}13&K<*RnHIJ>_B3CdAP+)ttZf>SKY_LnlRrZa8dW#z$dk||084>BNi{Sb z9dVJn>oN<*LjGAB4Y>Qxf{i>)o`KnLj>im-Ey9d&%V?cnSk=GML8~^vItKm{Dq2U( zAr6z9h!%vJs3r!$P;P_4Tn@v!9n|q1h&G)Y!z}{QCUK8IZ7&235}cw|4Cs1-8015t z_enk?wB%#*cc@1WfT7wOXXIbdyKG+xC9=?+&WHY2`^t1%c}_(RbmBPIEKUo7E!a&k zVz>WB`vv*3KJEVxf%-Zov?8&Hf*fXJosLDG^$4tk6s1m~zh?+3Nn=VA$xzXk#FsEb zR*_YZoU8>K{0r=HBd2I&;u5Pw{-OX;po`~lO;euKd5&HJoK{$?Xq-r>C`=UYGW+X@ z2($lb-R$pYC(;d?1BDRr8z&SaiWSA7{h|cHjfj%#rEdQKp=WkCLW2YEQ7Lqwgdzm| zlonc>T7i6;&E9quSwz-lA}cc0HnEL9dS3M@qL*S@o zg2$+zt9Pz!6uciQS`IF(JNWB!FxPMVci?=H4ZntZxK8@wdcfcw$hXL%OpxA|SI{VA zIVxHPe)L~(O~W`vhR|8E&tTDDSdjvk*o#y2YTh}guvF;EIPMHbMJvfQIt_A;F0FHUE;K)my?A7-(7>kyQmc8Q(T>D2Z z&ZI$p123-?trL9>lVF2A$#Qv-$1DepE}1Y1itVjY8yW!lUIqLsg%w5{5?YvsMjaH; z{Z6#?f5%j#(*+3Z7VRM?ikz!QqC>7-9f)H*R=(A`Dl1)B$%G~UcQoz{2BLkUW9XRZ zIK0MVUm+OyIE>>YDq6xRpuEf$7Udj71k?F%f}Inc|33-VV7P9IZvF2F^aYND{*=vy zo%$0RZ1b_`cj&4IIc@WwdbVj2Rvw%)S`D7O*!GTw99nn@Hi?H~Ib;_ba5y&M0=$si zjoUy^J%fX=4Z%B%OND6PmCJ@84l%uh(;(~fgq326*d4FMo9lQI%bmS_p`OTx2I^a$ zLi%)tWe}~SZQ2-XVsBwyuYr^V2FV*#@=k}bsVu!4GEkit6G`goj}=~!ZK z6s{P;AbLdHCx1lSAcfrp$tL8gb-48{1ML=9iL2ikSl5!?#WMPX#W`RrT`aA^IK$=} zJQ;uDlw)E&IeH6ABu=H7-w~h>K)elD_N_VMX`G11sgNVOG^p*_hIl3#xlGvTV4t_* zS&n+81}lkGumcda4xeL-!HTVe(_r{ZJ;H(C65iQa|o~?xh zcP2aqp%JLOgkDDm)Hhm`iZOl-HA)&#se$J*$SF|$2(=9s=)<29p4AHP2q$p#8s1b2 z6oF=QK!am)yYLiNLRMdcCv$h9f>9O$NYskDIGL1 z?dVb!g8rc{guG!H?6A?fR)$bNX4Te)P!~c+ZJDz!-1kmh2s(zkP*9(vFcci}+tN@N ziFb;_u7blq2D!bXIvfTCp->-w=co@iLa|rjC=0n4!f=@&S2`_Tq=*sr)<;!Y9LiW6;egiAgY)up-QPT zs+{_es-P;VDyo_qObwxiQZ>{tYB)858cB_!MpGQcQy)=dsIk;IYCJW8nn+EeCR0$AfrW%;ORsm-4ub$K^c!kjE7~uHZJRZhl(EJD7~p^@JnT7@=f6>4Dz=l{^&M^V{qh~Qqe z!sZ-pWaIT#R9XVG+U$=-HdY=15g!*KE?vN=K90A9U=@{B=WAhyh|A-S*c;WyS&|zQ z-=Sx3rP;elfTVVLwc$RKs4`aeU)11i`U^s{E>wipge!bfEz|bv^&hp%QU8+A;sb)- zE(G6(`l$4HV|<;skuC`ryrFb`qc;DA%PL3E(?+^3>BDuY{qe}UOc|~y99J5B&M};@ zx>h#=Lfs7SxXLZ$q;SdKg^&9Ee7Nc7a|lXE(vQgu2z>^esJ}Ahzo?b=d>} zcU-4c!(DmsK%fi$yUlI5?P@6XT0n5mzE7jp!~7Tj;p;-ZPS1jx--N@sR9Xsj=UuMa=Wp#g3cTQ|dAri;F<5J|j#=wE zpr&3_O8*?>$yPU!_O*7R>;Uwc~D6VeH#Y-oo0S~UaaaD_b4-&A`aIBmL z;JR2CWl#2~-^l$-oGHj3t|xy9`N#-+Li`d6<%zIi_e-=9(!V$;oR<@GpvN>GnxPZP zJ!m;3b4p=zE-VhbPYgicklSGc?;~jG4<#4DR^8obDkN;4Lwd66(oRaG?N^P z3d!A&rZ0m6JQqKLE_8D=4-&*_P}wX&^U1xikT4t)$IsA0G7vTePeU7s?_ulkEa+Kl zp$Vuap2K$FH&B3Q5#3QLT7`F_rO?U#7Pbt3j6Q)yi|Md!_)~HiS_joqcanodb24l! zUW?X4D|akf0j2*HXaHbHGdJNiXbx_JW&>5W1T@n)OQBasI<1&Q2@d)JDfslY7f)sofY)B4* zbagaz*R7CdN`-tAlK*&EJGcRh43i)^&xQW`YoVr_$U(A`A}mhGq5oG5JDHEdUS>V) zRNe?Xo?AhCek^oAk3rS>0~ZH{K?mrF(y*_&0CqY*64KCxuqC>^upDBA{mzh6MZi|) zUEF0ziiPC0H*AXD0~?>$3U&4h=yp^L_;PE#+-pJ!`dAy0on|Zv2$53Ew zzpY!+yk6-fmUmm~W@oOHx;R(bOm?z+iK$0g!JR#-@iYI8EV0hRrMo4(VohLka z!jmVwctXk(EKkUILe3Kko=}46v6t8&8IEMTvSl(nnEh3TZR~j&?#+&1&#}kYbao7T zn*B(IBiILQJ2s8I!(L$z$Z&u5kPPRuC)sY?ut^u_xH+Y)|$udxX6x!v-0SVt;3?>`1l?`;!b;vQOFZ>@D^T`;fgM z!$oW++g<^i`Gvn%>>n~*EW^<N$qhz?6?JUEC*oo`~87{DUa;DVY+;tqo(k_^#W7aZNbtmwN6ElPvx{MiW|8}H( z3&*ESt#YbzBvOT}+qKm}PHdnLyuCne`3mnByv*CjV4 zwapBom&b0810Fwl9QHWkam?em#|@9$9(O(NdpzxwSJ(E2% zJ+nOfdFFcNdk*j{@GSBy_N?(7;W^5a_nhiE-E*PmYR|7c*Lkk@{KfNx=ULB7o>x4t zdEW56<@u}UL(ktl|Mhb7a`*D|l6u8?C3vNHHSv1StC?2|uU1}dygGVy_UhwR;8o;R z>{aUZiPs{p#a>@{t@irLYn|76uW!7*_1f&U$7`S0DX;5ZH@$9q-H|dR-xxYM6u9j=%A@WeUS#FiLmUowD$TQ_x@*4SYIVT?{pCF$kpCX?opCMl$ z-z5K0zDK@Ken5U#eqa7r{+Il3`7`+o`M>hl3R6SE6yk`DlRLoDy}PTDsC(OQj$tmDOW0$ z-pUlEMfskxrLwiMt+Ktcqq4KIx3aIYzw!g6O*v3mq8z3ip&X^;luMP%m8+ENl zD8E&1R(_}4t=y|Tro5!QqP(WOp(0gcl|&^~$y5rJO68;SQw6Css!&zDDp8fJN>OF0 zdaJTk`Kke`0#%W!SXHVTt{SNtt>RTPRkKt}RbQ(%s5YuLsZOiTsxGOnt8S`ptL~`o zsUE2QRK4(~z1_Usy*<5SyyLu+y{+C&yx;R~=H0@(m3Ig4PTuL>A9&|_=X($Ep5Q&n zd%E{0-gCS^^`7s&zw~cQ*-$LI(zU98vzC(O#e24pv z^d0Rx)_1(`RNwi&3w#&(F81B!yW97G@6WzReSh&i;d{#WjPGB5D!&lFP`_}$NWb=e z9sRob_3-QI*UPVuUq8QWzkI(B{YLq5ejoXb_50FqrQce=Z~VUX+wAw9-!{MR{SNva z_B-PDi{E9xtA0=Y34f74<`A7T5`6v6Q_*?u_{hRu~??1qQqW>KKPyOfnFYw>t zztew@|3Uvl{y+O4_5a2Hg#T6l8~(Ta@AyCQe;VK!;2#hepbpRmG!JMQ&@P~JK-Yk@ zfb@VK0X+k%1EvQo2v`)bIACeO{(yr4M*>a+oC-J-a4z6Nz@>nD0S^Km1^gaJ2C4(2 z1C4>Pf$@Qvfmwn50&@fN0|x{a1QrDr2i61*4_q9$Ht_4f4S^d2&jy|kyb^db@OI#x zzgMW}>elME>h|hR>aOZEb-KETx~ICAy05yw`U7>I+NK_;E>sUv zm#E9s73wPWVD(V-F!c!aX!S?xaq5ZcDe7tJnd)uoJ?fv;N7X0Q=hPR}m(*9(*VH%E zx75F?|53kCzf!-^5E@$Jth?HFq`lH4invX&!6-&^*!nr47<*w4vH4 zZM4>?jn&3$6Sc|O6m1i2M{R~STbrZJ)7rEHwT0S2+L_u}+E29$wV!L3XqRcf(5}#K z(4N+w)n3wG*WT3L*51+H(>~BX4^{_jgTsRL!G_?N;JDy~;G|$vaM$3p;NHR6!2^P4 z1b-YnH+VttqTt2BOM{mOe;K?ocy;iu;A6q(g0BbP489$FC-`3QgAiqicSt~pHY6k@ zG$cGEGDII@2#F188qz7GM@Zk0{vjWP7obz?w7T}Xj=FBT z9=e{o-nzcJ{<;rzxjLJ!LdWYS>z3-4>sIJi>(=Vl>o)2(>9**$>b}?gNB4v7uI|3> zvFGXMChr|Goj}~FNWR{8Ozp&h}{ICIG1!09@gThL}O2a-4n;-Uh*z&M1!&Zi^4qFqpHtg%L^dd*pslo!=8n`2>UnebvO>E!d2nw@Z|86a7%cT@b|-;hqnxG9o{y)LwKj~ zF5%t6Yr=*zd5v?OSM0AZvi%5^?5s?{@712AQPsG@W@exxZW=714m=p17#QcZ_ z5sM<0M68V167fUC-iQMcKSdmlco^|p#FL0;5icWNMZAf`kz}M-WJ;tZ^1aBGk*y=! zMRthn6xk)RTV#44cSRnE zJQH~}@^0k)$cK@SBmane5)~Mwj?zU%M(LvrQ87`mQSniUQAtrPql%+Sqbj3@Mh%M^ z88teJj~Wv-E^1=br%}tJ)<%6D^-a_dQ3s=rM4i#|`Z4-(`U(2U`l6IGT(W zN7K=6(eBZn(b8yHv?5v+?Gx=69S|K9t%(kf)lhp(ilJj9F`hAOj3UN6#xEu?MiUbf6BZL06Ahnf#>XVZq{LWb zn#MGXX&KWdrhQDOn65G1V|v77#q^2kACnW4AG084SIq91YcVfksaR=jXlz_;huA@} zGh>&>u8;jb_Gs+&*cY*{;}miFxE664aYb<>;wHu|h}#->D(+g`^LUSVU3^%4RD4Q& z*Z8#fnei**_rza_f095Y_$7oTv`ENJn2@kEVRypygjb2$#MH#LiJcM$Ck{=VlDIf= zY2uE=ONsXr|4u?lq9nhh@TA0~c1amYg-K(RCL~QtT9ULZX+zSXq%%q9lAa}pBu6J3 zl4Fz2$(H0M$*oPzOf5{UOl?fsjn&9lxxa24Kx**icO`a za#Mw=$~4$C)HKXA!Zg~%o5q;NnI@Pfo2Htkn`WA3nP!{jn&z24Gc7cIZdzhmX8OXk z!nDftm1&)6gK48_lWB`-t7*GwhiRv2muZh_pXq?{5}p#7qE9iT#HPfjB&H;%q@-96VWzvn`)mKC>*cEU_%Ntgx)MthKDSY_x2)Y_)uE*=hOFve$CJa>(+t<*4Pj<)r1b z<(%b$<+A0f<%Z>!9L)tH?@Q-K_3bPpi}_ zvns6KR$r^XHPEWD23vL3aBHMhZ#7tBtZ~)^YqB-PYPB}8zGrQ2ZE0<7ZD;LZ?PTq0 zO|zz3Gp$+HKGuHLY-_I7W-YK5S&OY@)(@?f)@thzYmIfdb(EF2jdd+&ndfR%(de8dM`qcWj^;s%TB~rzyUa8Vld1^qaHZ?XiDYZ>% W$JAcP{znjwUuKl!x9 + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.apple.example.DispatchLife + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.1 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/examples/DispatchLife/ReadMe.txt b/examples/DispatchLife/ReadMe.txt new file mode 100644 index 0000000..2beea13 --- /dev/null +++ b/examples/DispatchLife/ReadMe.txt @@ -0,0 +1,37 @@ +### DispatchLife ### + +=========================================================================== +DESCRIPTION: + +The classic game of Life showing use of dispatch queues as lightweight threads (each cell is a queue), and an example of how to avoid overloading a slow queue (OpenGL or curses screen updates) with many requests (cell updates) by using a timer to drive the screen updates and allowing the cells to update as fast as they can. + +=========================================================================== +BUILD REQUIREMENTS: + +Mac OS X version 10.6 Snow Leopard + +=========================================================================== +RUNTIME REQUIREMENTS: + +Mac OS X version 10.6 Snow Leopard + +=========================================================================== +PACKAGING LIST: + +DispatchLife.c - Simulation engine using GCD. +DispatchLifeGLView.h - OpenGL view for visualization. +DispatchLifeGLView.m - OpenGL view for visualization. + +=========================================================================== +CHANGES FROM PREVIOUS VERSIONS: + +Version 1.2 +- Updated to use current GCD source API. +Version 1.1 +- Updated to use current GCD API. +- Added OpenGL view for visualization. +Version 1.0 +- First version (WWDC 2008). + +=========================================================================== +Copyright (C) 2008-2009 Apple Inc. All rights reserved. diff --git a/examples/DispatchLife/main.m b/examples/DispatchLife/main.m new file mode 100644 index 0000000..59eb37a --- /dev/null +++ b/examples/DispatchLife/main.m @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2009-2009 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ + +#import + +int main(int argc, char *argv[]) +{ + return NSApplicationMain(argc, (const char **) argv); +} diff --git a/examples/DispatchWebServer/DispatchWebServer.c b/examples/DispatchWebServer/DispatchWebServer.c new file mode 100644 index 0000000..d839d3b --- /dev/null +++ b/examples/DispatchWebServer/DispatchWebServer.c @@ -0,0 +1,956 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_DTS_LICENSE_HEADER_START@ + * + * IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + * ("Apple") in consideration of your agreement to the following terms, and your + * use, installation, modification or redistribution of this Apple software + * constitutes acceptance of these terms. If you do not agree with these terms, + * please do not use, install, modify or redistribute this Apple software. + * + * In consideration of your agreement to abide by the following terms, and + * subject to these terms, Apple grants you a personal, non-exclusive license, + * under Apple's copyrights in this original Apple software (the "Apple Software"), + * to use, reproduce, modify and redistribute the Apple Software, with or without + * modifications, in source and/or binary forms; provided that if you redistribute + * the Apple Software in its entirety and without modifications, you must retain + * this notice and the following text and disclaimers in all such redistributions + * of the Apple Software. Neither the name, trademarks, service marks or logos of + * Apple Computer, Inc. may be used to endorse or promote products derived from + * the Apple Software without specific prior written permission from Apple. Except + * as expressly stated in this notice, no other rights or licenses, express or + * implied, are granted by Apple herein, including but not limited to any patent + * rights that may be infringed by your derivative works or by other works in + * which the Apple Software may be incorporated. + * + * The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + * COMBINATION WITH YOUR PRODUCTS. + * + * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR + * DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF + * CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF + * APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * @APPLE_DTS_LICENSE_HEADER_END@ + */ + +/* A tiny web server that does as much stuff the "dispatch way" as it can, like a queue per connection... */ + +/**************************************************************************** +overview of dispatch related operations: + +main() { + have dump_reqs() called every 5 to 6 seconds, and on every SIGINFO + and SIGPIPE + + have accept_cb() called when there are new connections on our port + + have reopen_logfile_when_needed() called whenever our logfile is + renamed, deleted, or forcibly closed +} + +reopen_logfile_when_needed() { + call ourself whenever our logfile is renamed, deleted, or forcibly + closed +} + +accept_cb() { + allocate a new queue to handle network and file I/O, and timers + for a series of HTTP requests coming from a new network connection + + have read_req() called (on the new queue) when there + is network traffic for the new connection + + have req_free(new_req) called when the connection is "done" (no + pending work to be executed on the queue, an no sources left to + generate new work for the queue) +} + +req_free() { + uses dispatch_get_current_queue() and dispatch_async() to call itself + "on the right queue" +} + +read_req() { + If there is a timeout source delete_source() it + + if (we have a whole request) { + make a new dispatch source (req->fd_rd.ds) for the + content file + + have clean up fd, req->fd and req->fd_rd (if + appropriate) when the content file source is canceled + + have read_filedata called when the content file is + read to be read + + if we already have a dispatch source for "network + socket ready to be written", enable it. Otherwise + make one, and have write_filedata called when it + time to write to it. + + disable the call to read_req + } + + close the connection if something goes wrong +} + +write_filedata() { + close the connection if anything goes wrong + + if (we have written the whole HTTP document) { + timeout in a little bit, closing the connection if we + haven't received a new command + + enable the call to read_req + } + + if (we have written all the buffered data) { + disable the call to write_filedata() + } +} + +read_filedata() { + if (nothing left to read) { + delete the content file dispatch source + } else { + enable the call to write_filedata() + } +} + +qprintf, qfprintf, qflush + schedule stdio calls on a single queue + +disable_source, enable_source + implements a binary enable/disable on top of dispatch's + counted suspend/resume + +delete_source + cancels the source (this example program uses source + cancelation to schedule any source cleanup it needs, + so "delete" needs a cancel). + + ensure the source isn't suspended + + release the reference, which _should_ be the last + reference (this example program never has more + then one reference to a source) + +****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +char *DOC_BASE = NULL; +char *log_name = NULL; +FILE *logfile = NULL; +char *argv0 = "a.out"; +char *server_port = "8080"; +const int re_request_nmatch = 4; +regex_t re_first_request, re_nth_request, re_accept_deflate, re_host; + + +// qpf is the queue that we schedule our "stdio file I/O", which serves as a lock, +// and orders the output, and also gets it "out of the way" of our main line execution +dispatch_queue_t qpf; + +void qfprintf(FILE *f, const char *fmt, ...) __attribute__((format(printf, 2, 3))); + +void qfprintf(FILE *f, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + char *str; + /* We gennerate the formatted string on the same queue (or + thread) that calls qfprintf, that way the values can change + while the fputs call is being sent to the qpf queue, or waiting + for other work to complete ont he qpf queue. */ + + vasprintf(&str, fmt, ap); + dispatch_async(qpf, ^{ fputs(str, f); free(str); }); + if ('*' == *fmt) { + dispatch_sync(qpf, ^{ fflush(f); }); + } + va_end(ap); +} + +void qfflush(FILE *f) { + dispatch_sync(qpf, ^{ fflush(f); }); +} + +void reopen_logfile_when_needed() { + // We don't want to use a fd with a lifetime managed by something else + // because we need to close it inside the cancel handler (see below) + int lf_dup = dup(fileno(logfile)); + FILE **lf = &logfile; + + // We register the vnode callback on the qpf queue since that is where + // we do all our logfile printing. (we set up to reopen the logfile + // if the "old one" has been deleted or renamed (or revoked). This + // makes it pretty safe to mv the file to a new name, delay breifly, + // then gzip it. Safer to move the file to a new name, wait for the + // "old" file to reappear, then gzip. Niftier then doing the move, + // sending a SIGHUP to the right process (somehow) and then doing + // as above. Well, maybe it'll never catch on as "the new right + /// thing", but it makes a nifty demo. + dispatch_source_t vn = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, lf_dup, DISPATCH_VNODE_REVOKE|DISPATCH_VNODE_RENAME|DISPATCH_VNODE_DELETE, qpf); + + dispatch_source_set_event_handler(vn, ^{ + printf("lf_dup is %d (logfile's fileno=%d), closing it\n", lf_dup, fileno(logfile)); + fprintf(logfile, "# flush n' roll!\n"); + dispatch_cancel(vn); + dispatch_release(vn); + fflush(logfile); + *lf = freopen(log_name, "a", logfile); + + // The new logfile has (or may have) a diffrent fd from the old one, so + // we have to register it again + reopen_logfile_when_needed(); + }); + + dispatch_source_set_cancel_handler(vn, ^{ close(lf_dup); }); + + dispatch_resume(vn); +} + +#define qprintf(fmt...) qfprintf(stdout, ## fmt); + +struct buffer { + // Manage a buffer, currently at sz bytes, but will realloc if needed + // The buffer has a part that we read data INTO, and a part that we + // write data OUT OF. + // + // Best use of the space would be a circular buffer (and we would + // use readv/writev and pass around iovec structs), but we use a + // simpler layout: + // data from buf to outof is wasted. From outof to into is + // "ready to write data OUT OF", from into until buf+sz is + // "ready to read data IN TO". + size_t sz; + unsigned char *buf; + unsigned char *into, *outof; +}; + +struct request_source { + // libdispatch gives suspension a counting behaviour, we want a simple on/off behaviour, so we use + // this struct to provide track suspensions + dispatch_source_t ds; + bool suspended; +}; + +// The request struct manages an actiave HTTP request/connection. It gets reused for pipelined HTTP clients. +// Every request has it's own queue where all of it's network traffic, and source file I/O as well as +// compression (when requested by the HTTP client) is done. +struct request { + struct sockaddr_in r_addr; + z_stream *deflate; + // cmd_buf holds the HTTP request + char cmd_buf[8196], *cb; + char chunk_num[13], *cnp; // Big enough for 8 digits plus \r\n\r\n\0 + bool needs_zero_chunk; + bool reuse_guard; + short status_number; + size_t chunk_bytes_remaining; + char *q_name; + int req_num; // For debugging + int files_served; // For this socket + dispatch_queue_t q; + // "sd" is the socket descriptor, where the network I/O for this request goes. "fd" is the source file (or -1) + int sd, fd; + // fd_rd is for read events from the source file (say /Users/YOU/Sites/index.html for a GET /index.html request) + // sd_rd is for read events from the network socket (we suspend it after we read an HTTP request header, and + // resume it when we complete a request) + // sd_wr is for write events to the network socket (we suspend it when we have no buffered source data to send, + // and resume it when we have data ready to send) + // timeo is the timeout event waiting for a new client request header. + struct request_source fd_rd, sd_rd, sd_wr, timeo; + uint64_t timeout_at; + struct stat sb; + + // file_b is where we read data from fd into. + // For compressed GET requests: + // - data is compressed from file_b into deflate_b + // - data is written to the network socket from deflate_b + // For uncompressed GET requests + // - data is written to the network socket from file_b + // - deflate_b is unused + struct buffer file_b, deflate_b; + + ssize_t total_written; +}; + +void req_free(struct request *req); + +void disable_source(struct request *req, struct request_source *rs) { + // we want a binary suspend state, not a counted state. Our + // suspend flag is "locked" by only being used on req->q, this + // assert makes sure we are in a valid context to write the new + // suspend value. + assert(req->q == dispatch_get_current_queue()); + if (!rs->suspended) { + rs->suspended = true; + dispatch_suspend(rs->ds); + } +} + +void enable_source(struct request *req, struct request_source *rs) { + assert(req->q == dispatch_get_current_queue()); + if (rs->suspended) { + rs->suspended = false; + dispatch_resume(rs->ds); + } +} + +void delete_source(struct request *req, struct request_source *rs) { + assert(req->q == dispatch_get_current_queue()); + if (rs->ds) { + /* sources need to be resumed before they can be deleted + (otherwise an I/O and/or cancel block might be stranded + waiting for a resume that will never come, causing + leaks) */ + + enable_source(req, rs); + dispatch_cancel(rs->ds); + dispatch_release(rs->ds); + } + rs->ds = NULL; + rs->suspended = false; +} + +size_t buf_into_sz(struct buffer *b) { + return (b->buf + b->sz) - b->into; +} + +void buf_need_into(struct buffer *b, size_t cnt) { + // resize buf so into has at least cnt bytes ready to use + size_t sz = buf_into_sz(b); + if (cnt <= sz) { + return; + } + sz = malloc_good_size(cnt - sz + b->sz); + unsigned char *old = b->buf; + // We could special case b->buf == b->into && b->into == b->outof to + // do a free & malloc rather then realloc, but after testing it happens + // only for the 1st use of the buffer, where realloc is the same cost as + // malloc anyway. + b->buf = reallocf(b->buf, sz); + assert(b->buf); + b->sz = sz; + b->into = b->buf + (b->into - old); + b->outof = b->buf + (b->outof - old); +} + +void buf_used_into(struct buffer *b, size_t used) { + b->into += used; + assert(b->into <= b->buf + b->sz); +} + +size_t buf_outof_sz(struct buffer *b) { + return b->into - b->outof; +} + +int buf_sprintf(struct buffer *b, char *fmt, ...) __attribute__((format(printf,2,3))); + +int buf_sprintf(struct buffer *b, char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + size_t s = buf_into_sz(b); + int l = vsnprintf((char *)(b->into), s, fmt, ap); + if (l < s) { + buf_used_into(b, l); + } else { + // Reset ap -- vsnprintf has already used it. + va_end(ap); + va_start(ap, fmt); + buf_need_into(b, l); + s = buf_into_sz(b); + l = vsnprintf((char *)(b->into), s, fmt, ap); + assert(l <= s); + buf_used_into(b, l); + } + va_end(ap); + + return l; +} + +void buf_used_outof(struct buffer *b, size_t used) { + b->outof += used; + //assert(b->into <= b->outof); + assert(b->outof <= b->into); + if (b->into == b->outof) { + b->into = b->outof = b->buf; + } +} + +char *buf_debug_str(struct buffer *b) { + char *ret = NULL; + asprintf(&ret, "S%d i#%d o#%d", b->sz, buf_into_sz(b), buf_outof_sz(b)); + return ret; +} + +uint64_t getnanotime() { + struct timeval tv; + gettimeofday(&tv, NULL); + + return tv.tv_sec * NSEC_PER_SEC + tv.tv_usec * NSEC_PER_USEC; +} + +int n_req; +struct request **debug_req; + +void dump_reqs() { + int i = 0; + static last_reported = -1; + + // We want to see the transition into n_req == 0, but we don't need to + // keep seeing it. + if (n_req == 0 && n_req == last_reported) { + return; + } else { + last_reported = n_req; + } + + qprintf("%d actiave requests to dump\n", n_req); + uint64_t now = getnanotime(); + /* Because we iterate over the debug_req array in this queue + ("the main queue"), it has to "own" that array. All manipulation + of the array as a whole will have to be done on this queue. */ + + for(i = 0; i < n_req; i++) { + struct request *req = debug_req[i]; + qprintf("%s sources: fd_rd %p%s, sd_rd %p%s, sd_wr %p%s, timeo %p%s\n", req->q_name, req->fd_rd.ds, req->fd_rd.suspended ? " (SUSPENDED)" : "", req->sd_rd.ds, req->sd_rd.suspended ? " (SUSPENDED)" : "", req->sd_wr.ds, req->sd_wr.suspended ? " (SUSPENDED)" : "", req->timeo.ds, req->timeo.suspended ? " (SUSPENDED)" : ""); + if (req->timeout_at) { + double when = req->timeout_at - now; + when /= NSEC_PER_SEC; + if (when < 0) { + qprintf(" timeout %f seconds ago\n", -when); + } else { + qprintf(" timeout in %f seconds\n", when); + } + } else { + qprintf(" timeout_at not set\n"); + } + char *file_bd = buf_debug_str(&req->file_b), *deflate_bd = buf_debug_str(&req->deflate_b); + qprintf(" file_b %s; deflate_b %s\n cmd_buf used %ld; fd#%d; files_served %d\n", file_bd, deflate_bd, (long)(req->cb - req->cmd_buf), req->fd, req->files_served); + if (req->deflate) { + qprintf(" deflate total in: %ld ", req->deflate->total_in); + } + qprintf("%s total_written %lu, file size %lld\n", req->deflate ? "" : " ", req->total_written, req->sb.st_size); + free(file_bd); + free(deflate_bd); + } +} + +void req_free(struct request *req) { + assert(!req->reuse_guard); + if (dispatch_get_main_queue() != dispatch_get_current_queue()) { + /* dispatch_set_finalizer_f arranges to have us "invoked + asynchronously on req->q's target queue". However, + we want to manipulate the debug_req array in ways + that are unsafe anywhere except the same queue that + dump_reqs runs on (which happens to be the main queue). + So if we are running anywhere but the main queue, we + just arrange to be called there */ + + dispatch_async(dispatch_get_main_queue(), ^{ req_free(req); }); + return; + } + + req->reuse_guard = true; + *(req->cb) = '\0'; + qprintf("$$$ req_free %s; fd#%d; buf: %s\n", dispatch_queue_get_label(req->q), req->fd, req->cmd_buf); + assert(req->sd_rd.ds == NULL && req->sd_wr.ds == NULL); + close(req->sd); + assert(req->fd_rd.ds == NULL); + if (req->fd >= 0) close(req->fd); + free(req->file_b.buf); + free(req->deflate_b.buf); + free(req->q_name); + free(req->deflate); + free(req); + + int i; + bool found = false; + for(i = 0; i < n_req; i++) { + if (found) { + debug_req[i -1] = debug_req[i]; + } else { + found = (debug_req[i] == req); + } + } + debug_req = reallocf(debug_req, sizeof(struct request *) * --n_req); + assert(n_req >= 0); +} + +void close_connection(struct request *req) { + qprintf("$$$ close_connection %s, served %d files -- canceling all sources\n", dispatch_queue_get_label(req->q), req->files_served); + delete_source(req, &req->fd_rd); + delete_source(req, &req->sd_rd); + delete_source(req, &req->sd_wr); + delete_source(req, &req->timeo); +} + +// We have some "content data" (either from the file, or from +// compressing the file), and the network socket is ready for us to +// write it +void write_filedata(struct request *req, size_t avail) { + /* We always attempt to write as much data as we have. This + is safe becuase we use non-blocking I/O. It is a good idea + becuase the amount of buffer space that dispatch tells us may + be stale (more space could have opened up, or memory presure + may have caused it to go down). */ + + struct buffer *w_buf = req->deflate ? &req->deflate_b : &req->file_b; + ssize_t sz = buf_outof_sz(w_buf); + if (req->deflate) { + struct iovec iov[2]; + if (!req->chunk_bytes_remaining) { + req->chunk_bytes_remaining = sz; + req->needs_zero_chunk = sz != 0; + req->cnp = req->chunk_num; + int n = snprintf(req->chunk_num, sizeof(req->chunk_num), "\r\n%lx\r\n%s", sz, sz ? "" : "\r\n"); + assert(n <= sizeof(req->chunk_num)); + } + iov[0].iov_base = req->cnp; + iov[0].iov_len = req->cnp ? strlen(req->cnp) : 0; + iov[1].iov_base = w_buf->outof; + iov[1].iov_len = (req->chunk_bytes_remaining < sz) ? req->chunk_bytes_remaining : sz; + sz = writev(req->sd, iov, 2); + if (sz > 0) { + if (req->cnp) { + if (sz >= strlen(req->cnp)) { + req->cnp = NULL; + } else { + req->cnp += sz; + } + } + sz -= iov[0].iov_len; + sz = (sz < 0) ? 0 : sz; + req->chunk_bytes_remaining -= sz; + } + } else { + sz = write(req->sd, w_buf->outof, sz); + } + if (sz > 0) { + buf_used_outof(w_buf, sz); + } else if (sz < 0) { + int e = errno; + qprintf("write_filedata %s write error: %d %s\n", dispatch_queue_get_label(req->q), e, strerror(e)); + close_connection(req); + return; + } + + req->total_written += sz; + off_t bytes = req->total_written; + if (req->deflate) { + bytes = req->deflate->total_in - buf_outof_sz(w_buf); + if (req->deflate->total_in < buf_outof_sz(w_buf)) { + bytes = 0; + } + } + if (bytes == req->sb.st_size) { + if (req->needs_zero_chunk && req->deflate && (sz || req->cnp)) { + return; + } + + // We have transfered the file, time to write the log entry. + + // We don't deal with " in the request string, this is an example of how + // to use dispatch, not how to do C string manipulation, eh? + size_t rlen = strcspn(req->cmd_buf, "\r\n"); + char tstr[45], astr[45]; + struct tm tm; + time_t clock; + time(&clock); + strftime(tstr, sizeof(tstr), "%d/%b/%Y:%H:%M:%S +0", gmtime_r(&clock, &tm)); + addr2ascii(AF_INET, &req->r_addr.sin_addr, sizeof(struct in_addr), astr); + qfprintf(logfile, "%s - - [%s] \"%.*s\" %hd %zd\n", astr, tstr, (int)rlen, req->cmd_buf, req->status_number, req->total_written); + + int64_t t_offset = 5 * NSEC_PER_SEC + req->files_served * NSEC_PER_SEC / 10; + int64_t timeout_at = req->timeout_at = getnanotime() + t_offset; + + req->timeo.ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, req->q); + dispatch_source_set_timer(req->timeo.ds, dispatch_time(DISPATCH_TIME_NOW, t_offset), NSEC_PER_SEC, NSEC_PER_SEC); + dispatch_source_set_event_handler(req->timeo.ds, ^{ + if (req->timeout_at == timeout_at) { + qfprintf(stderr, "$$$ -- timeo fire (delta=%f) -- close connection: q=%s\n", (getnanotime() - (double)timeout_at) / NSEC_PER_SEC, dispatch_queue_get_label(req->q)); + close_connection(req); + } else { + // This happens if the timeout value has been updated, but a pending timeout event manages to race in before the cancel + } + }); + dispatch_resume(req->timeo.ds); + + req->files_served++; + qprintf("$$$ wrote whole file (%s); timeo %p, about to enable %p and close %d, total_written=%zd, this is the %d%s file served\n", dispatch_queue_get_label(req->q), req->timeo.ds, req->sd_rd.ds, req->fd, req->total_written, req->files_served, (1 == req->files_served) ? "st" : (2 == req->files_served) ? "nd" : "th"); + enable_source(req, &req->sd_rd); + if (req->fd_rd.ds) { + delete_source(req, &req->fd_rd); + } + req->cb = req->cmd_buf; + } else { + assert(bytes <= req->sb.st_size); + } + + if (0 == buf_outof_sz(w_buf)) { + // The write buffer is now empty, so we don't need to know when sd is ready for us to write to it. + disable_source(req, &req->sd_wr); + } +} + +// Our "content file" has some data ready for us to read. +void read_filedata(struct request *req, size_t avail) { + if (avail == 0) { + delete_source(req, &req->fd_rd); + return; + } + + /* We make sure we can read at least as many bytes as dispatch + says are avilable, but if our buffer is bigger we will read as + much as we have space for. We have the file opened in non-blocking + mode so this is safe. */ + + buf_need_into(&req->file_b, avail); + size_t rsz = buf_into_sz(&req->file_b); + ssize_t sz = read(req->fd, req->file_b.into, rsz); + if (sz >= 0) { + assert(req->sd_wr.ds); + size_t sz0 = buf_outof_sz(&req->file_b); + buf_used_into(&req->file_b, sz); + assert(sz == buf_outof_sz(&req->file_b) - sz0); + } else { + int e = errno; + qprintf("read_filedata %s read error: %d %s\n", dispatch_queue_get_label(req->q), e, strerror(e)); + close_connection(req); + return; + } + if (req->deflate) { + // Note:: deflateBound is "worst case", we could try with any non-zero + // buffer, and alloc more if we get Z_BUF_ERROR... + buf_need_into(&req->deflate_b, deflateBound(req->deflate, buf_outof_sz(&req->file_b))); + req->deflate->next_in = (req->file_b.outof); + size_t o_sz = buf_outof_sz(&req->file_b); + req->deflate->avail_in = o_sz; + req->deflate->next_out = req->deflate_b.into; + size_t i_sz = buf_into_sz(&req->deflate_b); + req->deflate->avail_out = i_sz; + assert(req->deflate->avail_in + req->deflate->total_in <= req->sb.st_size); + // at EOF we want to use Z_FINISH, otherwise we pass Z_NO_FLUSH so we get maximum compression + int rc = deflate(req->deflate, (req->deflate->avail_in + req->deflate->total_in >= req->sb.st_size) ? Z_FINISH : Z_NO_FLUSH); + assert(rc == Z_OK || rc == Z_STREAM_END); + buf_used_outof(&req->file_b, o_sz - req->deflate->avail_in); + buf_used_into(&req->deflate_b, i_sz - req->deflate->avail_out); + if (i_sz != req->deflate->avail_out) { + enable_source(req, &req->sd_wr); + } + } else { + enable_source(req, &req->sd_wr); + } +} + +// We are waiting to for an HTTP request (we eitther havn't gotten +// the first request, or pipelneing is on, and we finished a request), +// and there is data to read on the network socket. +void read_req(struct request *req, size_t avail) { + if (req->timeo.ds) { + delete_source(req, &req->timeo); + } + + // -1 to account for the trailing NUL + int s = (sizeof(req->cmd_buf) - (req->cb - req->cmd_buf)) -1; + if (s == 0) { + qprintf("read_req fd#%d command overflow\n", req->sd); + close_connection(req); + return; + } + int rd = read(req->sd, req->cb, s); + if (rd > 0) { + req->cb += rd; + if (req->cb > req->cmd_buf + 4) { + int i; + for(i = -4; i != 0; i++) { + char ch = *(req->cb + i); + if (ch != '\n' && ch != '\r') { + break; + } + } + if (i == 0) { + *(req->cb) = '\0'; + + assert(buf_outof_sz(&req->file_b) == 0); + assert(buf_outof_sz(&req->deflate_b) == 0); + regmatch_t pmatch[re_request_nmatch]; + regex_t *rex = req->files_served ? &re_first_request : &re_nth_request; + int rc = regexec(rex, req->cmd_buf, re_request_nmatch, pmatch, 0); + if (rc) { + char ebuf[1024]; + regerror(rc, rex, ebuf, sizeof(ebuf)); + qprintf("\n$$$ regexec error: %s, ditching request: '%s'\n", ebuf, req->cmd_buf); + close_connection(req); + return; + } else { + if (!strncmp("GET", req->cmd_buf + pmatch[1].rm_so, pmatch[1].rm_eo - pmatch[1].rm_so)) { + rc = regexec(&re_accept_deflate, req->cmd_buf, 0, NULL, 0); + assert(rc == 0 || rc == REG_NOMATCH); + // to disable deflate code: + // rc = REG_NOMATCH; + if (req->deflate) { + deflateEnd(req->deflate); + free(req->deflate); + } + req->deflate = (0 == rc) ? calloc(1, sizeof(z_stream)) : NULL; + char path_buf[4096]; + strlcpy(path_buf, DOC_BASE, sizeof(path_buf)); + // WARNING: this doesn't avoid use of .. in the path + // do get outside of DOC_ROOT, a real web server would + // really have to avoid that. + char ch = *(req->cmd_buf + pmatch[2].rm_eo); + *(req->cmd_buf + pmatch[2].rm_eo) = '\0'; + strlcat(path_buf, req->cmd_buf + pmatch[2].rm_so, sizeof(path_buf)); + *(req->cmd_buf + pmatch[2].rm_eo) = ch; + req->fd = open(path_buf, O_RDONLY|O_NONBLOCK); + qprintf("GET req for %s, path: %s, deflate: %p; fd#%d\n", dispatch_queue_get_label(req->q), path_buf, req->deflate, req->fd); + size_t n; + if (req->fd < 0) { + const char *msg = "404 Page not here

You step in the stream,
but the water has moved on.
This page is not here.
"; + req->status_number = 404; + n = buf_sprintf(&req->file_b, "HTTP/1.1 404 Not Found\r\nContent-Length: %zu\r\nExpires: now\r\nServer: %s\r\n\r\n%s", strlen(msg), argv0, msg); + req->sb.st_size = 0; + } else { + rc = fstat(req->fd, &req->sb); + assert(rc >= 0); + if (req->sb.st_mode & S_IFDIR) { + req->status_number = 301; + regmatch_t hmatch[re_request_nmatch]; + rc = regexec(&re_host, req->cmd_buf, re_request_nmatch, hmatch, 0); + assert(rc == 0 || rc == REG_NOMATCH); + if (rc == REG_NOMATCH) { + hmatch[1].rm_so = hmatch[1].rm_eo = 0; + } + n = buf_sprintf(&req->file_b, "HTTP/1.1 301 Redirect\r\nContent-Length: 0\r\nExpires: now\r\nServer: %s\r\nLocation: http://%*.0s/%*.0s/index.html\r\n\r\n", argv0, (int)(hmatch[1].rm_eo - hmatch[1].rm_so), req->cmd_buf + hmatch[1].rm_so, (int)(pmatch[2].rm_eo - pmatch[2].rm_so), req->cmd_buf + pmatch[2].rm_so); + req->sb.st_size = 0; + close(req->fd); + req->fd = -1; + } else { + req->status_number = 200; + if (req->deflate) { + n = buf_sprintf(&req->deflate_b, "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\nContent-Encoding: deflate\r\nExpires: now\r\nServer: %s\r\n", argv0); + req->chunk_bytes_remaining = buf_outof_sz(&req->deflate_b); + } else { + n = buf_sprintf(req->deflate ? &req->deflate_b : &req->file_b, "HTTP/1.1 200 OK\r\nContent-Length: %lld\r\nExpires: now\r\nServer: %s\r\n\r\n", req->sb.st_size, argv0); + } + } + } + + if (req->status_number != 200) { + free(req->deflate); + req->deflate = NULL; + } + + if (req->deflate) { + rc = deflateInit(req->deflate, Z_BEST_COMPRESSION); + assert(rc == Z_OK); + } + + // Cheat: we don't count the header bytes as part of total_written + req->total_written = -buf_outof_sz(&req->file_b); + if (req->fd >= 0) { + req->fd_rd.ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, req->fd, 0, req->q); + // Cancelation is async, so we capture the fd and read sources we will want to operate on as the req struct may have moved on to a new set of values + int fd = req->fd; + dispatch_source_t fd_rd = req->fd_rd.ds; + dispatch_source_set_cancel_handler(req->fd_rd.ds, ^{ + close(fd); + if (req->fd == fd) { + req->fd = -1; + } + if (req->fd_rd.ds == fd_rd) { + req->fd_rd.ds = NULL; + } + }); + dispatch_source_set_event_handler(req->fd_rd.ds, ^{ + if (req->fd_rd.ds) { + read_filedata(req, dispatch_source_get_data(req->fd_rd.ds)); + } + }); + dispatch_resume(req->fd_rd.ds); + } else { + req->fd_rd.ds = NULL; + } + + if (req->sd_wr.ds) { + enable_source(req, &req->sd_wr); + } else { + req->sd_wr.ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, req->sd, 0, req->q); + dispatch_source_set_event_handler(req->sd_wr.ds, ^{ write_filedata(req, dispatch_source_get_data(req->sd_wr.ds)); }); + dispatch_resume(req->sd_wr.ds); + } + disable_source(req, &req->sd_rd); + } + } + } + } + } else if (rd == 0) { + qprintf("### (%s) read_req fd#%d rd=0 (%s); %d files served\n", dispatch_queue_get_label(req->q), req->sd, (req->cb == req->cmd_buf) ? "no final request" : "incomplete request", req->files_served); + close_connection(req); + return; + } else { + int e = errno; + qprintf("reqd_req fd#%d rd=%d err=%d %s\n", req->sd, rd, e, strerror(e)); + close_connection(req); + return; + } +} + +// We have a new connection, allocate a req struct & set up a read event handler +void accept_cb(int fd) { + static int req_num = 0; + struct request *new_req = calloc(1, sizeof(struct request)); + assert(new_req); + new_req->cb = new_req->cmd_buf; + socklen_t r_len = sizeof(new_req->r_addr); + int s = accept(fd, (struct sockaddr *)&(new_req->r_addr), &r_len); + if (s < 0) { + qfprintf(stderr, "accept failure (rc=%d, errno=%d %s)\n", s, errno, strerror(errno)); + return; + } + assert(s >= 0); + new_req->sd = s; + new_req->req_num = req_num; + asprintf(&(new_req->q_name), "req#%d s#%d", req_num++, s); + qprintf("accept_cb fd#%d; made: %s\n", fd, new_req->q_name); + + // All further work for this request will happen "on" new_req->q, + // except the final tear down (see req_free()) + new_req->q = dispatch_queue_create(new_req->q_name, NULL); + dispatch_set_context(new_req->q, new_req); + dispatch_set_finalizer_f(new_req->q, (dispatch_function_t)req_free); + + debug_req = reallocf(debug_req, sizeof(struct request *) * ++n_req); + debug_req[n_req -1] = new_req; + + + new_req->sd_rd.ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, new_req->sd, 0, new_req->q); + dispatch_source_set_event_handler(new_req->sd_rd.ds, ^{ + read_req(new_req, dispatch_source_get_data(new_req->sd_rd.ds)); + }); + + // We want our queue to go away when all of it's sources do, so we + // drop the reference dispatch_queue_create gave us & rely on the + // references each source holds on the queue to keep it alive. + dispatch_release(new_req->q); + dispatch_resume(new_req->sd_rd.ds); +} + +int main(int argc, char *argv[]) { + int sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + assert(sock > 0); + int rc; + struct addrinfo ai_hints, *my_addr; + + qpf = dispatch_queue_create("printf", NULL); + + argv0 = basename(argv[0]); + struct passwd *pw = getpwuid(getuid()); + assert(pw); + asprintf(&DOC_BASE, "%s/Sites/", pw->pw_dir); + asprintf(&log_name, "%s/Library/Logs/%s-transfer.log", pw->pw_dir, argv0); + logfile = fopen(log_name, "a"); + reopen_logfile_when_needed(logfile, log_name); + + bzero(&ai_hints, sizeof(ai_hints)); + ai_hints.ai_flags = AI_PASSIVE; + ai_hints.ai_family = PF_INET; + ai_hints.ai_socktype = SOCK_STREAM; + ai_hints.ai_protocol = IPPROTO_TCP; + rc = getaddrinfo(NULL, server_port, &ai_hints, &my_addr); + assert(rc == 0); + + qprintf("Serving content from %s on port %s, logging transfers to %s\n", DOC_BASE, server_port, log_name); + + int yes = 1; + rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)); + assert(rc == 0); + yes = 1; + rc = setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes)); + assert(rc == 0); + + rc = bind(sock, my_addr->ai_addr, my_addr->ai_addr->sa_len); + assert(rc >= 0); + + rc = listen(sock, 25); + assert(rc >= 0); + + rc = regcomp(&re_first_request, "^([A-Z]+)[ \t]+([^ \t\n]+)[ \t]+HTTP/1\\.1[\r\n]+", REG_EXTENDED); + assert(rc == 0); + + rc = regcomp(&re_nth_request, "^([A-Z]+)[ \t]+([^ \t\n]+)([ \t]+HTTP/1\\.1)?[\r\n]+", REG_EXTENDED); + assert(rc == 0); + + rc = regcomp(&re_accept_deflate, "[\r\n]+Accept-Encoding:(.*,)? *deflate[,\r\n]+", REG_EXTENDED); + assert(rc == 0); + + rc = regcomp(&re_host, "[\r\n]+Host: *([^ \r\n]+)[ \r\n]+", REG_EXTENDED); + assert(rc == 0); + + dispatch_source_t accept_ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sock, 0, dispatch_get_main_queue()); + dispatch_source_set_event_handler(accept_ds, ^{ accept_cb(sock); }); + assert(accept_ds); + dispatch_resume(accept_ds); + + sigset_t sigs; + sigemptyset(&sigs); + sigaddset(&sigs, SIGINFO); + sigaddset(&sigs, SIGPIPE); + + int s; + for(s = 0; s < NSIG; s++) { + if (sigismember(&sigs, s)) { + dispatch_source_t sig_ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, s, 0, dispatch_get_main_queue()); + assert(sig_ds); + dispatch_source_set_event_handler(sig_ds, ^{ dump_reqs(); }); + dispatch_resume(sig_ds); + } + } + + rc = sigprocmask(SIG_BLOCK, &sigs, NULL); + assert(rc == 0); + + dispatch_source_t dump_timer_ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue()); + dispatch_source_set_timer(dump_timer_ds, DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC, NSEC_PER_SEC); + dispatch_source_set_event_handler(dump_timer_ds, ^{ dump_reqs(); }); + dispatch_resume(dump_timer_ds); + + dispatch_main(); + printf("dispatch_main returned\n"); + + return 1; +} diff --git a/examples/DispatchWebServer/DispatchWebServer.xcodeproj/project.pbxproj b/examples/DispatchWebServer/DispatchWebServer.xcodeproj/project.pbxproj new file mode 100644 index 0000000..444288a --- /dev/null +++ b/examples/DispatchWebServer/DispatchWebServer.xcodeproj/project.pbxproj @@ -0,0 +1,203 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 4CDA1C1F0F795F5B00E0869E /* DispatchWebServer.c in Sources */ = {isa = PBXBuildFile; fileRef = 4CDA1C1E0F795F5B00E0869E /* DispatchWebServer.c */; }; + 4CDA1C400F79786E00E0869E /* libz.1.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4CDA1C3F0F79786E00E0869E /* libz.1.dylib */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 8DD76FAF0486AB0100D96B5E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 4CDA1C1E0F795F5B00E0869E /* DispatchWebServer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = DispatchWebServer.c; sourceTree = ""; }; + 4CDA1C3F0F79786E00E0869E /* libz.1.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.1.dylib; path = /usr/lib/libz.1.dylib; sourceTree = ""; }; + 8DD76FB20486AB0100D96B5E /* DispatchWebServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = DispatchWebServer; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DD76FAD0486AB0100D96B5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CDA1C400F79786E00E0869E /* libz.1.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* DispatchWebServer */ = { + isa = PBXGroup; + children = ( + 4CDA1C3F0F79786E00E0869E /* libz.1.dylib */, + 08FB7795FE84155DC02AAC07 /* Source */, + C6A0FF2B0290797F04C91782 /* Documentation */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = DispatchWebServer; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 4CDA1C1E0F795F5B00E0869E /* DispatchWebServer.c */, + ); + name = Source; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8DD76FB20486AB0100D96B5E /* DispatchWebServer */, + ); + name = Products; + sourceTree = ""; + }; + C6A0FF2B0290797F04C91782 /* Documentation */ = { + isa = PBXGroup; + children = ( + ); + name = Documentation; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DD76FA90486AB0100D96B5E /* DispatchWebServer */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "DispatchWebServer" */; + buildPhases = ( + 8DD76FAB0486AB0100D96B5E /* Sources */, + 8DD76FAD0486AB0100D96B5E /* Frameworks */, + 8DD76FAF0486AB0100D96B5E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = DispatchWebServer; + productInstallPath = "$(HOME)/bin"; + productName = DispatchWebServer; + productReference = 8DD76FB20486AB0100D96B5E /* DispatchWebServer */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "DispatchWebServer" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* DispatchWebServer */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DD76FA90486AB0100D96B5E /* DispatchWebServer */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DD76FAB0486AB0100D96B5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4CDA1C1F0F795F5B00E0869E /* DispatchWebServer.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB928608733DD80010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = DispatchWebServer; + }; + name = Debug; + }; + 1DEB928708733DD80010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = DispatchWebServer; + }; + name = Release; + }; + 1DEB928A08733DD80010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + SDKROOT = macosx10.6; + }; + name = Debug; + }; + 1DEB928B08733DD80010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = macosx10.6; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "DispatchWebServer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB928608733DD80010E9CD /* Debug */, + 1DEB928708733DD80010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "DispatchWebServer" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB928A08733DD80010E9CD /* Debug */, + 1DEB928B08733DD80010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/examples/DispatchWebServer/ReadMe.txt b/examples/DispatchWebServer/ReadMe.txt new file mode 100644 index 0000000..4a63596 --- /dev/null +++ b/examples/DispatchWebServer/ReadMe.txt @@ -0,0 +1,44 @@ +### DispatchWebServer ### + +=========================================================================== +DESCRIPTION: + +Sample code showing how to: Use dispatch in a real world setting, +schedule file and network I/O, use vnode sources, create and manage +timers. + +=========================================================================== +BUILD REQUIREMENTS: + +Mac OS X version 10.6 Snow Leopard + +=========================================================================== +RUNTIME REQUIREMENTS: + +Mac OS X version 10.6 Snow Leopard + +=========================================================================== +PACKAGING LIST: + +DispatchWebServer.c - the web server + +=========================================================================== +RUNNING: + +Running the program will start a web server on port 8080, it will read +content from ~/Sites and write ~/Library/Logs/DispatchWebServer-transfer.log +each time complets a request. + +It will write some to stdout when it makes new connections, recieves +requests, completes requests, and when it closes connections. It also +shows the state of each actiave request once evey five seconds and any +time you send a SIGINFO signal to it. + +=========================================================================== +CHANGES FROM PREVIOUS VERSIONS: + +Version 1.0 +- First version + +=========================================================================== +Copyright (C) 2009 Apple Inc. All rights reserved. diff --git a/libdispatch.xcodeproj/project.pbxproj b/libdispatch.xcodeproj/project.pbxproj new file mode 100644 index 0000000..01b065c --- /dev/null +++ b/libdispatch.xcodeproj/project.pbxproj @@ -0,0 +1,497 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 2EC9C9B80E8809EF00E2499A /* legacy.c in Sources */ = {isa = PBXBuildFile; fileRef = 2EC9C9B70E8809EF00E2499A /* legacy.c */; }; + 5A5D13AC0F6B280500197CC3 /* semaphore_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 5A5D13AB0F6B280500197CC3 /* semaphore_internal.h */; }; + 721F5C5D0F15520500FF03A6 /* semaphore.h in Headers */ = {isa = PBXBuildFile; fileRef = 721F5C5C0F15520500FF03A6 /* semaphore.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 721F5CCF0F15553500FF03A6 /* semaphore.c in Sources */ = {isa = PBXBuildFile; fileRef = 721F5CCE0F15553500FF03A6 /* semaphore.c */; }; + 72CC94300ECCD8750031B751 /* base.h in Headers */ = {isa = PBXBuildFile; fileRef = 72CC942F0ECCD8750031B751 /* base.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 96032E4B0F5CC8C700241C5F /* time.c in Sources */ = {isa = PBXBuildFile; fileRef = 96032E4A0F5CC8C700241C5F /* time.c */; }; + 96032E4D0F5CC8D100241C5F /* time.h in Headers */ = {isa = PBXBuildFile; fileRef = 96032E4C0F5CC8D100241C5F /* time.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 961B99360F3E83980006BC96 /* benchmark.h in Headers */ = {isa = PBXBuildFile; fileRef = 961B99350F3E83980006BC96 /* benchmark.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 961B99500F3E85C30006BC96 /* object.h in Headers */ = {isa = PBXBuildFile; fileRef = 961B994F0F3E85C30006BC96 /* object.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 965CD6350F3E806200D4E28D /* benchmark.c in Sources */ = {isa = PBXBuildFile; fileRef = 965CD6340F3E806200D4E28D /* benchmark.c */; }; + 965ECC210F3EAB71004DDD89 /* object_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 965ECC200F3EAB71004DDD89 /* object_internal.h */; }; + 9661E56B0F3E7DDF00749F3E /* object.c in Sources */ = {isa = PBXBuildFile; fileRef = 9661E56A0F3E7DDF00749F3E /* object.c */; }; + 9676A0E10F3E755D00713ADB /* apply.c in Sources */ = {isa = PBXBuildFile; fileRef = 9676A0E00F3E755D00713ADB /* apply.c */; }; + 96929D840F3EA1020041FF5D /* hw_shims.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D820F3EA1020041FF5D /* hw_shims.h */; }; + 96929D850F3EA1020041FF5D /* os_shims.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D830F3EA1020041FF5D /* os_shims.h */; }; + 96929D960F3EA2170041FF5D /* queue_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 96929D950F3EA2170041FF5D /* queue_internal.h */; }; + 96A8AA870F41E7A400CD570B /* source.c in Sources */ = {isa = PBXBuildFile; fileRef = 96A8AA860F41E7A400CD570B /* source.c */; }; + 96BC39BD0F3EBAB100C59689 /* queue_private.h in Headers */ = {isa = PBXBuildFile; fileRef = 96BC39BC0F3EBAB100C59689 /* queue_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 96C9553B0F3EAEDD000D2CA4 /* once.h in Headers */ = {isa = PBXBuildFile; fileRef = 96C9553A0F3EAEDD000D2CA4 /* once.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 96DF70BE0F38FE3C0074BD99 /* once.c in Sources */ = {isa = PBXBuildFile; fileRef = 96DF70BD0F38FE3C0074BD99 /* once.c */; }; + FC0B34790FA2851C0080FFA0 /* source_internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FC0B34780FA2851C0080FFA0 /* source_internal.h */; }; + FC5C9C1E0EADABE3006E462D /* group.h in Headers */ = {isa = PBXBuildFile; fileRef = FC5C9C1D0EADABE3006E462D /* group.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FC7BED990E8361E600161930 /* queue.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED8A0E8361E600161930 /* queue.c */; }; + FC7BED9A0E8361E600161930 /* queue.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED8B0E8361E600161930 /* queue.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FC7BED9C0E8361E600161930 /* source.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED8D0E8361E600161930 /* source.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FC7BED9E0E8361E600161930 /* internal.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED8F0E8361E600161930 /* internal.h */; settings = {ATTRIBUTES = (); }; }; + FC7BED9F0E8361E600161930 /* legacy.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED900E8361E600161930 /* legacy.h */; settings = {ATTRIBUTES = (Private, ); }; }; + FC7BEDA20E8361E600161930 /* private.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED930E8361E600161930 /* private.h */; settings = {ATTRIBUTES = (Private, ); }; }; + FC7BEDA40E8361E600161930 /* protocol.defs in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED950E8361E600161930 /* protocol.defs */; settings = {ATTRIBUTES = (Client, Server, ); }; }; + FC7BEDA50E8361E600161930 /* dispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = FC7BED960E8361E600161930 /* dispatch.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FC7BEDA60E8361E600161930 /* shims.c in Sources */ = {isa = PBXBuildFile; fileRef = FC7BED970E8361E600161930 /* shims.c */; }; + FCEF04800F5661960067401F /* source_private.h in Headers */ = {isa = PBXBuildFile; fileRef = FCEF047F0F5661960067401F /* source_private.h */; settings = {ATTRIBUTES = (Private, ); }; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 2EC9C9B70E8809EF00E2499A /* legacy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = legacy.c; path = src/legacy.c; sourceTree = ""; }; + 5A5D13AB0F6B280500197CC3 /* semaphore_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = semaphore_internal.h; path = src/semaphore_internal.h; sourceTree = ""; }; + 721F5C5C0F15520500FF03A6 /* semaphore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = semaphore.h; path = src/semaphore.h; sourceTree = ""; }; + 721F5CCE0F15553500FF03A6 /* semaphore.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = semaphore.c; path = src/semaphore.c; sourceTree = ""; }; + 72B54F690EB169EB00DBECBA /* dispatch_source_create.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = dispatch_source_create.3; path = man/dispatch_source_create.3; sourceTree = ""; }; + 72CC940C0ECCD5720031B751 /* dispatch_object.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = dispatch_object.3; path = man/dispatch_object.3; sourceTree = ""; }; + 72CC940D0ECCD5720031B751 /* dispatch.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = dispatch.3; path = man/dispatch.3; sourceTree = ""; }; + 72CC942F0ECCD8750031B751 /* base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = base.h; path = src/base.h; sourceTree = ""; }; + 96032E4A0F5CC8C700241C5F /* time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = time.c; path = src/time.c; sourceTree = ""; }; + 96032E4C0F5CC8D100241C5F /* time.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = time.h; path = src/time.h; sourceTree = ""; }; + 960F0E7D0F3FB232000D88BF /* dispatch_apply.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_apply.3; path = man/dispatch_apply.3; sourceTree = ""; }; + 960F0E7E0F3FB232000D88BF /* dispatch_once.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_once.3; path = man/dispatch_once.3; sourceTree = ""; }; + 961B99350F3E83980006BC96 /* benchmark.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = benchmark.h; path = src/benchmark.h; sourceTree = ""; }; + 961B994F0F3E85C30006BC96 /* object.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = object.h; path = src/object.h; sourceTree = ""; }; + 963FDDE50F3FB6BD00BF2D00 /* dispatch_semaphore_create.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_semaphore_create.3; path = man/dispatch_semaphore_create.3; sourceTree = ""; }; + 965CD6340F3E806200D4E28D /* benchmark.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = benchmark.c; path = src/benchmark.c; sourceTree = ""; }; + 965ECC200F3EAB71004DDD89 /* object_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = object_internal.h; path = src/object_internal.h; sourceTree = ""; }; + 9661E56A0F3E7DDF00749F3E /* object.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = object.c; path = src/object.c; sourceTree = ""; }; + 9676A0E00F3E755D00713ADB /* apply.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = apply.c; path = src/apply.c; sourceTree = ""; }; + 96859A3D0EF71BAD003EB3FB /* dispatch_benchmark.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = dispatch_benchmark.3; path = man/dispatch_benchmark.3; sourceTree = ""; }; + 96929D820F3EA1020041FF5D /* hw_shims.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = hw_shims.h; path = src/hw_shims.h; sourceTree = ""; }; + 96929D830F3EA1020041FF5D /* os_shims.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = os_shims.h; path = src/os_shims.h; sourceTree = ""; }; + 96929D950F3EA2170041FF5D /* queue_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = queue_internal.h; path = src/queue_internal.h; sourceTree = ""; }; + 96A8AA860F41E7A400CD570B /* source.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = source.c; path = src/source.c; sourceTree = ""; }; + 96BC39BC0F3EBAB100C59689 /* queue_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = queue_private.h; path = src/queue_private.h; sourceTree = ""; }; + 96C9553A0F3EAEDD000D2CA4 /* once.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = once.h; path = src/once.h; sourceTree = ""; }; + 96DF70BD0F38FE3C0074BD99 /* once.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = once.c; path = src/once.c; sourceTree = ""; }; + D2AAC046055464E500DB518D /* libdispatch.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libdispatch.a; sourceTree = BUILT_PRODUCTS_DIR; }; + FC0B34780FA2851C0080FFA0 /* source_internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = source_internal.h; path = src/source_internal.h; sourceTree = ""; }; + FC36279C0E933ED80054F1A3 /* dispatch_queue_create.3 */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = dispatch_queue_create.3; path = man/dispatch_queue_create.3; sourceTree = ""; }; + FC5C9C1D0EADABE3006E462D /* group.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = group.h; path = src/group.h; sourceTree = ""; }; + FC678DE80F97E0C300AB5993 /* dispatch_after.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_after.3; path = man/dispatch_after.3; sourceTree = ""; }; + FC678DE90F97E0C300AB5993 /* dispatch_api.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_api.3; path = man/dispatch_api.3; sourceTree = ""; }; + FC678DEA0F97E0C300AB5993 /* dispatch_async.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_async.3; path = man/dispatch_async.3; sourceTree = ""; }; + FC678DEB0F97E0C300AB5993 /* dispatch_group_create.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_group_create.3; path = man/dispatch_group_create.3; sourceTree = ""; }; + FC678DEC0F97E0C300AB5993 /* dispatch_time.3 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = dispatch_time.3; path = man/dispatch_time.3; sourceTree = ""; }; + FC7BED8A0E8361E600161930 /* queue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = queue.c; path = src/queue.c; sourceTree = ""; }; + FC7BED8B0E8361E600161930 /* queue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = queue.h; path = src/queue.h; sourceTree = ""; }; + FC7BED8D0E8361E600161930 /* source.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = source.h; path = src/source.h; sourceTree = ""; }; + FC7BED8F0E8361E600161930 /* internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = internal.h; path = src/internal.h; sourceTree = ""; }; + FC7BED900E8361E600161930 /* legacy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = legacy.h; path = src/legacy.h; sourceTree = ""; }; + FC7BED930E8361E600161930 /* private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = private.h; path = src/private.h; sourceTree = ""; }; + FC7BED950E8361E600161930 /* protocol.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 4; name = protocol.defs; path = src/protocol.defs; sourceTree = ""; }; + FC7BED960E8361E600161930 /* dispatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dispatch.h; path = src/dispatch.h; sourceTree = ""; }; + FC7BED970E8361E600161930 /* shims.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = shims.c; path = src/shims.c; sourceTree = ""; }; + FCEF047F0F5661960067401F /* source_private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = source_private.h; path = src/source_private.h; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D289987405E68DCB004EDB86 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* libdispatch */ = { + isa = PBXGroup; + children = ( + FC7BEDAA0E83625200161930 /* Public Headers */, + FC7BEDAF0E83626100161930 /* Private Headers */, + FC7BEDB60E8363DC00161930 /* Project Headers */, + 08FB7795FE84155DC02AAC07 /* Source */, + C6A0FF2B0290797F04C91782 /* Documentation */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + ); + name = libdispatch; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 965CD6340F3E806200D4E28D /* benchmark.c */, + 9661E56A0F3E7DDF00749F3E /* object.c */, + 9676A0E00F3E755D00713ADB /* apply.c */, + FC7BED8A0E8361E600161930 /* queue.c */, + 96DF70BD0F38FE3C0074BD99 /* once.c */, + 96032E4A0F5CC8C700241C5F /* time.c */, + 721F5CCE0F15553500FF03A6 /* semaphore.c */, + 96A8AA860F41E7A400CD570B /* source.c */, + FC7BED970E8361E600161930 /* shims.c */, + 2EC9C9B70E8809EF00E2499A /* legacy.c */, + FC7BED950E8361E600161930 /* protocol.defs */, + ); + name = Source; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + D2AAC046055464E500DB518D /* libdispatch.a */, + ); + name = Products; + sourceTree = ""; + }; + C6A0FF2B0290797F04C91782 /* Documentation */ = { + isa = PBXGroup; + children = ( + FC678DE80F97E0C300AB5993 /* dispatch_after.3 */, + FC678DE90F97E0C300AB5993 /* dispatch_api.3 */, + FC678DEA0F97E0C300AB5993 /* dispatch_async.3 */, + FC678DEB0F97E0C300AB5993 /* dispatch_group_create.3 */, + FC678DEC0F97E0C300AB5993 /* dispatch_time.3 */, + 72CC940D0ECCD5720031B751 /* dispatch.3 */, + 960F0E7D0F3FB232000D88BF /* dispatch_apply.3 */, + 96859A3D0EF71BAD003EB3FB /* dispatch_benchmark.3 */, + 72CC940C0ECCD5720031B751 /* dispatch_object.3 */, + 960F0E7E0F3FB232000D88BF /* dispatch_once.3 */, + 963FDDE50F3FB6BD00BF2D00 /* dispatch_semaphore_create.3 */, + 72B54F690EB169EB00DBECBA /* dispatch_source_create.3 */, + FC36279C0E933ED80054F1A3 /* dispatch_queue_create.3 */, + ); + name = Documentation; + sourceTree = ""; + }; + FC7BEDAA0E83625200161930 /* Public Headers */ = { + isa = PBXGroup; + children = ( + 96032E4C0F5CC8D100241C5F /* time.h */, + 721F5C5C0F15520500FF03A6 /* semaphore.h */, + 961B994F0F3E85C30006BC96 /* object.h */, + 96C9553A0F3EAEDD000D2CA4 /* once.h */, + 961B99350F3E83980006BC96 /* benchmark.h */, + 72CC942F0ECCD8750031B751 /* base.h */, + FC7BED960E8361E600161930 /* dispatch.h */, + FC7BED8B0E8361E600161930 /* queue.h */, + FC7BED8D0E8361E600161930 /* source.h */, + FC5C9C1D0EADABE3006E462D /* group.h */, + ); + name = "Public Headers"; + sourceTree = ""; + }; + FC7BEDAF0E83626100161930 /* Private Headers */ = { + isa = PBXGroup; + children = ( + FC7BED930E8361E600161930 /* private.h */, + 96BC39BC0F3EBAB100C59689 /* queue_private.h */, + FC7BED900E8361E600161930 /* legacy.h */, + ); + name = "Private Headers"; + sourceTree = ""; + }; + FC7BEDB60E8363DC00161930 /* Project Headers */ = { + isa = PBXGroup; + children = ( + 965ECC200F3EAB71004DDD89 /* object_internal.h */, + 96929D950F3EA2170041FF5D /* queue_internal.h */, + 5A5D13AB0F6B280500197CC3 /* semaphore_internal.h */, + FC0B34780FA2851C0080FFA0 /* source_internal.h */, + FCEF047F0F5661960067401F /* source_private.h */, + 96929D820F3EA1020041FF5D /* hw_shims.h */, + 96929D830F3EA1020041FF5D /* os_shims.h */, + FC7BED8F0E8361E600161930 /* internal.h */, + ); + name = "Project Headers"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + D2AAC043055464E500DB518D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 72CC94300ECCD8750031B751 /* base.h in Headers */, + FC7BEDA50E8361E600161930 /* dispatch.h in Headers */, + FC7BED9A0E8361E600161930 /* queue.h in Headers */, + FC7BED9C0E8361E600161930 /* source.h in Headers */, + FC5C9C1E0EADABE3006E462D /* group.h in Headers */, + FC7BEDA20E8361E600161930 /* private.h in Headers */, + FC7BED9F0E8361E600161930 /* legacy.h in Headers */, + FC7BED9E0E8361E600161930 /* internal.h in Headers */, + 721F5C5D0F15520500FF03A6 /* semaphore.h in Headers */, + 961B99360F3E83980006BC96 /* benchmark.h in Headers */, + 961B99500F3E85C30006BC96 /* object.h in Headers */, + 96929D840F3EA1020041FF5D /* hw_shims.h in Headers */, + 96929D850F3EA1020041FF5D /* os_shims.h in Headers */, + 96929D960F3EA2170041FF5D /* queue_internal.h in Headers */, + 965ECC210F3EAB71004DDD89 /* object_internal.h in Headers */, + 96C9553B0F3EAEDD000D2CA4 /* once.h in Headers */, + 96BC39BD0F3EBAB100C59689 /* queue_private.h in Headers */, + FCEF04800F5661960067401F /* source_private.h in Headers */, + 96032E4D0F5CC8D100241C5F /* time.h in Headers */, + 5A5D13AC0F6B280500197CC3 /* semaphore_internal.h in Headers */, + FC0B34790FA2851C0080FFA0 /* source_internal.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXLegacyTarget section */ + 721EB4790F69D26F00845379 /* testbots */ = { + isa = PBXLegacyTarget; + buildArgumentsString = testbots; + buildConfigurationList = 721EB4850F69D2A600845379 /* Build configuration list for PBXLegacyTarget "testbots" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + buildWorkingDirectory = testing; + dependencies = ( + ); + name = testbots; + passBuildSettingsInEnvironment = 0; + productName = testbots; + }; + 7276FCBA0EB10E0F00F7F487 /* test */ = { + isa = PBXLegacyTarget; + buildArgumentsString = test; + buildConfigurationList = 7276FCC80EB10E2300F7F487 /* Build configuration list for PBXLegacyTarget "test" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + buildWorkingDirectory = testing; + dependencies = ( + ); + name = test; + passBuildSettingsInEnvironment = 0; + productName = test; + }; +/* End PBXLegacyTarget section */ + +/* Begin PBXNativeTarget section */ + D2AAC045055464E500DB518D /* libdispatch */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB91EB08733DB70010E9CD /* Build configuration list for PBXNativeTarget "libdispatch" */; + buildPhases = ( + D2AAC043055464E500DB518D /* Headers */, + D2AAC044055464E500DB518D /* Sources */, + D289987405E68DCB004EDB86 /* Frameworks */, + 2EC9C9800E846B5200E2499A /* ShellScript */, + 4CED8B9D0EEDF8B600AF99AB /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = libdispatch; + productName = libdispatch; + productReference = D2AAC046055464E500DB518D /* libdispatch.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "libdispatch" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 08FB7794FE84155DC02AAC07 /* libdispatch */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D2AAC045055464E500DB518D /* libdispatch */, + 7276FCBA0EB10E0F00F7F487 /* test */, + 721EB4790F69D26F00845379 /* testbots */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 2EC9C9800E846B5200E2499A /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "# private.h supersedes dispatch.h where available\nmv \"$DSTROOT\"/usr/local/include/dispatch/private.h \"$DSTROOT\"/usr/local/include/dispatch/dispatch.h\nln -sf dispatch.h \"$DSTROOT\"/usr/local/include/dispatch/private.h\n\n# keep events.h around for a little while\nln -sf ../../../include/dispatch/source.h \"$DSTROOT\"/usr/local/include/dispatch/events.h"; + }; + 4CED8B9D0EEDF8B600AF99AB /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "#!/bin/sh\n\nmkdir -p $DSTROOT/usr/share/man/man3 || true\nmkdir -p $DSTROOT/usr/local/share/man/man3 || true\n\n# Copy man pages\ncd $SRCROOT/man\nBASE_PAGES=\"dispatch.3 dispatch_after.3 dispatch_api.3 dispatch_apply.3 dispatch_async.3 dispatch_group_create.3 dispatch_object.3 dispatch_once.3 dispatch_queue_create.3 dispatch_semaphore_create.3 dispatch_source_create.3 dispatch_time.3\"\n\nPRIVATE_PAGES=\"dispatch_benchmark.3\"\n\ncp ${BASE_PAGES} $DSTROOT/usr/share/man/man3\ncp ${PRIVATE_PAGES} $DSTROOT/usr/local/share/man/man3\n\n# Make hard links (lots of hard links)\n\ncd $DSTROOT/usr/local/share/man/man3\nln -f dispatch_benchmark.3 dispatch_benchmark_f.3\nchown ${INSTALL_OWNER}:${INSTALL_GROUP} $PRIVATE_PAGES\nchmod $INSTALL_MODE_FLAG $PRIVATE_PAGES\n\n\ncd $DSTROOT/usr/share/man/man3\n\nchown ${INSTALL_OWNER}:${INSTALL_GROUP} $BASE_PAGES\nchmod $INSTALL_MODE_FLAG $BASE_PAGES\n\nln -f dispatch_after.3 dispatch_after_f.3\nln -f dispatch_apply.3 dispatch_apply_f.3\nln -f dispatch_once.3 dispatch_once_f.3\n\nfor m in dispatch_async_f dispatch_sync dispatch_sync_f; do\n\tln -f dispatch_async.3 ${m}.3\ndone\n\nfor m in dispatch_group_enter dispatch_group_leave dispatch_group_wait dispatch_group_async dispatch_group_async_f dispatch_group_notify dispatch_group_notify_f; do\n\tln -f dispatch_group_create.3 ${m}.3\ndone\n\nfor m in dispatch_retain dispatch_release dispatch_suspend dispatch_resume dispatch_get_context dispatch_set_context dispatch_set_finalizer_f; do\n\tln -f dispatch_object.3 ${m}.3\ndone\n\nfor m in dispatch_semaphore_signal dispatch_semaphore_wait; do\n\tln -f dispatch_semaphore_create.3 ${m}.3\ndone\n\nfor m in dispatch_get_current_queue dispatch_main dispatch_get_main_queue dispatch_get_global_queue dispatch_queue_get_label dispatch_set_target_queue; do\n\tln -f dispatch_queue_create.3 ${m}.3\ndone\n\nfor m in dispatch_source_set_event_handler dispatch_source_set_event_handler_f dispatch_source_set_cancel_handler dispatch_source_set_cancel_handler_f dispatch_source_cancel dispatch_source_testcancel dispatch_source_get_handle dispatch_source_get_mask dispatch_source_get_data dispatch_source_merge_data dispatch_source_set_timer; do\n\tln -f dispatch_source_create.3 ${m}.3\ndone\n\nln -f dispatch_time.3 dispatch_walltime.3"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + D2AAC044055464E500DB518D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC7BEDA40E8361E600161930 /* protocol.defs in Sources */, + FC7BED990E8361E600161930 /* queue.c in Sources */, + FC7BEDA60E8361E600161930 /* shims.c in Sources */, + 2EC9C9B80E8809EF00E2499A /* legacy.c in Sources */, + 721F5CCF0F15553500FF03A6 /* semaphore.c in Sources */, + 96DF70BE0F38FE3C0074BD99 /* once.c in Sources */, + 9676A0E10F3E755D00713ADB /* apply.c in Sources */, + 9661E56B0F3E7DDF00749F3E /* object.c in Sources */, + 965CD6350F3E806200D4E28D /* benchmark.c in Sources */, + 96A8AA870F41E7A400CD570B /* source.c in Sources */, + 96032E4B0F5CC8C700241C5F /* time.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB91ED08733DB70010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + EXECUTABLE_PREFIX = ""; + GCC_CW_ASM_SYNTAX = NO; + GCC_ENABLE_CPP_EXCEPTIONS = NO; + GCC_ENABLE_CPP_RTTI = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_PREPROCESSOR_DEFINITIONS = "__DARWIN_NON_CANCELABLE=1"; + GENERATE_MASTER_OBJECT_FILE = NO; + INSTALL_PATH = /usr/local/lib/system; + LINK_WITH_STANDARD_LIBRARIES = NO; + OTHER_CFLAGS = ( + "-fno-unwind-tables", + "-fno-exceptions", + "-I$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + "-fdiagnostics-show-option", + "-fsched-interblock", + "-freorder-blocks", + "-Xarch_x86_64", + "-momit-leaf-frame-pointer", + "-Xarch_i386", + "-momit-leaf-frame-pointer", + ); + OTHER_CFLAGS_debug = "-O0 -fstack-protector -fno-inline -DDISPATCH_DEBUG=1"; + PRIVATE_HEADERS_FOLDER_PATH = /usr/local/include/dispatch; + PRODUCT_NAME = libdispatch; + PUBLIC_HEADERS_FOLDER_PATH = /usr/include/dispatch; + SEPARATE_STRIP = NO; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = __; + }; + name = Release; + }; + 1DEB91F108733DB70010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ARCHS = "$(ARCHS_STANDARD_32_64_BIT)"; + BUILD_VARIANTS = ( + normal, + debug, + profile, + ); + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_PASCAL_STRINGS = NO; + GCC_OPTIMIZATION_LEVEL = s; + GCC_STRICT_ALIASING = YES; + GCC_SYMBOLS_PRIVATE_EXTERN = YES; + GCC_TREAT_WARNINGS_AS_ERRORS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_MISSING_NEWLINE = YES; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + LINK_WITH_STANDARD_LIBRARIES = YES; + ONLY_ACTIVE_ARCH = NO; + OTHER_CFLAGS = ( + "-I$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + "-fdiagnostics-show-option", + "-fsched-interblock", + "-freorder-blocks", + "-Xarch_x86_64", + "-momit-leaf-frame-pointer", + "-Xarch_i386", + "-momit-leaf-frame-pointer", + ); + OTHER_CFLAGS_debug = "-O0 -fstack-protector -fno-inline -DDISPATCH_DEBUG=1"; + PREBINDING = NO; + STRIP_INSTALLED_PRODUCT = NO; + WARNING_CFLAGS = ( + "-Wall", + "-Wextra", + "-Waggregate-return", + "-Wfloat-equal", + "-Wpacked", + "-Wmissing-declarations", + "-Wstrict-overflow=4", + "-Wstrict-aliasing=2", + ); + }; + name = Release; + }; + 721EB47A0F69D26F00845379 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + 7276FCBB0EB10E0F00F7F487 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB91EB08733DB70010E9CD /* Build configuration list for PBXNativeTarget "libdispatch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91ED08733DB70010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB91EF08733DB70010E9CD /* Build configuration list for PBXProject "libdispatch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91F108733DB70010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 721EB4850F69D2A600845379 /* Build configuration list for PBXLegacyTarget "testbots" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 721EB47A0F69D26F00845379 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7276FCC80EB10E2300F7F487 /* Build configuration list for PBXLegacyTarget "test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7276FCBB0EB10E0F00F7F487 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/man/dispatch.3 b/man/dispatch.3 new file mode 100644 index 0000000..c361863 --- /dev/null +++ b/man/dispatch.3 @@ -0,0 +1,38 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch 3 +.Os Darwin +.Sh NAME +.Nm dispatch +.Nd the dispatch framework +.Sh SYNOPSIS +.Fd #include +.Sh DESCRIPTION +The dispatch framework allows blocks to be scheduled for asynchronous and +concurrent execution via the core functions described in +.Xr dispatch_async 3 and +.Xr dispatch_apply 3 . +.Pp +Dispatch queues are the basic units of organization of blocks. Several queues +are created by default, and applications may create additional queues for their +own use. See +.Xr dispatch_queue_create 3 +for more information. +.Pp +Dispatch groups allow applications to track the progress of blocks submitted to +queues and take action when the blocks complete. See +.Xr dispatch_group_create 3 +for more information. +.Pp +The dispatch framework also provides functions to monitor underlying system +events and automatically submit event handler blocks to dispatch queues. +.Sh SEE ALSO +.Xr dispatch_async 3 , +.Xr dispatch_object 3 , +.Xr dispatch_queue_create 3 , +.Xr dispatch_group_create 3 , +.Xr dispatch_source_create 3 , +.Xr dispatch_benchmark 3 , +.Xr dispatch_time 3 , +.Xr dispatch_apply 3 , +.Xr dispatch_once 3 . diff --git a/man/dispatch_after.3 b/man/dispatch_after.3 new file mode 100644 index 0000000..404aefb --- /dev/null +++ b/man/dispatch_after.3 @@ -0,0 +1,57 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_after 3 +.Os Darwin +.Sh NAME +.Nm dispatch_after +.Nd schedule blocks for deferred execution +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fo dispatch_after +.Fa "dispatch_time_t when" "dispatch_queue_t queue" "void (^block)(void)" +.Fc +.Ft void +.Fo dispatch_after_f +.Fa "dispatch_time_t when" "dispatch_queue_t queue" "void *context" "void (^function)(void *)" +.Fc +.Sh DESCRIPTION +The +.Fn dispatch_after +function submits the +.Fa block +to the given +.Fa queue +at the time specified by the +.Fa when +parameter. +The +.Fa when +parameter is a value created by +.Fn dispatch_time +or +.Fn dispatch_walltime . +.Pp +For a more detailed description about submitting blocks to queues, see +.Xr dispatch_async 3 . +.Sh CAVEATS +Specifying +.Vt DISPATCH_TIME_NOW +as the +.Fa when +parameter +is supported, but is not as efficient as calling +.Fn dispatch_async . +The result of passing +.Vt DISPATCH_TIME_FOREVER +as the +.Fa when +parameter is undefined. +.Sh FUNDAMENTALS +The +.Fn dispatch_after +function is a wrapper around +.Fn dispatch_after_f . +.Sh SEE ALSO +.Xr dispatch_async 3 , +.Xr dispatch_time 3 diff --git a/man/dispatch_api.3 b/man/dispatch_api.3 new file mode 100644 index 0000000..a39fa64 --- /dev/null +++ b/man/dispatch_api.3 @@ -0,0 +1,44 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_api 3 +.Os Darwin +.Sh NAME +.Nm dispatch_api +.Nd Designing API using dispatch +.Sh DESCRIPTION +The following is a brief summary of some of the common design patterns to +consider when designing and implementing API in terms of dispatch queues +and blocks. +.Pp +A general recommendation is to allow both a callback block and target dispatch +queue to be specified. This gives the application the greatest flexibility in +handling asynchronous events. +.Pp +It's also recommended that interfaces take only a single block as the last +parameter. This is both for consistency across projects, as well as the visual +aesthetics of multiline blocks that are declared inline. The dispatch queue to +which the block will be submitted should immediately precede the block argument +(second-to-last argument). For example: +.Pp +.Bd -literal -offset indent +read_async(file, callback_queue, ^{ + printf("received callback.\n"); +}); +.Ed +.Pp +When function pointer alternatives to interfaces that take blocks are provided, +the argument order of the function signature should be identical to the block +variant; with the exception that the block argument is replaced with a context +pointer, and a new last parameter is added, which is the function to call. +.Pp +The function based callback should pass the context pointer as the first +argument, and the subsequent arguments should be identical to the block based +variant (albeit offset by one in order). +.Pp +It is also important to use consistent naming. The dispatch API, for example, +uses the suffix "_f" for function based variants. +.Pp +.Sh SEE ALSO +.Xr dispatch 3 , +.Xr dispatch_async 3 , +.Xr dispatch_queue_create 3 diff --git a/man/dispatch_apply.3 b/man/dispatch_apply.3 new file mode 100644 index 0000000..48fb395 --- /dev/null +++ b/man/dispatch_apply.3 @@ -0,0 +1,80 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_apply 3 +.Os Darwin +.Sh NAME +.Nm dispatch_apply +.Nd schedule blocks for iterative execution +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fo dispatch_apply +.Fa "size_t iterations" "dispatch_queue_t queue" "void (^block)(size_t)" +.Fc +.Ft void +.Fo dispatch_apply_f +.Fa "size_t iterations" "dispatch_queue_t queue" "void *context" "void (*function)(void *, size_t)" +.Fc +.Sh DESCRIPTION +The +.Fn dispatch_apply +function provides data-level concurrency through a "for (;;)" loop like primitive: +.Bd -literal +dispatch_queue_t the_queue = dispatch_get_concurrent_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT); +size_t iterations = 10; + +// 'idx' is zero indexed, just like: +// for (idx = 0; idx < iterations; idx++) + +dispatch_apply(iterations, the_queue, ^(size_t idx) { + printf("%zu\\n", idx); +}); +.Ed +.Pp +Like a "for (;;)" loop, the +.Fn dispatch_apply +function is synchronous. +If asynchronous behavior is desired, please wrap the call to +.Fn dispatch_apply +with a call to +.Fn dispatch_async +against another queue. +.Pp +Sometimes, when the block passed to +.Fn dispatch_apply +is simple, the use of striding can tune performance. +Calculating the optimal stride is best left to experimentation. +Start with a stride of one and work upwards until the desired performance is +achieved (perhaps using a power of two search): +.Bd -literal +#define STRIDE 3 + +dispatch_apply(count / STRIDE, queue, ^(size_t idx) { + size_t j = idx * STRIDE; + size_t j_stop = j + STRIDE; + do { + printf("%zu\\n", j++); + } while (j < j_stop); +}); + +size_t i; +for (i = count - (count % STRIDE); i < count; i++) { + printf("%zu\\n", i); +} +.Ed +.Sh FUNDAMENTALS +Conceptually, +.Fn dispatch_apply +is a convenient wrapper around +.Fn dispatch_async +and a semaphore to wait for completion. +In practice, the dispatch library optimizes this function. +.Pp +The +.Fn dispatch_apply +function is a wrapper around +.Fn dispatch_apply_f . +.Sh SEE ALSO +.Xr dispatch_async 3 , +.Xr dispatch_semaphore_create 3 , +.Xr dispatch_queue_create 3 diff --git a/man/dispatch_async.3 b/man/dispatch_async.3 new file mode 100644 index 0000000..4b874fb --- /dev/null +++ b/man/dispatch_async.3 @@ -0,0 +1,234 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_async 3 +.Os Darwin +.Sh NAME +.Nm dispatch_async , +.Nm dispatch_sync +.Nd schedule blocks for execution +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fo dispatch_async +.Fa "dispatch_queue_t queue" "void (^block)(void)" +.Fc +.Ft void +.Fo dispatch_sync +.Fa "dispatch_queue_t queue" "void (^block)(void)" +.Fc +.Ft void +.Fo dispatch_async_f +.Fa "dispatch_queue_t queue" "void *context" "void (*function)(void *)" +.Fc +.Ft void +.Fo dispatch_sync_f +.Fa "dispatch_queue_t queue" "void *context" "void (*function)(void *)" +.Fc +.Sh DESCRIPTION +The +.Fn dispatch_async +and +.Fn dispatch_sync +functions schedule blocks for concurrent execution within the +.Xr dispatch 3 +framework. Blocks are submitted to a queue which dictates the policy for their +execution. See +.Xr dispatch_queue_create 3 +for more information about creating dispatch queues. +.Pp +These functions support efficient temporal synchronization, background +concurrency and data-level concurrency. These same functions can also be used +for efficient notification of the completion of asynchronous blocks (a.k.a. +callbacks). +.Sh TEMPORAL SYNCHRONIZATION +Synchronization is often required when multiple threads of execution access +shared data concurrently. The simplest form of synchronization is +mutual-exclusion (a lock), whereby different subsystems execute concurrently +until a shared critical section is entered. In the +.Xr pthread 3 +family of procedures, temporal synchronization is accomplished like so: +.Bd -literal -offset indent +int r = pthread_mutex_lock(&my_lock); +assert(r == 0); + +// critical section + +r = pthread_mutex_unlock(&my_lock); +assert(r == 0); +.Ed +.Pp +The +.Fn dispatch_sync +function may be used with a serial queue to accomplish the same style of +synchronization. For example: +.Bd -literal -offset indent +dispatch_sync(my_queue, ^{ + // critical section +}); +.Ed +.Pp +In addition to providing a more concise expression of synchronization, this +approach is less error prone as the critical section cannot be accidentally +left without restoring the queue to a reentrant state. +.Pp +The +.Fn dispatch_async +function may be used to implement deferred critical sections when the result +of the block is not needed locally. Deferred critical sections have the same +synchronization properties as the above code, but are non-blocking and +therefore more efficient to perform. For example: +.Bd -literal +dispatch_async(my_queue, ^{ + // critical section +}); +.Ed +.Sh BACKGROUND CONCURRENCY +.The +.Fn dispatch_async +function may be used to execute trivial backgound tasks on a global concurrent +queue. For example: +.Bd -literal +dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{ + // background operation +}); +.Ed +.Pp +This approach is an efficient replacement for +.Xr pthread_create 3 . +.Sh COMPLETION CALLBACKS +Completion callbacks can be accomplished via nested calls to the +.Fn dispatch_async +function. It is important to remember to retain the destination queue before the +first call to +.Fn dispatch_async , +and to release that queue at the end of the completion callback to ensure the +destination queue is not deallocated while the completion callback is pending. +For example: +.Bd -literal +void +async_read(object_t obj, + void *where, size_t bytes, + dispatch_queue_t destination_queue, + void (^reply_block)(ssize_t r, int err)) +{ + // There are better ways of doing async I/O. + // This is just an example of nested blocks. + + dispatch_retain(destination_queue); + + dispatch_async(obj->queue, ^{ + ssize_t r = read(obj->fd, where, bytes); + int err = errno; + + dispatch_async(destination_queue, ^{ + reply_block(r, err); + }); + dispatch_release(destination_queue); + }); +} +.Ed +.Sh RECURSIVE LOCKS +While +.Fn dispatch_sync +can replace a lock, it cannot replace a recursive lock. Unlike locks, queues +support both asynchronous and synchrnous operations, and those operations are +ordered by definition. A recursive call to +.Fn dispatch_sync +causes a simple deadlock as the currently executing block waits for the next +block to complete, but the next block will not start until the currently +running block completes. +.Pp +As the dispatch framework was designed, we studied recursive locks. We found +that the vast majority of recursive locks are deployed retroactively when +ill-defined lock hierarchies are discovered. As a consequence, the adoption of +recursive locks often mutates obvious bugs into obscure ones. This study also +revealed an insight: if reentrancy is unavoidable, then reader/writer locks are +preferable to recursive locks. Disciplined use of reader/writer locks enable +reentrancy only when reentrancy is safe (the "read" side of the lock). +.Pp +Nevertheless, if it is absolutely necessary, what follows is an imperfect way of +implementing recursive locks using the dispatch framework: +.Bd -literal +void +sloppy_lock(object_t object, void (^block)(void)) +{ + if (object->owner == pthread_self()) { + return block(); + } + dispatch_sync(object->queue, ^{ + object->owner = pthread_self(); + block(); + object->owner = NULL; + }); +} +.Ed +.Pp +The above example does not solve the case where queue A runs on thread X which +calls +.Fn dispatch_sync +against queue B which runs on thread Y which recursively calls +.Fn dispatch_sync +against queue A, which deadlocks both examples. This is bug-for-bug compatible +with nontrivial pthread usage. In fact, nontrivial reentrancy is impossible to +support in recursive locks once the ultimate level of reentrancy is deployed +(IPC or RPC). +.Sh IMPLIED REFERENCES +Synchronous functions within the dispatch framework hold an implied reference +on the target queue. In other words, the synchronous function borrows the +reference of the calling function (this is valid because the calling function +is blocked waiting for the result of the synchronous function, and therefore +cannot modify the reference count of the target queue until after the +synchronous function has returned). +For example: +.Bd -literal +queue = dispatch_queue_create("com.example.queue", NULL); +assert(queue); +dispatch_sync(queue, ^{ + do_something(); + //dispatch_release(queue); // NOT SAFE -- dispatch_sync() is still using 'queue' +}); +dispatch_release(queue); // SAFELY balanced outside of the block provided to dispatch_sync() +.Ed +.Pp +This is in contrast to asynchronous functions which must retain both the block +and target queue for the duration of the asynchronous operation (as the calling +function may immediately release its interest in these objects). +.Sh FUNDAMENTALS +Conceptually, +.Fn dispatch_sync +is a convenient wrapper around +.Fn dispatch_async +with the addition of a semaphore to wait for completion of the block, and a +wrapper around the block to signal its completion. See +.Xr dispatch_semaphore_create 3 +for more information about dispatch semaphores. The actual implementation of the +.Fn dispatch_sync +function may be optimized and differ from the above description. +.Pp +The +.Fn dispatch_async +function is a wrapper around +.Fn dispatch_async_f . +The application-defined +.Fa context +parameter is passed to the +.Fa function +when it is invoked on the target +.Fa queue . +.Pp +The +.Fn dispatch_sync +function is a wrapper around +.Fn dispatch_sync_f . +The application-defined +.Fa context +parameter is passed to the +.Fa function +when it is invoked on the target +.Fa queue . +.Pp +.Sh SEE ALSO +.Xr dispatch_once 3 , +.Xr dispatch_queue_create 3 , +.Xr dispatch_semaphore_create 3 , +.Xr dispatch_apply 3 diff --git a/man/dispatch_benchmark.3 b/man/dispatch_benchmark.3 new file mode 100644 index 0000000..0890aff --- /dev/null +++ b/man/dispatch_benchmark.3 @@ -0,0 +1,55 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_benchmark 3 +.Os Darwin +.Sh NAME +.Nm dispatch_benchmark +.Nd Measures block execution time +.Sh SYNOPSIS +.Fd #include +.Ft uint64_t +.Fo dispatch_benchmark +.Fa "size_t count" "void (^block)(void)" +.Fc +.Sh DESCRIPTION +The +.Fn dispatch_benchmark +function executes the given +.Fa block +multiple times according to the +.Fa count +variable and then returns the average number of nanoseconds per execution. +This function is for debugging and performance analysis work. +For the best +results, pass a high count value to +.Fn dispatch_benchmark . +When benchmarking concurrent code, please compare the +serial version of the code against the concurrent version, and compare the +concurrent version on different classes of hardware. +Please look for inflection +points with various data sets and keep the following facts in mind: +.Pp +.Bl -bullet -offset indent -compact +.It +Code bound by computational bandwidth may be inferred by proportional +changes in performance as concurrency is increased. +.It +Code bound by memory bandwidth may be inferred by negligible changes in +performance as concurrency is increased. +.It +Code bound by critical sections may be inferred by retrograde changes in +performance as concurrency is increased. +.Bl -bullet -offset indent -compact +.It +Intentional: locks, mutexes, and condition variables. +.It +Accidental: unrelated and frequently modified data on the same cache-line. +.El +.El +.Sh RETURN VALUE +The +.Fn dispatch_benchmark +function returns the average number of nanoseconds the given block takes to +execute. +.Sh SEE ALSO +.Xr dispatch 3 diff --git a/man/dispatch_group_create.3 b/man/dispatch_group_create.3 new file mode 100644 index 0000000..5cca4ca --- /dev/null +++ b/man/dispatch_group_create.3 @@ -0,0 +1,149 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_group_create 3 +.Os Darwin +.Sh NAME +.Nm dispatch_group_create , +.Nm dispatch_group_async , +.Nm dispatch_group_wait , +.Nm dispatch_group_notify +.Nd group blocks submitted to queues +.Sh SYNOPSIS +.Fd #include +.Ft dispatch_group_t +.Fo dispatch_group_create +.Fa void +.Fc +.Ft void +.Fo dispatch_group_enter +.Fa "dispatch_group_t group" +.Fc +.Ft void +.Fo dispatch_group_leave +.Fa "dispatch_group_t group" +.Fc +.Ft long +.Fo dispatch_group_wait +.Fa "dispatch_group_t group" "dispatch_time_t timeout" +.Fc +.Ft void +.Fo dispatch_group_notify +.Fa "dispatch_group_t group" "dispatch_queue_t queue" "void (^block)(void)" +.Fc +.Ft void +.Fo dispatch_group_notify_f +.Fa "dispatch_group_t group" "dispatch_queue_t queue" "void *context" "void (*function)(void *)" +.Fc +.Ft void +.Fo dispatch_group_async +.Fa "dispatch_group_t group" "dispatch_queue_t queue" "void (^block)(void)" +.Fc +.Ft void +.Fo dispatch_group_async_f +.Fa "dispatch_group_t group" "dispatch_queue_t queue" "void *context" "void (*function)(void *)" +.Fc +.Sh DESCRIPTION +A dispatch group is an association of one or more blocks submitted to dispatch +queues for asynchronous invocation. +Applications may use dispatch groups to +wait for the completion of blocks associated with the group. +.Pp +The +.Fn dispatch_group_create +function returns a new and empty dispatch group. +.Pp +The +.Fn dispatch_group_enter +and +.Fn dispatch_group_leave +functions update the number of blocks running within a group. +.Pp +The +.Fn dispatch_group_wait +function waits until all blocks associated with the +.Fa group +have completed, or until the specified +.Fa timeout +has elapsed. +If the +.Fa group +becomes empty within the specified amount of time, the function will return zero +indicating success. Otherwise, a non-zero return code will be returned. +When +.Va DISPATCH_TIME_FOREVER +is passed as the +.Fa timeout , +calls to this function will wait an unlimited amount of time until the group +becomes empty and the return value is always zero. +.Pp +The +.Fn dispatch_group_notify +function provides asynchronous notification of the completion of the blocks +associated with the +.Fa group +by submitting the +.Fa block +to the specified +.Fa queue +once all blocks associated with the +.Fa group +have completed. +The system holds a reference to the dispatch group while an asynchronous +notification is pending, therefore it is valid to release the +.Fa group +after setting a notification block. +The group will be empty at the time the notification block is submitted to the +target queue. The group may either be released with +.Fn dispatch_release +or reused for additional operations. +.Pp +The +.Fn dispatch_group_async +convenience function behaves like so: +.Bd -literal +void +dispatch_group_async(dispatch_group_t group, dispatch_queue_t queue, dispatch_block_t block) +{ + dispatch_retain(group); + dispatch_group_enter(group); + dispatch_async(queue, ^{ + block(); + dispatch_group_leave(group); + dispatch_release(group); + }); +} +.Ed +.Sh RETURN VALUE +The +.Fn dispatch_group_create +function returns NULL on failure and non-NULL on success. +.Pp +The +.Fn dispatch_group_wait +function returns zero upon success and non-zero after the timeout expires. +If the timeout is +.Va DISPATCH_TIME_FOREVER , +then +.Fn dispatch_group_wait +waits forever and always returns zero. +.Sh MEMORY MODEL +Dispatch groups are retained and released via calls to +.Fn dispatch_retain +and +.Fn dispatch_release . +.Sh FUNDAMENTALS +The +.Fn dispatch_group_async +and +.Fn dispatch_group_notify +functions are wrappers around +.Fn dispatch_group_async_f +and +.Fn dispatch_group_notify_f +respectively. +.Sh SEE ALSO +.Xr dispatch_object 3 , +.Xr dispatch_async 3 , +.Xr dispatch_time 3 , +.Xr dispatch_queue_create 3 , +.Xr dispatch_semaphore_create 3 diff --git a/man/dispatch_object.3 b/man/dispatch_object.3 new file mode 100644 index 0000000..b60831a --- /dev/null +++ b/man/dispatch_object.3 @@ -0,0 +1,99 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_object 3 +.Os Darwin +.Sh NAME +.Nm dispatch_object +.Nd General manipulation of dispatch objects +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fo dispatch_retain +.Fa "dispatch_object_t object" +.Fc +.Ft void +.Fo dispatch_release +.Fa "dispatch_object_t object" +.Fc +.Ft void +.Fo dispatch_suspend +.Fa "dispatch_object_t object" +.Fc +.Ft void +.Fo dispatch_resume +.Fa "dispatch_object_t object" +.Fc +.Ft "void *" +.Fo dispatch_get_context +.Fa "dispatch_object_t object" +.Fc +.Ft void +.Fo dispatch_set_context +.Fa "dispatch_object_t object" +.Fa "void *context" +.Fc +.Ft void +.Fo dispatch_set_finalizer_f +.Fa "dispatch_object_t object" +.Fa "dispatch_function_t finalizer" +.Fc +.Sh DESCRIPTION +Dispatch objects share functions for coordinating memory management, suspension, +cancellation and context pointers. While all dispatch objects are retainable, +not all objects support suspension, context pointers or finalizers (currently +only queues and sources support these additional interfaces). +.Sh MEMORY MANGEMENT +Objects returned by creation functions in the dispatch framework may be +uniformly retained and released with the functions +.Fn dispatch_retain +and +.Fn dispatch_release +respectively. +.Pp +The dispatch framework does not guarantee that any given client has the last or +only reference to a given object. Objects may be retained internally by the +system. +.Sh SUSPENSION +The invocation of blocks on dispatch queues or dispatch sources may be suspended +or resumed with the functions +.Fn dispatch_suspend +and +.Fn dispatch_resume +respectively. +The dispatch framework always checks the suspension status before executing a +block, but such changes never affect a block during execution (non-preemptive). +Therefore the suspension of an object is asynchronous, unless it is performed +from the context of the target queue for the given object. +The result of suspending or resuming an object that is not a dispatch queue or +a dispatch source is undefined. +.Pp +.Em Important : +suspension applies to all aspects of the dispatch object life cycle, including +the finalizer function and cancellation handler. Therefore it is important to +balance calls to +.Fn dispatch_suspend +and +.Fn dispatch_resume +such that the dispatch object is fully resumed when the last reference is +released. The result of releasing all references to a dispatch object while in +a suspended state is undefined. +.Sh CONTEXT POINTERS +Dispatch queues and sources support supplemental context pointers. The value of +the context point may be retrieved and updated with +.Fn dispatch_get_context +and +.Fn dispatch_set_context +respectively. +The +.Fn dispatch_set_finalizer_f +specifies an optional per-object finalizer function to that is invoked +asynchronously when the last reference to the object is released. This gives the +application an opportunity to free the context data associated with the object. +.Pp +The result of getting or setting the context of an object that is not a +dispatch queue or a dispatch source is undefined. +.Sh SEE ALSO +.Xr dispatch_group_create 3 , +.Xr dispatch_queue_create 3 , +.Xr dispatch_semaphore_create 3 , +.Xr dispatch_source_create 3 diff --git a/man/dispatch_once.3 b/man/dispatch_once.3 new file mode 100644 index 0000000..da21896 --- /dev/null +++ b/man/dispatch_once.3 @@ -0,0 +1,44 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_once 3 +.Os Darwin +.Sh NAME +.Nm dispatch_once +.Nd execute a block only once +.Sh SYNOPSIS +.Fd #include +.Ft void +.Fo dispatch_once +.Fa "dispatch_once_t *predicate" "void (^block)(void)" +.Fc +.Ft void +.Fo dispatch_once_f +.Fa "dispatch_once_t *predicate" "void *context" "void (*function)(void *)" +.Fc +.Sh DESCRIPTION +The +.Fn dispatch_once +function provides a simple and efficient mechanism to run an initializer +exactly once, similar to +.Xr pthread_once 3 . +Well designed code hides the use of lazy initialization. +For example: +.Bd -literal +FILE *getlogfile(void) +{ + static dispatch_once_t pred; + static FILE *logfile; + + dispatch_once(&pred, ^{ + logfile = fopen(MY_LOG_FILE, "a"); + }); + + return logfile; +} +.Ed +.Pp +.Sh FUNDAMENTALS +The +.Fn dispatch_once +function is a wrapper around +.Fn dispatch_once_f . diff --git a/man/dispatch_queue_create.3 b/man/dispatch_queue_create.3 new file mode 100644 index 0000000..d11c1c1 --- /dev/null +++ b/man/dispatch_queue_create.3 @@ -0,0 +1,318 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2008 +.Dt dispatch_queue_create 3 +.Os Darwin +.Sh NAME +.Nm dispatch_queue_create , +.Nm dispatch_queue_get_label , +.Nm dispatch_get_current_queue , +.Nm dispatch_get_global_queue , +.Nm dispatch_get_main_queue , +.Nm dispatch_main , +.Nm dispatch_set_target_queue +.Nd where blocks are scheduled for execution +.Sh SYNOPSIS +.Fd #include +.Ft dispatch_queue_t +.Fo dispatch_queue_create +.Fa "const char *label" "dispatch_queue_attr_t attr" +.Fc +.Ft "const char *" +.Fo dispatch_queue_get_label +.Fa "dispatch_queue_t queue" +.Fc +.Ft dispatch_queue_t +.Fo dispatch_get_current_queue +.Fa void +.Fc +.Ft dispatch_queue_t +.Fo dispatch_get_global_queue +.Fa "long priority" +.Fa "unsigned long flags" +.Fc +.Ft dispatch_queue_t +.Fo dispatch_get_main_queue +.Fa void +.Fc +.Ft void +.Fo dispatch_main +.Fa void +.Fc +.Ft void +.Fo dispatch_set_target_queue +.Fa "dispatch_object_t object" +.Fa "dispatch_queue_t target" +.Fc +.Sh DESCRIPTION +Queues are the fundamental mechanism for scheduling blocks for execution within +the +.Xr dispatch 3 +framework. +.Pp +All blocks submitted to dispatch queues are dequeued in FIFO order. +By default, queues created with +.Fn dispatch_queue_create +wait for the previously dequeued block to complete before dequeuing the next +block. This FIFO completion behavior is sometimes simply described as a "serial queue." +Queues are not bound to any specific thread of execution and blocks submitted +to independent queues may execute concurrently. +Queues, like all dispatch objects, are reference counted and newly created +queues have a reference count of one. +.Pp +The optional +.Fa label +argument is used to describe the purpose of the queue and is useful during +debugging and performance analysis. By convention, clients should pass a +reverse DNS style label. +If a label is provided, it is copied. If a label is not provided, then +.Fn dispatch_queue_get_label +returns an empty C string. +For example: +.Pp +.Bd -literal +my_queue = dispatch_queue_create("com.example.subsystem.taskXYZ", NULL); +.Ed +.Pp +The +.Fa attr +argument is reserved for future use and must be NULL. +.Pp +Queues may be temporarily suspended and resumed with the functions +.Fn dispatch_suspend +and +.Fn dispatch_resume +respectively. Suspension is checked prior to block execution and is +.Em not +preemptive. +.Sh MAIN QUEUE +The dispatch framework provides a default serial queue for the application to use. +This queue is accessed via +.Fn dispatch_get_main_queue . +Programs must call +.Fn dispatch_main +at the end of +.Fn main +in order to process blocks submitted to the main queue. (See the compatibility +section for exceptions.) +.Sh GLOBAL CONCURRENT QUEUES +Unlike the main queue or queues allocated with +.Fn dispatch_queue_create , +the global concurrent queues schedule blocks as soon as threads become +available (non-FIFO completion order). The global concurrent queues represent +three priority bands: +.Bl -bullet -compact -offset indent +.It +DISPATCH_QUEUE_PRIORITY_HIGH +.It +DISPATCH_QUEUE_PRIORITY_DEFAULT +.It +DISPATCH_QUEUE_PRIORITY_LOW +.El +.Pp +Blocks submitted to the high priority global queue will be invoked before those +submitted to the default or low priority global queues. Blocks submitted to the +low priority global queue will only be invoked if no blocks are pending on the +default or high priority queues. +.Pp +.Sh RETURN VALUES +The +.Fn dispatch_queue_create +function returns NULL on failure. +.Pp +The +.Fn dispatch_queue_get_label +function always returns a valid C string. An empty C string is returned if the +.Fa label +was NULL creation time. +.Pp +The +.Fn dispatch_get_main_queue +function returns the default main queue. +.Pp +The +.Fn dispatch_get_current_queue +function always returns a valid queue. When called from within a block submitted +to a dispatch queue, that queue will be returned. If this function is called from +the main thread before +.Fn dispatch_main +is called, then the result of +.Fn dispatch_get_main_queue +is returned. Otherwise, the result of +.Fo dispatch_get_global_queue +.Fa DISPATCH_QUEUE_PRIORITY_DEFAULT +.Fa 0 +.Fc +will be returned in all other cases. +.Pp +The +.Fn dispatch_main +function never returns. +.Sh TARGET QUEUE +The +.Fn dispatch_set_target_queue +function updates the target queue of the given dispatch object. The target +queue of an object is responsible for processing the object. Currently only +dispatch queues and dispatch sources are supported by this function. The result +of using +.Fn dispatch_set_target_queue +with any other dispatch object type is undefined. +.Pp +The new target queue is retained by the given object before the previous target +queue is released. The new target queue will take effect between block +executions, but not in the middle of any existing block executions +(non-preemptive). +.Pp +The priority of a dispatch queue is inherited by its target queue. +In order to change the priority of a queue created with +.Fn dispatch_queue_create , +use the +.Fn dispatch_get_global_queue +function to obtain a target queue of the desired priority. The +.Fa flags +argument is reserved for future use and must be zero. Passing any value other +than zero may result in a +.Vt NULL +return value. +.Pp +The target queue of a dispatch source specifies where its event handler and +cancellation handler blocks will be submitted. See +.Xr dispatch_source_create 3 +for more information about dispatch sources. +.Pp +The result of passing the main queue or a global concurrent queue to the first +argument of +.Fn dispatch_set_target_queue +is undefined. +.Pp +Directly or indirectly setting the target queue of a dispatch queue to itself is undefined. +.Sh CAVEATS +Code cannot make any assumptions about the queue returned by +.Fn dispatch_get_current_queue . +The returned queue may have arbitrary policies that may surprise code that tries +to schedule work with the queue. The list of policies includes, but is not +limited to, queue width (i.e. serial vs. concurrent), scheduling priority, +security credential or filesystem configuration. Therefore, +.Fn dispatch_get_current_queue +.Em MUST +only be used for identity tests or debugging. +.Sh COMPATIBILITY +Cocoa applications need not call +.Fn dispatch_main . +Blocks submitted to the main queue will be executed as part of the "common modes" +of the application's main NSRunLoop or CFRunLoop. +.Pp +The dispatch framework is a pure C level API. As a result, it does not catch +exceptions generated by higher level languages such as Objective-C or C++. +Applications +.Em MUST +catch all exceptions before returning from a block submitted to a dispatch +queue; otherwise the internal data structures of the dispatch framework will be +left in an inconsistent state. +.Pp +The dispatch framework manages the relationship between dispatch queues and +threads of execution. As a result, applications +.Em MUST NOT +delete or mutate objects that they did not create. The following interfaces +.Em MUST NOT +be called by blocks submitted to a dispatch queue: +.Bl -bullet -offset indent +.It +.Fn pthread_cancel +.It +.Fn pthread_detach +.It +.Fn pthread_join +.It +.Fn pthread_kill +.It +.Fn pthread_exit +.El +.Pp +Applications +.Em MAY +call the following interfaces from a block submitted to a dispatch queue if +and only if they restore the thread to its original state before returning: +.Bl -bullet -offset indent +.It +.Fn pthread_setcancelstate +.It +.Fn pthread_setcanceltype +.It +.Fn pthread_setschedparam +.It +.Fn pthread_sigmask +.It +.Fn pthread_setugid_np +.It +.Fn pthread_chdir +.It +.Fn pthread_fchdir +.El +.Pp +Applications +.Em MUST NOT +rely on the following interfaces returning predictable results between +invocations of blocks submitted to a dispatch queue: +.Bl -bullet -offset indent +.It +.Fn pthread_self +.It +.Fn pthread_getschedparam +.It +.Fn pthread_get_stacksize_np +.It +.Fn pthread_get_stackaddr_np +.It +.Fn pthread_mach_thread_np +.It +.Fn pthread_from_mach_thread_np +.El +.Pp +While the result of +.Fn pthread_self +may change between invocations of blocks, the value will not change during the +execution of any single block. Because the underlying thread may change beteween +block invocations on a single queue, using per-thread data as an out-of-band +return value is error prone. In other words, the result of calling +.Fn pthread_setspecific +and +.Fn pthread_getspecific +is well defined within a signle block, but not across multiple blocks. Also, +one cannot make any assumptions about when the destructor passed to +.Fn pthread_key_create +is called. The destructor may be called between the invocation of blocks on +the same queue, or during the idle state of a process. +.Pp +The following example code correctly handles per-thread return values: +.Bd -literal -offset indent +__block int r; +__block int e; +dispatch_sync(queue, ^{ + r = kill(1, 0); + // Copy the per-thread return value to the callee thread + e = errno; +}); +printf("kill(1,0) returned %d and errno %d\n", r, e); +.Ed +.Pp +Note that in the above example +.Va errno +is a per-thread variable and must be copied out explicitly as the block may be +invoked on different thread of execution than the caller. Another example of +per-thread data that would need to be copied is the use of +.Fn getpwnam +instead of +.Fn getpwnam_r . +.Pp +As an optimization, +.Fn dispatch_sync +invokes the block on the current thread when possible. In this case, the thread +specific data such as +.Va errno +may persist from the block until back to the caller. Great care should be taken +not to accidentally rely on this side-effect. +.Pp +.Sh SEE ALSO +.Xr dispatch_object 3 , +.Xr dispatch_async 3 , +.Xr dispatch_source_create 3 diff --git a/man/dispatch_semaphore_create.3 b/man/dispatch_semaphore_create.3 new file mode 100644 index 0000000..1250642 --- /dev/null +++ b/man/dispatch_semaphore_create.3 @@ -0,0 +1,114 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_semaphore_create 3 +.Os Darwin +.Sh NAME +.Nm dispatch_semaphore_create , +.Nm dispatch_semaphore_signal , +.Nm dispatch_semaphore_wait +.Nd synchronized counting semaphore +.Sh SYNOPSIS +.Fd #include +.Ft dispatch_semaphore_t +.Fo dispatch_semaphore_create +.Fa "long count" +.Fc +.Ft long +.Fo dispatch_semaphore_signal +.Fa "dispatch_semaphore_t semaphore" +.Fc +.Ft long +.Fo dispatch_semaphore_wait +.Fa "dispatch_semaphore_t semaphore" "dispatch_time_t timeout" +.Fc +.Sh DESCRIPTION +Dispatch semaphores are used to synchronize threads. +The +.Fa timeout +parameter is creatable with the +.Xr dispatch_time 3 +or +.Xr dispatch_walltime 3 +functions. +.Sh COMPLETION SYNCHRONIZATION +If the +.Fa count +parameter is equal to zero, then the semaphore is useful for synchronizing completion of work. +For example: +.Bd -literal -offset indent +sema = dispatch_semaphore_create(0); + +dispatch_async(queue, ^{ + foo(); + dispatch_semaphore_signal(sema); +}); + +bar(); + +dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); +.Ed +.Sh FINITE RESOURCE POOL +If the +.Fa count +parameter is greater than zero, then the semaphore is useful for managing a finite pool of resources. +For example, a library that wants to limit Unix descriptor usage: +.Bd -literal -offset indent +sema = dispatch_semaphore_create(getdtablesize() / 4); +.Ed +.Pp +At each Unix FD allocation: +.Bd -literal -offset indent +dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); +fd = open("/etc/services", O_RDONLY); +.Ed +.Pp +When each FD is closed: +.Bd -literal -offset indent +close(fd); +dispatch_semaphore_signal(sema); +.Ed +.Sh RETURN VALUES +The +.Fn dispatch_semaphore_create +function returns NULL if no memory is available or if the +.Fa count +parameter is less than zero. +.Pp +The +.Fn dispatch_semaphore_signal +function returns non-zero when a thread is woken. +Otherwise, zero is returned. +.Pp +The +.Fn dispatch_semaphore_wait +function returns zero upon success and non-zero after the timeout expires. If the timeout is DISPATCH_TIME_FOREVER, then +.Fn dispatch_semaphore_wait +waits forever and always returns zero. +.Sh MEMORY MODEL +Dispatch semaphores are retained and released via calls to +.Fn dispatch_retain +and +.Fn dispatch_release . +.Sh CAVEATS +Dispatch semaphores are strict counting semaphores. +In other words, dispatch semaphores do not saturate at any particular value. +Saturation can be achieved through atomic compare-and-swap logic. +What follows is a saturating binary semaphore: +.Bd -literal +void +saturating_semaphore_signal(dispatch_semaphore_t dsema, int *sent) +{ + if (__sync_bool_compare_and_swap(sent, 0, 1)) { + dispatch_semaphore_signal(dsema); + } +} + +void +saturating_semaphore_wait(dispatch_semaphore_t dsema, int *sent) +{ + *sent = 0; + dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER); +} +.Ed +.Sh SEE ALSO +.Xr dispatch_object 3 diff --git a/man/dispatch_source_create.3 b/man/dispatch_source_create.3 new file mode 100644 index 0000000..0a38cd2 --- /dev/null +++ b/man/dispatch_source_create.3 @@ -0,0 +1,456 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_source_create 3 +.Os Darwin +.Sh NAME +.Nm dispatch_source_create +.Nd dispatch event sources +.Sh SYNOPSIS +.Fd #include +.Ft dispatch_source_t +.Fo dispatch_source_create +.Fa "dispatch_source_type_t type" +.Fa "uintptr_t handle" +.Fa "unsigned long mask" +.Fa "dispatch_queue_t queue" +.Fc +.Ft void +.Fo dispatch_source_set_event_handler +.Fa "dispatch_source_t source" +.Fa "void (^block)(void)" +.Fc +.Ft void +.Fo dispatch_source_set_event_handler_f +.Fa "dispatch_source_t source" +.Fa "void (*function)(void *)" +.Fc +.Ft void +.Fo dispatch_source_set_cancel_handler +.Fa "dispatch_source_t source" +.Fa "void (^block)(void)" +.Fc +.Ft void +.Fo dispatch_source_set_cancel_handler_f +.Fa "dispatch_source_t source" +.Fa "void (*function)(void *)" +.Fc +.Ft void +.Fo dispatch_source_cancel +.Fa "dispatch_source_t source" +.Fc +.Ft void +.Fo dispatch_source_testcancel +.Fa "dispatch_source_t source" +.Fc +.Ft uintptr_t +.Fo dispatch_source_get_handle +.Fa "dispatch_source_t source" +.Fc +.Ft "unsigned long" +.Fo dispatch_source_get_mask +.Fa "dispatch_source_t source" +.Fc +.Ft "unsigned long" +.Fo dispatch_source_get_data +.Fa "dispatch_source_t source" +.Fc +.Ft void +.Fo dispatch_source_merge_data +.Fa "dispatch_source_t source" +.Fa "unsigned long data" +.Fc +.Ft void +.Fo dispatch_source_set_timer +.Fa "dispatch_source_t source" +.Fa "dispatch_time_t start" +.Fa "uint64_t interval" +.Fa "uint64_t leeway" +.Fc +.Sh DESCRIPTION +Dispatch event sources may be used to monitor a variety of system objects and +events including file descriptors, mach ports, processes, virtual filesystem +nodes, signal delivery and timers. +.Pp +When a state change occurs, the dispatch source will submit its event handler +block to its target queue. +.Pp +The +.Fn dispatch_source_create +function creates a new dispatch source object that may be retained and released +with calls to +.Fn dispatch_retain +and +.Fn dispatch_release +respectively. Newly created sources are created in a suspended state. After the +source has been configured by setting an event handler, cancellation handler, +context, etc., the source must be activated by a call to +.Fn dispatch_resume +before any events will be delivered. +.Pp +Dispatch sources may be one of the following types: +.Bl -bullet -compact -offset indent +.It +DISPATCH_SOURCE_TYPE_DATA_ADD +.It +DISPATCH_SOURCE_TYPE_DATA_OR +.It +DISPATCH_SOURCE_TYPE_MACH_SEND +.It +DISPATCH_SOURCE_TYPE_MACH_RECV +.It +DISPATCH_SOURCE_TYPE_PROC +.It +DISPATCH_SOURCE_TYPE_READ +.It +DISPATCH_SOURCE_TYPE_SIGNAL +.It +DISPATCH_SOURCE_TYPE_TIMER +.It +DISPATCH_SOURCE_TYPE_VNODE +.It +DISPATCH_SOURCE_TYPE_WRITE +.El +.Pp +The +.Fa handle +and +.Fa mask +arguments to +.Fn dispatch_source_create +and the return values of the +.Fn dispatch_source_get_handle , +.Fn dispatch_source_get_mask , +and +.Fn dispatch_source_get_data +functions should be interpreted according to the type of the dispatch source. +.Pp +The +.Fn dispatch_source_get_handle +function +returns the underlying handle to the dispatch source (i.e. file descriptor, +mach port, process identifer, etc.). The result of this function may be cast +directly to the underlying type. +.Pp +The +.Fn dispatch_source_get_mask +function +returns the set of flags that were specified at source creation time via the +.Fa mask +argument. +.Pp +The +.Fn dispatch_source_get_data +function returns the currently pending data for the dispatch source. +This function should only be called from within the source's event handler. +The result of calling this function from any other context is undefined. +.Pp +The +.Fn dispatch_source_merge_data +function is intended for use with the +.Vt DISPATCH_SOURCE_TYPE_DATA_ADD +and +.Vt DISPATCH_SOURCE_TYPE_DATA_OR +source types. The result of using this function with any other source type is +undefined. Calling this function will atomically add or logical OR the data +into the source's data, and trigger the delivery of the source's event handler. +.Pp +.Sh SOURCE EVENT HANDLERS +In order to receive events from the dispatch source, an event handler should be +specified via +.Fn dispatch_source_set_event_handler . +The event handler block is submitted to the source's target queue when the state +of the underlying system handle changes, or when an event occurs. +.Pp +Dispatch sources may be suspended or resumed independently of their target +queues using +.Fn dispatch_suspend +and +.Fn dispatch_resume +on the dispatch source directly. The data describing events which occur while a +source is suspended are coalesced and delivered once the source is resumed. +.Pp +The +.Fa handler +block +need not be reentrant safe, as it is not resubmitted to the target +.Fa queue +until any prior invocation for that dispatch source has completed. +When the hander is set, the dispatch source will perform a +.Fn Block_copy +on the +.Fa handler +block. +.Pp +.Sh CANCELLATION +The +.Fn dispatch_source_cancel +function asynchronously cancels the dispatch source, preventing any further +invocation of its event handler block. Cancellation does not interrupt a +currently executing handler block (non-preemptive). +.Pp +The +.Fn dispatch_source_testcancel +function may be used to determine whether the specified source has been +canceled. A non-zero value will be returned if the source is canceled. +.Pp +When a dispatch source is canceled its optional cancellation handler will be +submitted to its target queue. The cancellation handler may be specified via +.Fn dispatch_source_set_cancel_handler . +This cancellation handler is invoked only once, and only as a direct consequence +of calling +.Fn dispatch_source_cancel . +.Pp +.Em Important: +a cancellation handler is required for file descriptor and mach port based +sources in order to safely close the descriptor or destroy the port. Closing the +descriptor or port before the cancellation handler has run may result in a race +condition: if a new descriptor is allocated with the same value as the recently +cosed descriptor while the source's event handler is still running, the event +handler may read/write data to the wrong descriptor. +.Pp +.Sh DISPATCH SOURCE TYPES +The following section contains a summary of supported dispatch event types and +the interpretation of their parameters and returned data. +.Pp +.Vt DISPATCH_SOURCE_TYPE_DATA_ADD , +.Vt DISPATCH_SOURCE_TYPE_DATA_OR +.Pp +Sources of this type allow applications to manually trigger the source's event +handler via a call to +.Fn dispatch_source_merge_data . +The data will be merged with the source's pending data via an atomic add or +logic OR (based on the source's type), and the event handler block will be +submitted to the source's target queue. The +.Fa mask +and +.Fa data +are application defined. These sources have no +.Fa handle +and zero should be used. +.Pp +.Vt DISPATCH_SOURCE_TYPE_MACH_SEND +.Pp +Sources of this type monitor a mach port with a send right for state changes. +The +.Fa handle +is the mach port (mach_port_t) to monitor and the +.Fa mask +may be: +.Bl -tag -width "XXDISPATCH_PROC_SIGNAL" -compact -offset indent +.It \(bu DISPATCH_MACH_SEND_DEAD +The port's corresponding receive right has been destroyed +.El +.Pp +The data returned by +.Fn dispatch_source_get_data +indicates which of the events in the +.Fa mask +were observed. +.Pp +.Vt DISPATCH_SOURCE_TYPE_MACH_RECV +.Pp +Sources of this type monitor a mach port with a receive right for state changes. +The +.Fa handle +is the mach port (mach_port_t) to monitor and the +.Fa mask +is unused and should be zero. +The event handler block will be submitted to the target queue when a message +on the mach port is waiting to be received. +.Pp +.Vt DISPATCH_SOURCE_TYPE_PROC +.Pp +Sources of this type monitor processes for state changes. +The +.Fa handle +is the process identifier (pid_t) of the process to monitor and the +.Fa mask +may be one or more of the following: +.Bl -tag -width "XXDISPATCH_PROC_SIGNAL" -compact -offset indent +.It \(bu DISPATCH_PROC_EXIT +The process has exited and is available to +.Xr wait 2 . +.It \(bu DISPATCH_PROC_FORK +The process has created one or more child processes. +.It \(bu DISPATCH_PROC_EXEC +The process has become another executable image via a call to +.Xr execve 2 +or +.Xr posix_spawn 2 . +.It \(bu DISPATCH_PROC_REAP +The process status has been collected by its parent process via +.Xr wait 2 . +.It \(bu DISPATCH_PROC_SIGNAL +A signal was delivered to the process. +.El +.Pp +The data returned by +.Fn dispatch_source_get_data +indicates which of the events in the +.Fa mask +were observed. +.Pp +.Vt DISPATCH_SOURCE_TYPE_READ +.Pp +Sources of this type monitor file descriptors for pending data. +The +.Fa handle +is the file descriptor (int) to monitor and the +.Fa mask +is unused and should be zero. +.Pp +The data returned by +.Fn dispatch_source_get_data +is an estimated number of bytes available to be read from the descriptor. This +estimate should be treated as a suggested +.Em minimum +read buffer size. There are no guarantees that a complete read of this size +will be performed. +.Pp +Users of this source type are strongly encouraged to perform non-blocking I/O +and handle any truncated reads or error conditions that may occur. See +.Xr fnctl 2 +for additional information about setting the +.Vt O_NONBLOCK +flag on a file descriptor. +.Pp +.Vt DISPATCH_SOURCE_TYPE_SIGNAL +.Pp +Sources of this type monitor signals delivered to the current process. The +.Fa handle +is the signal number to monitor (int) and the +.Fa mask +is unused and should be zero. +.Pp +The data returned by +.Fn dispatch_source_get_data +is the number of signals received since the last invocation of the event handler +block. +.Pp +Unlike signal handlers specified via +.Fn sigaction , +the execution of the event handler block does not interrupt the current thread +of execution; therefore the handler block is not limited to the use of signal +safe interfaces defined in +.Xr sigaction 2 . +Furthermore, multiple observers of a given signal are supported; thus allowing +applications and libraries to cooperate safely. However, a dispatch source +.Em does not +install a signal handler or otherwise alter the behavior of signal delivery. +Therefore, applications must ignore or at least catch any signal that terminates +a process by default. For example, near the top of +.Fn main : +.Bd -literal -offset ident +signal(SIGTERM, SIG_IGN); +.Ed +.Pp +.Vt DISPATCH_SOURCE_TYPE_TIMER +.Pp +Sources of this type periodically submit the event handler block to the target +queue on an interval specified by +.Fn dispatch_source_set_timer . +The +.Fa handle +and +.Fa mask +arguments are unused and should be zero. +.Pp +A best effort attempt is made to submit the event handler block to the target +queue at the specified time; however, actual invocation may occur at a later +time. +.Pp +The data returned by +.Fn dispatch_source_get_data +is the number of times the timer has fired since the last invocation of the +event handler block. +.Pp +The function +.Fn dispatch_source_set_timer +takes as an argument the +.Fa start +time of the timer (initial fire time) represented as a +.Vt dispatch_time_t . +The timer dispatch source will use the same clock as the function used to +create this value. (See +.Xr dispatch_time 3 +for more information.) The +.Fa interval , +in nanoseconds, specifies the period at which the timer should repeat. All +timers will repeat indefinitely until +.Fn dispatch_source_cancel +is called. The +.Fa leeway , +in nanoseconds, is a hint to the system that it may defer the timer in order to +align with other system activity for improved system performance or reduced +power consumption. (For example, an application might perform a periodic task +every 5 minutes with a leeway of up to 30 seconds.) Note that some latency is +to be expected for all timers even when a value of zero is used. +.Pp +.Em Note : +Under the C language, untyped numbers default to the +.Vt int +type. This can lead to truncation bugs when arithmetic operations with other +numbers are expected to generate a +.Vt uint64_t +sized result. When in doubt, use +.Vt ull +as a suffix. For example: +.Bd -literal -offset indent +3ull * NSEC_PER_SEC +.Ed +.Pp +.Vt DISPATCH_SOURCE_TYPE_VNODE +.Pp +Sources of this type monitor the virtual filesystem nodes for state changes. +The +.Fa handle +is a file descriptor (int) referencing the node to monitor, and +the +.Fa mask +may be one or more of the following: +.Bl -tag -width "XXDISPATCH_VNODE_ATTRIB" -compact -offset indent +.It \(bu DISPATCH_VNODE_DELETE +The referenced node was removed from the filesystem namespace via +.Xr unlink 2 . +.It \(bu DISPATCH_VNODE_WRITE +A write to the referenced file occurred +.It \(bu DISPATCH_VNODE_EXTEND +The referenced file was extended +.It \(bu DISPATCH_VNODE_ATTRIB +The metadata attributes of the referenced node have changed +.It \(bu DISPATCH_VNODE_LINK +The link count on the referenced node has changed +.It \(bu DISPATCH_VNODE_RENAME +The referenced node was renamed +.It \(bu DISPATCH_VNODE_REVOKE +Access to the referenced node was revoked via +.Xr revoke 2 +or the underlying fileystem was unmounted. +.El +.Pp +The data returned by +.Fn dispatch_source_get_data +indicates which of the events in the +.Fa mask +were observed. +.Pp +.Vt DISPATCH_SOURCE_TYPE_WRITE +.Pp +Sources of this type monitor file descriptors for available write buffer space. +The +.Fa handle +is the file descriptor (int) to monitor and the +.Fa mask +is unused and should be zero. +.Pp +Users of this source type are strongly encouraged to perform non-blocking I/O +and handle any truncated reads or error conditions that may occur. See +.Xr fnctl 2 +for additional information about setting the +.Vt O_NONBLOCK +flag on a file descriptor. +.Pp +.Sh SEE ALSO +.Xr dispatch 3 , +.Xr dispatch_object 3 , +.Xr dispatch_queue_create 3 diff --git a/man/dispatch_time.3 b/man/dispatch_time.3 new file mode 100644 index 0000000..06d78e8 --- /dev/null +++ b/man/dispatch_time.3 @@ -0,0 +1,110 @@ +.\" Copyright (c) 2008-2009 Apple Inc. All rights reserved. +.Dd May 1, 2009 +.Dt dispatch_time 3 +.Os Darwin +.Sh NAME +.Nm dispatch_time , +.Nm dispatch_walltime +.Nd Calculate temporal milestones +.Sh SYNOPSIS +.Fd #include +.Vt static const dispatch_time_t DISPATCH_TIME_NOW = 0 ; +.Vt static const dispatch_time_t DISPATCH_TIME_FOREVER = ~0ull ; +.Ft dispatch_time_t +.Fo dispatch_time +.Fa "dispatch_time_t base" "int64_t offset" +.Fc +.Ft dispatch_time_t +.Fo dispatch_walltime +.Fa "struct timespec *base" "int64_t offset" +.Fc +.Sh DESCRIPTION +The +.Fn dispatch_time +and +.Fn dispatch_walltime +functions provide a simple mechanism for expressing temporal milestones for use +with dispatch functions that need timeouts or operate on a schedule. +.Pp +The +.Fa dispatch_time_t +type is a semi-opaque integer, with only the special values +.Vt DISPATCH_TIME_NOW +and +.Vt DISPATCH_TIME_FOREVER +being externally defined. All other values are represented using an internal +format that is not safe for integer arithmetic or comparison. +The internal format is subject to change. +.Pp +The +.Fn dispatch_time +function returns a milestone relative to an existing milestone after adding +.Fa offset +nanoseconds. +If the +.Fa base +parameter maps internally to a wall clock, then the returned value is +relative to the wall clock. +Otherwise, if +.Fa base +is +.Vt DISPATCH_TIME_NOW , +then the the current time of the default host clock is used. +.Pp +The +.Fn dispatch_walltime +function is useful for creating a milestone relative to a fixed point in time +using the wall clock, as specified by the optional +.Fa base +parameter. If +.Fa base +is NULL, then the current time of the wall clock is used. +.Sh EDGE CONDITIONS +The +.Fn dispatch_time +and +.Fn dispatch_walltime +functions detect overflow and underflow conditions when applying the +.Fa offset +parameter. +.Pp +Overflow causes +.Vt DISPATCH_TIME_FOREVER +to be returned. When +.Fa base +is +.Vt DISPATCH_TIME_FOREVER , +then the +.Fa offset +parameter is ignored. +.Pp +Underflow causes the smallest representable value to be +returned for a given clock. +.Sh EXAMPLES +Create a milestone two seconds in the future: +.Bd -literal -offset indent +milestone = dispatch_time(DISPATCH_TIME_NOW, 2LL * NSEC_PER_SEC); +.Ed +.Pp +Create a milestone for use as an infinite timeout: +.Bd -literal -offset indent +milestone = DISPATCH_TIME_FOREVER; +.Ed +.Pp +Create a milestone on Tuesday, January 19, 2038: +.Bd -literal -offset indent +struct timespec ts; +ts.tv_sec = 0x7FFFFFFF; +ts.tv_nsec = 0; +milestone = dispatch_walltime(&ts, 0); +.Ed +.Sh RETURN VALUE +These functions return an abstract value for use with +.Fn dispatch_after , +.Fn dispatch_group_wait , +or +.Fn dispatch_semaphore_wait . +.Sh SEE ALSO +.Xr dispatch_after 3 , +.Xr dispatch_group_create 3 , +.Xr dispatch_semaphore_create 3 diff --git a/src/apply.c b/src/apply.c new file mode 100644 index 0000000..2c51eb2 --- /dev/null +++ b/src/apply.c @@ -0,0 +1,178 @@ +/* + * 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@ + */ +#include "internal.h" + +// We'd use __attribute__((aligned(x))), but it does not atually increase the +// alignment of stack variables. All we really need is the stack usage of the +// local thread to be sufficiently away to avoid cache-line contention with the +// busy 'da_index' variable. +// +// NOTE: 'char' arrays cause GCC to insert buffer overflow detection logic +struct dispatch_apply_s { + long _da_pad0[DISPATCH_CACHELINE_SIZE / sizeof(long)]; + void (*da_func)(void *, size_t); + void *da_ctxt; + size_t da_iterations; + size_t da_index; + uint32_t da_thr_cnt; + dispatch_semaphore_t da_sema; + long _da_pad1[DISPATCH_CACHELINE_SIZE / sizeof(long)]; +}; + +static void +_dispatch_apply2(void *_ctxt) +{ + struct dispatch_apply_s *da = _ctxt; + size_t const iter = da->da_iterations; + typeof(da->da_func) const func = da->da_func; + void *const ctxt = da->da_ctxt; + size_t idx; + + _dispatch_workitem_dec(); // this unit executes many items + + // Striding is the responsibility of the caller. + while (fastpath((idx = dispatch_atomic_inc(&da->da_index) - 1) < iter)) { + func(ctxt, idx); + _dispatch_workitem_inc(); + } + + if (dispatch_atomic_dec(&da->da_thr_cnt) == 0) { + dispatch_semaphore_signal(da->da_sema); + } +} + +static void +_dispatch_apply_serial(void *context) +{ + struct dispatch_apply_s *da = context; + size_t idx = 0; + + _dispatch_workitem_dec(); // this unit executes many items + do { + da->da_func(da->da_ctxt, idx); + _dispatch_workitem_inc(); + } while (++idx < da->da_iterations); +} + +#ifdef __BLOCKS__ +void +dispatch_apply(size_t iterations, dispatch_queue_t dq, void (^work)(size_t)) +{ + struct Block_basic *bb = (void *)work; + + dispatch_apply_f(iterations, dq, bb, (void *)bb->Block_invoke); +} +#endif + +// 256 threads should be good enough for the short to mid term +#define DISPATCH_APPLY_MAX_CPUS 256 + +DISPATCH_NOINLINE +void +dispatch_apply_f(size_t iterations, dispatch_queue_t dq, void *ctxt, void (*func)(void *, size_t)) +{ + struct dispatch_apply_dc_s { + DISPATCH_CONTINUATION_HEADER(dispatch_apply_dc_s); + } da_dc[DISPATCH_APPLY_MAX_CPUS]; + struct dispatch_apply_s da; + size_t i; + + da.da_func = func; + da.da_ctxt = ctxt; + da.da_iterations = iterations; + da.da_index = 0; + da.da_thr_cnt = _dispatch_hw_config.cc_max_active; + + if (da.da_thr_cnt > DISPATCH_APPLY_MAX_CPUS) { + da.da_thr_cnt = DISPATCH_APPLY_MAX_CPUS; + } + if (slowpath(iterations == 0)) { + return; + } + if (iterations < da.da_thr_cnt) { + da.da_thr_cnt = (uint32_t)iterations; + } + if (slowpath(dq->dq_width <= 2 || da.da_thr_cnt <= 1)) { + return dispatch_sync_f(dq, &da, _dispatch_apply_serial); + } + + for (i = 0; i < da.da_thr_cnt; i++) { + da_dc[i].do_vtable = NULL; + da_dc[i].do_next = &da_dc[i + 1]; + da_dc[i].dc_func = _dispatch_apply2; + da_dc[i].dc_ctxt = &da; + } + + da.da_sema = _dispatch_get_thread_semaphore(); + + // some queues are easy to borrow and some are not + if (slowpath(dq->do_targetq)) { + _dispatch_queue_push_list(dq, (void *)&da_dc[0], (void *)&da_dc[da.da_thr_cnt - 1]); + } else { + dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key); + // root queues are always concurrent and safe to borrow + _dispatch_queue_push_list(dq, (void *)&da_dc[1], (void *)&da_dc[da.da_thr_cnt - 1]); + _dispatch_thread_setspecific(dispatch_queue_key, dq); + // The first da_dc[] element was explicitly not pushed on to the queue. + // We need to either call it like so: + // da_dc[0].dc_func(da_dc[0].dc_ctxt); + // Or, given that we know the 'func' and 'ctxt', we can call it directly: + _dispatch_apply2(&da); + _dispatch_workitem_inc(); + _dispatch_thread_setspecific(dispatch_queue_key, old_dq); + } + dispatch_semaphore_wait(da.da_sema, DISPATCH_TIME_FOREVER); + _dispatch_put_thread_semaphore(da.da_sema); +} + +#if 0 +#ifdef __BLOCKS__ +void +dispatch_stride(size_t offset, size_t stride, size_t iterations, dispatch_queue_t dq, void (^work)(size_t)) +{ + struct Block_basic *bb = (void *)work; + dispatch_stride_f(offset, stride, iterations, dq, bb, (void *)bb->Block_invoke); +} +#endif + +DISPATCH_NOINLINE +void +dispatch_stride_f(size_t offset, size_t stride, size_t iterations, + dispatch_queue_t dq, void *ctxt, void (*func)(void *, size_t)) +{ + if (stride == 0) { + stride = 1; + } + dispatch_apply(iterations / stride, queue, ^(size_t idx) { + size_t i = idx * stride + offset; + size_t stop = i + stride; + do { + func(ctxt, i++); + } while (i < stop); + }); + + dispatch_sync(queue, ^{ + size_t i; + for (i = iterations - (iterations % stride); i < iterations; i++) { + func(ctxt, i + offset); + } + }); +} +#endif diff --git a/src/base.h b/src/base.h new file mode 100644 index 0000000..3799a9a --- /dev/null +++ b/src/base.h @@ -0,0 +1,113 @@ +/* + * 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@ + */ + +#ifndef __DISPATCH_BASE__ +#define __DISPATCH_BASE__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#endif + +#ifdef __cplusplus +/* + * Dispatch objects are NOT C++ objects. Nevertheless, we can at least keep C++ + * aware of type compatibility. + */ +typedef struct dispatch_object_s { +private: + dispatch_object_s(); + ~dispatch_object_s(); + dispatch_object_s(const dispatch_object_s &); + void operator=(const dispatch_object_s &); +} *dispatch_object_t; +#else +typedef union { + struct dispatch_object_s *_do; + struct dispatch_continuation_s *_dc; + struct dispatch_queue_s *_dq; + struct dispatch_queue_attr_s *_dqa; + struct dispatch_group_s *_dg; + struct dispatch_source_s *_ds; + struct dispatch_source_attr_s *_dsa; + struct dispatch_semaphore_s *_dsema; +} dispatch_object_t __attribute__((transparent_union)); +#endif + +typedef void (*dispatch_function_t)(void *); + +#ifdef __cplusplus +#define DISPATCH_DECL(name) typedef struct name##_s : public dispatch_object_s {} *name##_t; +#else +/*! @parseOnly */ +#define DISPATCH_DECL(name) typedef struct name##_s *name##_t; +#endif + +#ifdef __GNUC__ +#define DISPATCH_NORETURN __attribute__((__noreturn__)) +#define DISPATCH_NOTHROW __attribute__((__nothrow__)) +#define DISPATCH_NONNULL1 __attribute__((__nonnull__(1))) +#define DISPATCH_NONNULL2 __attribute__((__nonnull__(2))) +#define DISPATCH_NONNULL3 __attribute__((__nonnull__(3))) +#define DISPATCH_NONNULL4 __attribute__((__nonnull__(4))) +#define DISPATCH_NONNULL5 __attribute__((__nonnull__(5))) +#define DISPATCH_NONNULL6 __attribute__((__nonnull__(6))) +#define DISPATCH_NONNULL7 __attribute__((__nonnull__(7))) +#if __clang__ +// rdar://problem/6857843 +#define DISPATCH_NONNULL_ALL +#else +#define DISPATCH_NONNULL_ALL __attribute__((__nonnull__)) +#endif +#define DISPATCH_SENTINEL __attribute__((__sentinel__)) +#define DISPATCH_PURE __attribute__((__pure__)) +#define DISPATCH_WARN_RESULT __attribute__((__warn_unused_result__)) +#define DISPATCH_MALLOC __attribute__((__malloc__)) +#else +/*! @parseOnly */ +#define DISPATCH_NORETURN +/*! @parseOnly */ +#define DISPATCH_NOTHROW +/*! @parseOnly */ +#define DISPATCH_NONNULL1 +/*! @parseOnly */ +#define DISPATCH_NONNULL2 +/*! @parseOnly */ +#define DISPATCH_NONNULL3 +/*! @parseOnly */ +#define DISPATCH_NONNULL4 +/*! @parseOnly */ +#define DISPATCH_NONNULL5 +/*! @parseOnly */ +#define DISPATCH_NONNULL6 +/*! @parseOnly */ +#define DISPATCH_NONNULL7 +/*! @parseOnly */ +#define DISPATCH_NONNULL_ALL +/*! @parseOnly */ +#define DISPATCH_SENTINEL +/*! @parseOnly */ +#define DISPATCH_PURE +/*! @parseOnly */ +#define DISPATCH_WARN_RESULT +/*! @parseOnly */ +#define DISPATCH_MALLOC +#endif + +#endif diff --git a/src/benchmark.c b/src/benchmark.c new file mode 100644 index 0000000..fafe909 --- /dev/null +++ b/src/benchmark.c @@ -0,0 +1,114 @@ +/* + * 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@ + */ + +#include "internal.h" + + +struct __dispatch_benchmark_data_s { + mach_timebase_info_data_t tbi; + uint64_t loop_cost; + void (*func)(void *); + void *ctxt; + size_t count; +}; + +static void +_dispatch_benchmark_init(void *context) +{ + struct __dispatch_benchmark_data_s *bdata = context; + // try and simulate performance of real benchmark as much as possible + // keep 'f', 'c' and 'cnt' in registers + register void (*f)(void *) = bdata->func; + register void *c = bdata->ctxt; + register size_t cnt = bdata->count; + uint64_t start, delta; +#ifdef __LP64__ + __uint128_t lcost; +#else + long double lcost; +#endif + kern_return_t kr; + size_t i = 0; + + kr = mach_timebase_info(&bdata->tbi); + dispatch_assert_zero(kr); + + start = mach_absolute_time(); + do { + i++; + f(c); + } while (i < cnt); + delta = mach_absolute_time() - start; + + lcost = delta; + lcost *= bdata->tbi.numer; + lcost /= bdata->tbi.denom; + lcost /= cnt; + + bdata->loop_cost = lcost; +} + +#ifdef __BLOCKS__ +uint64_t +dispatch_benchmark(size_t count, void (^block)(void)) +{ + struct Block_basic *bb = (void *)block; + return dispatch_benchmark_f(count, block, (void *)bb->Block_invoke); +} +#endif + +uint64_t +dispatch_benchmark_f(size_t count, register void *ctxt, register void (*func)(void *)) +{ + static struct __dispatch_benchmark_data_s bdata = { + .func = (void *)dummy_function, + .count = 10000000ul, // ten million + }; + static dispatch_once_t pred; + uint64_t ns, start, delta; +#ifdef __LP64__ + __uint128_t conversion, big_denom; +#else + long double conversion, big_denom; +#endif + size_t i = 0; + + dispatch_once_f(&pred, &bdata, _dispatch_benchmark_init); + + if (slowpath(count == 0)) { + return 0; + } + + start = mach_absolute_time(); + do { + i++; + func(ctxt); + } while (i < count); + delta = mach_absolute_time() - start; + + conversion = delta; + conversion *= bdata.tbi.numer; + big_denom = bdata.tbi.denom; + big_denom *= count; + conversion /= big_denom; + ns = conversion; + + return ns - bdata.loop_cost; +} diff --git a/src/benchmark.h b/src/benchmark.h new file mode 100644 index 0000000..b77af45 --- /dev/null +++ b/src/benchmark.h @@ -0,0 +1,83 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_BENCHMARK__ +#define __DISPATCH_BENCHMARK__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +__BEGIN_DECLS + +/*! + * @function dispatch_benchmark + * + * @abstract + * Count the average number of cycles a given block takes to execute. + * + * @param count + * The number of times to serially execute the given block. + * + * @param block + * The block to execute. + * + * @result + * The approximate number of cycles the block takes to execute. + * + * @discussion + * This function is for debugging and performance analysis work. For the best + * results, pass a high count value to dispatch_benchmark(). When benchmarking + * concurrent code, please compare the serial version of the code against the + * concurrent version, and compare the concurrent version on different classes + * of hardware. Please look for inflection points with various data sets and + * keep the following facts in mind: + * + * 1) Code bound by computational bandwidth may be inferred by proportional + * changes in performance as concurrency is increased. + * 2) Code bound by memory bandwidth may be inferred by negligible changes in + * performance as concurrency is increased. + * 3) Code bound by critical sections may be inferred by retrograde changes in + * performance as concurrency is increased. + * 3a) Intentional: locks, mutexes, and condition variables. + * 3b) Accidental: unrelated and frequently modified data on the same cache-line. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL2 DISPATCH_NOTHROW +uint64_t +dispatch_benchmark(size_t count, void (^block)(void)); +#endif + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL3 DISPATCH_NOTHROW +uint64_t +dispatch_benchmark_f(size_t count, void *ctxt, void (*func)(void *)); + +__END_DECLS + +#endif diff --git a/src/dispatch.h b/src/dispatch.h new file mode 100644 index 0000000..95331d7 --- /dev/null +++ b/src/dispatch.h @@ -0,0 +1,52 @@ +/* + * 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@ + */ + +#ifndef __DISPATCH_PUBLIC__ +#define __DISPATCH_PUBLIC__ + +#include +#include +#include +#include +#include +#include + +#define DISPATCH_API_VERSION 20090501 + +#ifndef __DISPATCH_BUILDING_DISPATCH__ + +#ifndef __DISPATCH_INDIRECT__ +#define __DISPATCH_INDIRECT__ +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#undef __DISPATCH_INDIRECT__ + +#endif /* !__DISPATCH_BUILDING_DISPATCH__ */ + +#endif diff --git a/src/group.h b/src/group.h new file mode 100644 index 0000000..a282948 --- /dev/null +++ b/src/group.h @@ -0,0 +1,273 @@ +/* + * 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@ + */ + +#ifndef __DISPATCH_GROUP__ +#define __DISPATCH_GROUP__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +/*! + * @typedef dispatch_group_t + * @abstract + * A group of blocks submitted to queues for asynchronous invocation. + */ +DISPATCH_DECL(dispatch_group); + +__BEGIN_DECLS + +/*! + * @function dispatch_group_create + * + * @abstract + * Creates new group with which blocks may be associated. + * + * @discussion + * This function creates a new group with which blocks may be associated. + * The dispatch group may be used to wait for the completion of the blocks it + * references. The group object memory is freed with dispatch_release(). + * + * @result + * The newly created group, or NULL on failure. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_WARN_RESULT +dispatch_group_t +dispatch_group_create(void); + +/*! + * @function dispatch_group_async + * + * @abstract + * Submits a block to a dispatch queue and associates the block with the given + * dispatch group. + * + * @discussion + * Submits a block to a dispatch queue and associates the block with the given + * dispatch group. The dispatch group may be used to wait for the completion + * of the blocks it references. + * + * @param group + * A dispatch group to associate with the submitted block. + * The result of passing NULL in this parameter is undefined. + * + * @param queue + * The dispatch queue to which the block will be submitted for asynchronous + * invocation. + * + * @param block + * The block to perform asynchronously. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL +void +dispatch_group_async(dispatch_group_t group, + dispatch_queue_t queue, + dispatch_block_t block); +#endif /* __BLOCKS__ */ + +/*! + * @function dispatch_group_async_f + * + * @abstract + * Submits a function to a dispatch queue and associates the block with the + * given dispatch group. + * + * @discussion + * See dispatch_group_async() for details. + * + * @param group + * A dispatch group to associate with the submitted function. + * The result of passing NULL in this parameter is undefined. + * + * @param queue + * The dispatch queue to which the function will be submitted for asynchronous + * invocation. + * + * @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_group_async_f(). + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_NONNULL4 +void +dispatch_group_async_f(dispatch_group_t group, + dispatch_queue_t queue, + void *context, + dispatch_function_t work); + +/*! + * @function dispatch_group_wait + * + * @abstract + * Wait synchronously for the previously submitted blocks to complete; + * returns if the blocks have not completed within the specified timeout. + * + * @discussion + * This function waits for the completion of the blocks associated with the + * given dispatch group, and returns after all blocks have completed or when + * the specified timeout has elapsed. When a timeout occurs, the group is + * restored to its original state. + * + * This function will return immediately if there are no blocks associated + * with the dispatch group (i.e. the group is empty). + * + * The result of calling this function from mulitple threads simultaneously + * with the same dispatch group is undefined. + * + * After the successful return of this function, the dispatch group is empty. + * It may either be released with dispatch_release() or re-used for additional + * blocks. See dispatch_group_async() for more information. + * + * @param group + * The dispatch group to wait on. + * The result of passing NULL in this parameter is undefined. + * + * @param timeout + * When to timeout (see dispatch_time). As a convenience, there are the + * DISPATCH_TIME_NOW and DISPATCH_TIME_FOREVER constants. + * + * @result + * Returns zero on success (all blocks associated with the group completed + * within the specified timeout) or non-zero on error (i.e. timed out). + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL +long +dispatch_group_wait(dispatch_group_t group, dispatch_time_t timeout); + +/*! + * @function dispatch_group_notify + * + * @abstract + * Schedule a block to be submitted to a queue when a group of previously + * submitted blocks have completed. + * + * @discussion + * This function schedules a notification block to be submitted to the specified + * queue once all blocks associated with the dispatch group have completed. + * + * If no blocks are associated with the dispatch group (i.e. the group is empty) + * then the notification block will be submitted immediately. + * + * The group will be empty at the time the notification block is submitted to + * the target queue. The group may either be released with dispatch_release() + * or reused for additional operations. + * See dispatch_group_async() for more information. + * + * @param group + * The dispatch group to observe. + * The result of passing NULL in this parameter is undefined. + * + * @param queue + * The queue to which the supplied block will be submitted when the group + * completes. + * + * @param block + * The block to submit when the group completes. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL +void +dispatch_group_notify(dispatch_group_t group, + dispatch_queue_t queue, + dispatch_block_t block); +#endif /* __BLOCKS__ */ + +/*! + * @function dispatch_group_notify_f + * + * @abstract + * Schedule a function to be submitted to a queue when a group of previously + * submitted functions have completed. + * + * @discussion + * See dispatch_group_notify() for details. + * + * @param group + * The dispatch group to observe. + * 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_group_notify_f(). + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_NONNULL4 +void +dispatch_group_notify_f(dispatch_group_t group, + dispatch_queue_t queue, + void *context, + dispatch_function_t work); + +/*! + * @function dispatch_group_enter + * + * @abstract + * Manually indicate a block has entered the group + * + * @discussion + * Calling this function indicates another block has joined the group through + * a means other than dispatch_group_async(). Calls to this function must be + * balanced with dispatch_group_leave(). + * + * @param group + * The dispatch group to update. + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NOTHROW DISPATCH_NONNULL_ALL +void +dispatch_group_enter(dispatch_group_t group); + +/*! + * @function dispatch_group_leave + * + * @abstract + * Manually indicate a block in the group has completed + * + * @discussion + * Calling this function indicates block has completed and left the dispatch + * groupJ by a means other than dispatch_group_async(). + * + * @param group + * The dispatch group to update. + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NOTHROW DISPATCH_NONNULL_ALL +void +dispatch_group_leave(dispatch_group_t group); + +__END_DECLS + +#endif diff --git a/src/hw_shims.h b/src/hw_shims.h new file mode 100644 index 0000000..b99bf17 --- /dev/null +++ b/src/hw_shims.h @@ -0,0 +1,72 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_HW_SHIMS__ +#define __DISPATCH_HW_SHIMS__ + +/* x86 has a 64 byte cacheline */ +#define DISPATCH_CACHELINE_SIZE 64 +#define ROUND_UP_TO_CACHELINE_SIZE(x) (((x) + (DISPATCH_CACHELINE_SIZE - 1)) & ~(DISPATCH_CACHELINE_SIZE - 1)) +#define ROUND_UP_TO_VECTOR_SIZE(x) (((x) + 15) & ~15) + +#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) +// GCC generates suboptimal register pressure +// LLVM does better, but doesn't support tail calls +// 6248590 __sync_*() intrinsics force a gratuitous "lea" instruction, with resulting register pressure +#if 0 && defined(__i386__) || defined(__x86_64__) +#define dispatch_atomic_xchg(p, n) ({ typeof(*(p)) _r; asm("xchg %0, %1" : "=r" (_r) : "m" (*(p)), "0" (n)); _r; }) +#else +#define dispatch_atomic_xchg(p, n) __sync_lock_test_and_set((p), (n)) +#endif +#define dispatch_atomic_cmpxchg(p, o, n) __sync_bool_compare_and_swap((p), (o), (n)) +#define dispatch_atomic_inc(p) __sync_add_and_fetch((p), 1) +#define dispatch_atomic_dec(p) __sync_sub_and_fetch((p), 1) +#define dispatch_atomic_add(p, v) __sync_add_and_fetch((p), (v)) +#define dispatch_atomic_sub(p, v) __sync_sub_and_fetch((p), (v)) +#define dispatch_atomic_or(p, v) __sync_fetch_and_or((p), (v)) +#define dispatch_atomic_and(p, v) __sync_fetch_and_and((p), (v)) +#if defined(__i386__) || defined(__x86_64__) +/* GCC emits nothing for __sync_synchronize() on i386/x86_64. */ +#define dispatch_atomic_barrier() __asm__ __volatile__("mfence") +#else +#define dispatch_atomic_barrier() __sync_synchronize() +#endif +#else +#error "Please upgrade to GCC 4.2 or newer." +#endif + +#if defined(__i386__) || defined(__x86_64__) +#define _dispatch_hardware_pause() asm("pause") +#define _dispatch_debugger() asm("int3") +#else +#define _dispatch_hardware_pause() asm("") +#define _dispatch_debugger() asm("trap") +#endif +// really just a low level abort() +#define _dispatch_hardware_crash() __builtin_trap() + + +#endif diff --git a/src/internal.h b/src/internal.h new file mode 100644 index 0000000..d55540b --- /dev/null +++ b/src/internal.h @@ -0,0 +1,299 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_INTERNAL__ +#define __DISPATCH_INTERNAL__ + +#define __DISPATCH_BUILDING_DISPATCH__ +#define __DISPATCH_INDIRECT__ +#include "dispatch.h" +#include "base.h" +#include "time.h" +#include "queue.h" +#include "object.h" +#include "source.h" +#include "group.h" +#include "semaphore.h" +#include "once.h" +#include "benchmark.h" + +/* private.h uses #include_next and must be included last to avoid picking + * up installed headers. */ +#include "queue_private.h" +#include "source_private.h" +#include "private.h" +#include "legacy.h" +/* More #includes at EOF (dependent on the contents of internal.h) ... */ + +/* The "_debug" library build */ +#ifndef DISPATCH_DEBUG +#define DISPATCH_DEBUG 0 +#endif + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __BLOCKS__ +#include +#include +#endif /* __BLOCKS__ */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DISPATCH_NOINLINE __attribute__((noinline)) + +// workaround 6368156 +#ifdef NSEC_PER_SEC +#undef NSEC_PER_SEC +#endif +#ifdef USEC_PER_SEC +#undef USEC_PER_SEC +#endif +#ifdef NSEC_PER_USEC +#undef NSEC_PER_USEC +#endif +#define NSEC_PER_SEC 1000000000ull +#define USEC_PER_SEC 1000000ull +#define NSEC_PER_USEC 1000ull + +/* I wish we had __builtin_expect_range() */ +#define fastpath(x) ((typeof(x))__builtin_expect((long)(x), ~0l)) +#define slowpath(x) ((typeof(x))__builtin_expect((long)(x), 0l)) + +void _dispatch_bug(size_t line, long val) __attribute__((__noinline__)); +void _dispatch_abort(size_t line, long val) __attribute__((__noinline__,__noreturn__)); +void _dispatch_log(const char *msg, ...) __attribute__((__noinline__,__format__(printf,1,2))); +void _dispatch_logv(const char *msg, va_list) __attribute__((__noinline__,__format__(printf,1,0))); + +/* + * For reporting bugs within libdispatch when using the "_debug" version of the library. + */ +#define dispatch_assert(e) do { \ + if (__builtin_constant_p(e)) { \ + char __compile_time_assert__[(bool)(e) ? 1 : -1] __attribute__((unused)); \ + } else { \ + typeof(e) _e = fastpath(e); /* always eval 'e' */ \ + if (DISPATCH_DEBUG && !_e) { \ + _dispatch_abort(__LINE__, (long)_e); \ + } \ + } \ + } while (0) +/* A lot of API return zero upon success and not-zero on fail. Let's capture and log the non-zero value */ +#define dispatch_assert_zero(e) do { \ + if (__builtin_constant_p(e)) { \ + char __compile_time_assert__[(bool)(!(e)) ? 1 : -1] __attribute__((unused)); \ + } else { \ + typeof(e) _e = slowpath(e); /* always eval 'e' */ \ + if (DISPATCH_DEBUG && _e) { \ + _dispatch_abort(__LINE__, (long)_e); \ + } \ + } \ + } while (0) + +/* + * For reporting bugs or impedance mismatches between libdispatch and external subsystems. + * These do NOT abort(), and are always compiled into the product. + * + * In particular, we wrap all system-calls with assume() macros. + */ +#define dispatch_assume(e) ({ \ + typeof(e) _e = fastpath(e); /* always eval 'e' */ \ + if (!_e) { \ + if (__builtin_constant_p(e)) { \ + char __compile_time_assert__[(e) ? 1 : -1]; \ + (void)__compile_time_assert__; \ + } \ + _dispatch_bug(__LINE__, (long)_e); \ + } \ + _e; \ + }) +/* A lot of API return zero upon success and not-zero on fail. Let's capture and log the non-zero value */ +#define dispatch_assume_zero(e) ({ \ + typeof(e) _e = slowpath(e); /* always eval 'e' */ \ + if (_e) { \ + if (__builtin_constant_p(e)) { \ + char __compile_time_assert__[(e) ? -1 : 1]; \ + (void)__compile_time_assert__; \ + } \ + _dispatch_bug(__LINE__, (long)_e); \ + } \ + _e; \ + }) + +/* + * For reporting bugs in clients when using the "_debug" version of the library. + */ +#define dispatch_debug_assert(e, msg, args...) do { \ + if (__builtin_constant_p(e)) { \ + char __compile_time_assert__[(bool)(e) ? 1 : -1] __attribute__((unused)); \ + } else { \ + typeof(e) _e = fastpath(e); /* always eval 'e' */ \ + if (DISPATCH_DEBUG && !_e) { \ + _dispatch_log("%s() 0x%lx: " msg, __func__, (long)_e, ##args); \ + abort(); \ + } \ + } \ + } while (0) + + + +#ifdef __BLOCKS__ +dispatch_block_t _dispatch_Block_copy(dispatch_block_t block); +void _dispatch_call_block_and_release(void *block); +void _dispatch_call_block_and_release2(void *block, void *ctxt); +#endif /* __BLOCKS__ */ + +void dummy_function(void); +long dummy_function_r0(void); + + +/* Make sure the debug statments don't get too stale */ +#define _dispatch_debug(x, args...) \ +({ \ + if (DISPATCH_DEBUG) { \ + _dispatch_log("libdispatch: %u\t%p\t" x, __LINE__, _dispatch_thread_self(), ##args); \ + } \ +}) + + +#if DISPATCH_DEBUG +void dispatch_debug_kevents(struct kevent* kev, size_t count, const char* str); +#else +#define dispatch_debug_kevents(x, y, z) +#endif + +uint64_t _dispatch_get_nanoseconds(void); + +void _dispatch_source_drain_kevent(struct kevent *); + +dispatch_source_t +_dispatch_source_create2(dispatch_source_t ds, + dispatch_source_attr_t attr, + void *context, + dispatch_source_handler_function_t handler); + +void _dispatch_update_kq(const struct kevent *); +void _dispatch_run_timers(void); +// Returns howsoon with updated time value, or NULL if no timers active. +struct timespec *_dispatch_get_next_timer_fire(struct timespec *howsoon); + +dispatch_semaphore_t _dispatch_get_thread_semaphore(void); +void _dispatch_put_thread_semaphore(dispatch_semaphore_t); + +bool _dispatch_source_testcancel(dispatch_source_t); + +uint64_t _dispatch_timeout(dispatch_time_t when); + +__private_extern__ bool _dispatch_safe_fork; + +__private_extern__ struct _dispatch_hw_config_s { + uint32_t cc_max_active; + uint32_t cc_max_logical; + uint32_t cc_max_physical; +} _dispatch_hw_config; + +/* #includes dependent on internal.h */ +#include "object_internal.h" +#include "hw_shims.h" +#include "os_shims.h" +#include "queue_internal.h" +#include "semaphore_internal.h" +#include "source_internal.h" + +// MIG_REPLY_MISMATCH means either: +// 1) A signal handler is NOT using async-safe API. See the sigaction(2) man page for more info. +// 2) A hand crafted call to mach_msg*() screwed up. Use MIG. +#define DISPATCH_VERIFY_MIG(x) do { \ + if ((x) == MIG_REPLY_MISMATCH) { \ + __crashreporter_info__ = "MIG_REPLY_MISMATCH"; \ + _dispatch_hardware_crash(); \ + } \ + } while (0) + +#if defined(__x86_64__) || defined(__i386__) +// total hack to ensure that return register of a function is not trashed +#define DISPATCH_CRASH(x) do { \ + asm("mov %1, %0" : "=m" (__crashreporter_info__) : "c" ("BUG IN LIBDISPATCH: " x)); \ + _dispatch_hardware_crash(); \ + } while (0) + +#define DISPATCH_CLIENT_CRASH(x) do { \ + asm("mov %1, %0" : "=m" (__crashreporter_info__) : "c" ("BUG IN CLIENT OF LIBDISPATCH: " x)); \ + _dispatch_hardware_crash(); \ + } while (0) + +#else + +#define DISPATCH_CRASH(x) do { \ + __crashreporter_info__ = "BUG IN LIBDISPATCH: " x; \ + _dispatch_hardware_crash(); \ + } while (0) + +#define DISPATCH_CLIENT_CRASH(x) do { \ + __crashreporter_info__ = "BUG IN CLIENT OF LIBDISPATCH: " x; \ + _dispatch_hardware_crash(); \ + } while (0) + +#endif + + +#endif /* __DISPATCH_INTERNAL__ */ diff --git a/src/legacy.c b/src/legacy.c new file mode 100644 index 0000000..6232990 --- /dev/null +++ b/src/legacy.c @@ -0,0 +1,444 @@ +/* + * 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@ + */ + +#include "internal.h" +#include "legacy.h" + +/* + * LEGACY: This header file describles LEGACY interfaces to libdispatch from an + * earlier revision of the API. These interfaces WILL be removed in the future. + */ + +DISPATCH_PUBLIC_API DISPATCH_NONNULL1 DISPATCH_NONNULL2 +dispatch_item_t +LEGACY_dispatch_call(dispatch_queue_t, dispatch_legacy_block_t work, dispatch_legacy_block_t completion) +__asm__("_dispatch_call2"); + +DISPATCH_PUBLIC_API DISPATCH_PURE DISPATCH_WARN_RESULT +dispatch_queue_t +LEGACY_dispatch_queue_get_current(void) +__asm__("_dispatch_queue_get_current"); + +///////////////////////////////////////////////////////////////////////////// + +dispatch_queue_t +LEGACY_dispatch_queue_get_current(void) +{ + return _dispatch_queue_get_current(); +} + +dispatch_item_t +LEGACY_dispatch_call(dispatch_queue_t dq, + dispatch_legacy_block_t dispatch_block, + dispatch_legacy_block_t callback_block) +{ + dispatch_queue_t lq = _dispatch_queue_get_current() ?: dispatch_get_main_queue(); + dispatch_item_t di; + + di = dispatch_block ? calloc(1, ROUND_UP_TO_CACHELINE_SIZE(sizeof(*di))) : NULL; + + if (!di) { + return di; + } + + if (callback_block) { + dispatch_retain(lq); + } + + dispatch_async(dq, ^{ + dispatch_block(di); + + if (callback_block) { + dispatch_async(lq, ^{ + callback_block(di); + free(di); + dispatch_release(lq); + }); + } else { + free(di); + } + }); + + return di; +} + +sigset_t +dispatch_event_get_signals(dispatch_event_t de) +{ + sigset_t ret; + sigemptyset(&ret); + sigaddset(&ret, (int)dispatch_event_get_signal(de)); + return ret; +} + +void dispatch_cancel(dispatch_source_t ds) { dispatch_source_cancel(ds); } +long dispatch_testcancel(dispatch_source_t ds) { return dispatch_source_testcancel(ds); } + +void dispatch_queue_resume(dispatch_queue_t dq) { dispatch_resume(dq); } +void dispatch_queue_retain(dispatch_queue_t dq) { dispatch_retain(dq); } +void dispatch_queue_release(dispatch_queue_t dq) { dispatch_release(dq); } + +void dispatch_source_suspend(dispatch_source_t ds) { dispatch_suspend(ds); } +void dispatch_source_resume(dispatch_source_t ds) { dispatch_resume(ds); } +void dispatch_source_release(dispatch_source_t ds) { dispatch_release(ds); } + +void dispatch_source_attr_release(dispatch_source_attr_t attr) { dispatch_release(attr); } +void dispatch_queue_attr_release(dispatch_queue_attr_t attr) { dispatch_release(attr); } + +void *dispatch_queue_get_context(dispatch_queue_t dq) { return dispatch_get_context(dq); } +void dispatch_queue_set_context(dispatch_queue_t dq, void *context) { dispatch_set_context(dq, context); } + +void *dispatch_source_get_context(dispatch_source_t ds) { return dispatch_get_context(ds); } +void dispatch_source_set_context(dispatch_source_t ds, void *context) { dispatch_set_context(ds, context); } + +void dispatch_source_custom_trigger(dispatch_source_t ds) { dispatch_source_merge_data(ds, 1); } + +void +dispatch_source_trigger(dispatch_source_t ds, unsigned long val) +{ + dispatch_source_merge_data(ds, val); +} + +int dispatch_source_get_descriptor(dispatch_source_t ds) { return (int)dispatch_source_get_handle(ds); } + +pid_t dispatch_source_get_pid(dispatch_source_t ds) { return (pid_t)dispatch_source_get_handle(ds); } + +mach_port_t dispatch_source_get_machport(dispatch_source_t ds) { return (mach_port_t)dispatch_source_get_handle(ds); } + +uint64_t dispatch_source_get_flags(dispatch_source_t ds) { return dispatch_source_get_mask(ds); } + +dispatch_source_t dispatch_event_get_source(dispatch_event_t event) { return event; } + +long dispatch_event_get_error(dispatch_event_t event, long* error) { return dispatch_source_get_error(event, error); } + +uint64_t dispatch_event_get_flags(dispatch_event_t event) { return dispatch_source_get_data(event); } + +size_t dispatch_event_get_bytes_available(dispatch_event_t event) { return (size_t)dispatch_source_get_data(event); } + +unsigned long dispatch_event_get_count(dispatch_event_t event) { return (unsigned long)dispatch_source_get_data(event); } + +long dispatch_event_get_signal(dispatch_event_t event) { return (long)dispatch_source_get_handle(event); } + +dispatch_source_t +dispatch_source_custom_create( + unsigned long behavior, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_event_handler_t handler) { + return dispatch_source_data_create(behavior, attr, queue, handler); +} + +dispatch_source_t +dispatch_source_custom_create_f( + unsigned long behavior, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_event_handler_function_t handler) { + return dispatch_source_data_create_f(behavior, attr, queue, h_context, handler); +} + +#define _dispatch_source_call_block ((void *)-1) + + + +#ifdef __BLOCKS__ +dispatch_source_t +dispatch_source_timer_create(uint64_t flags, + uint64_t nanoseconds, + uint64_t leeway, + dispatch_source_attr_t attr, + dispatch_queue_t q, + dispatch_source_handler_t callback) +{ + return dispatch_source_timer_create_f(flags, nanoseconds, leeway, + attr, q, callback, _dispatch_source_call_block); +} +#endif + +dispatch_source_t +dispatch_source_timer_create_f(uint64_t timer_flags, + uint64_t nanoseconds, + uint64_t leeway, + dispatch_source_attr_t attr, + dispatch_queue_t q, + void *context, + dispatch_source_handler_function_t callback) +{ + dispatch_source_t ds; + dispatch_time_t start; + + // 6866347 - make sure nanoseconds won't overflow + if ((int64_t)nanoseconds < 0) { + nanoseconds = INT64_MAX; + } + + if (timer_flags & DISPATCH_TIMER_ONESHOT) { + timer_flags |= DISPATCH_TIMER_WALL_CLOCK; + } + if (timer_flags == (DISPATCH_TIMER_ABSOLUTE|DISPATCH_TIMER_WALL_CLOCK)) { + static const struct timespec t0; + start = dispatch_walltime(&t0, nanoseconds); + } else if (timer_flags & DISPATCH_TIMER_WALL_CLOCK) { + start = dispatch_walltime(DISPATCH_TIME_NOW, nanoseconds); + } else { + start = dispatch_time(DISPATCH_TIME_NOW, nanoseconds); + } + if (timer_flags & DISPATCH_TIMER_ONESHOT) { + // 6866347 - make sure nanoseconds won't overflow + nanoseconds = INT64_MAX; // non-repeating (~292 years) + } + + ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, (unsigned long)timer_flags, q); + if (!ds) { + return NULL; + } + ds = _dispatch_source_create2(ds, attr, context, callback); + if (!ds) { + return NULL; + } + dispatch_source_set_timer(ds, start, nanoseconds, leeway); + + return ds; +} + +#ifdef __BLOCKS__ +dispatch_source_t +dispatch_source_read_create(int descriptor, + dispatch_source_attr_t attr, + dispatch_queue_t q, + dispatch_source_handler_t callback) +{ + return dispatch_source_read_create_f(descriptor, + attr, q, callback, _dispatch_source_call_block); +} +#endif + +dispatch_source_t +dispatch_source_read_create_f(int fd, + dispatch_source_attr_t attr, + dispatch_queue_t q, + void *context, + dispatch_source_handler_function_t callback) +{ + dispatch_source_t ds; + ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, fd, 0, q); + return _dispatch_source_create2(ds, attr, context, callback); +} + +#ifdef __BLOCKS__ +dispatch_source_t +dispatch_source_write_create(int descriptor, + dispatch_source_attr_t attr, + dispatch_queue_t q, + dispatch_source_handler_t callback) +{ + return dispatch_source_write_create_f(descriptor, + attr, q, callback, _dispatch_source_call_block); +} +#endif + +dispatch_source_t +dispatch_source_write_create_f(int fd, + dispatch_source_attr_t attr, + dispatch_queue_t q, + void *context, + dispatch_source_handler_function_t callback) +{ + dispatch_source_t ds; + ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, fd, 0, q); + return _dispatch_source_create2(ds, attr, context, callback); +} + +#ifdef __BLOCKS__ +dispatch_source_t +dispatch_source_vnode_create(int descriptor, + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t q, + dispatch_source_handler_t callback) +{ + return dispatch_source_vnode_create_f(descriptor, + flags, attr, q, callback, _dispatch_source_call_block); +} +#endif + +dispatch_source_t +dispatch_source_vnode_create_f(int fd, + uint64_t event_mask, + dispatch_source_attr_t attr, + dispatch_queue_t q, + void *context, + dispatch_source_handler_function_t callback) +{ + dispatch_source_t ds; + ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd, (unsigned long)event_mask, q); + return _dispatch_source_create2(ds, attr, context, callback); +} + +#ifdef __BLOCKS__ +dispatch_source_t +dispatch_source_signal_create(unsigned long sig, + dispatch_source_attr_t attr, + dispatch_queue_t q, + dispatch_source_handler_t callback) +{ + return dispatch_source_signal_create_f(sig, + attr, q, callback, _dispatch_source_call_block); +} +#endif + +dispatch_source_t +dispatch_source_signal_create_f(unsigned long signo, + dispatch_source_attr_t attr, + dispatch_queue_t q, + void *context, + dispatch_source_handler_function_t callback) +{ + dispatch_source_t ds; + ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, signo, 0, q); + return _dispatch_source_create2(ds, attr, context, callback); +} + +#ifdef __BLOCKS__ +dispatch_source_t +dispatch_source_proc_create(pid_t pid, + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t q, + dispatch_source_handler_t callback) +{ + return dispatch_source_proc_create_f(pid, + flags, attr, q, callback, _dispatch_source_call_block); +} +#endif + +dispatch_source_t +dispatch_source_proc_create_f(pid_t pid, + uint64_t event_mask, + dispatch_source_attr_t attr, + dispatch_queue_t q, + void *context, + dispatch_source_handler_function_t callback) +{ + dispatch_source_t ds; + ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, (unsigned long)event_mask, q); + return _dispatch_source_create2(ds, attr, context, callback); +} + +#ifdef __BLOCKS__ +dispatch_source_t +dispatch_source_vfs_create(uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t q, + dispatch_source_handler_t callback) +{ + return dispatch_source_vfs_create_f(flags, + attr, q, callback, _dispatch_source_call_block); +} +#endif + +dispatch_source_t +dispatch_source_vfs_create_f(uint64_t event_mask, + dispatch_source_attr_t attr, + dispatch_queue_t q, + void *context, + dispatch_source_handler_function_t callback) +{ + dispatch_source_t ds; + ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_VFS, 0, (unsigned long)event_mask, q); + return _dispatch_source_create2(ds, attr, context, callback); +} + +#ifdef __BLOCKS__ +dispatch_source_t +dispatch_source_data_create(unsigned long behavior, + dispatch_source_attr_t attr, + dispatch_queue_t q, + dispatch_source_handler_t callback) +{ + return dispatch_source_data_create_f(behavior, + attr, q, callback, _dispatch_source_call_block); +} +#endif + +#ifdef __BLOCKS__ +dispatch_source_t +dispatch_source_machport_create(mach_port_t mport, + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t dq, + dispatch_source_handler_t callback) +{ + return dispatch_source_machport_create_f(mport, flags, + attr, dq, callback, _dispatch_source_call_block); +} +#endif + +dispatch_source_t +dispatch_source_data_create_f(unsigned long behavior, + dispatch_source_attr_t attr, + dispatch_queue_t q, + void *context, + dispatch_source_handler_function_t callback) +{ + dispatch_source_t ds; + dispatch_source_type_t type; + switch (behavior) { + case DISPATCH_SOURCE_CUSTOM_ADD: + type = DISPATCH_SOURCE_TYPE_DATA_ADD; + break; + case DISPATCH_SOURCE_CUSTOM_OR: + type = DISPATCH_SOURCE_TYPE_DATA_OR; + break; + default: + return NULL; + } + ds = dispatch_source_create(type, 0, 0, q); + return _dispatch_source_create2(ds, attr, context, callback); +} + +dispatch_source_t +dispatch_source_machport_create_f(mach_port_t mport, + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t dq, + void *ctxt, + dispatch_source_handler_function_t func) +{ + dispatch_source_t ds; + dispatch_source_type_t type; + unsigned long newflags = 0; + + if (flags & ~(DISPATCH_MACHPORT_DEAD|DISPATCH_MACHPORT_RECV)) { + return NULL; + } + // XXX DELETED + if (flags & DISPATCH_MACHPORT_DEAD) { + type = DISPATCH_SOURCE_TYPE_MACH_SEND; + newflags |= DISPATCH_MACH_SEND_DEAD; + } else { + type = DISPATCH_SOURCE_TYPE_MACH_RECV; + } + + ds = dispatch_source_create(type, mport, newflags, dq); + return _dispatch_source_create2(ds, attr, ctxt, func); +} + diff --git a/src/legacy.h b/src/legacy.h new file mode 100644 index 0000000..e6bffbc --- /dev/null +++ b/src/legacy.h @@ -0,0 +1,748 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +/* + * LEGACY: This header file describles LEGACY interfaces to libdispatch from an + * earlier revision of the API. These interfaces WILL be removed in the future. + */ + +#ifndef __DISPATCH_LEGACY__ +#define __DISPATCH_LEGACY__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +#include + +#define DISPATCH_DEPRECATED __attribute__((deprecated)) +#define DISPATCH_PUBLIC_API __attribute__((visibility("default"))) + +typedef struct dispatch_item_s *dispatch_item_t; + +struct dispatch_item_s { + void * di_objc_isa; /* FIXME -- someday... */ + struct dispatch_item_s *volatile di_next; + dispatch_queue_t di_cback_q; + uint32_t di_flags; + semaphore_t di_semaphore; + void * di_work_func; + void * di_work_ctxt; + void * di_cback_func; + void * di_cback_ctxt; + void * di_ctxt; +}; + +// Use: dispatch_source_t +typedef struct dispatch_source_s *dispatch_event_t; + +// Obsolete +#ifdef __BLOCKS__ +typedef void (^dispatch_legacy_block_t)(dispatch_item_t); +typedef void (^dispatch_queue_deletion_block_t)(dispatch_queue_t queue); +typedef void (^dispatch_source_deletion_t)(dispatch_source_t source); +typedef void (^dispatch_event_callback_t)(dispatch_event_t event); +typedef void (^dispatch_source_handler_t)(dispatch_source_t source); +typedef dispatch_source_handler_t dispatch_event_handler_t; +typedef void (^dispatch_source_finalizer_t)(dispatch_source_t source); +#endif /* __BLOCKS__ */ + +// Obsolete +typedef void (*dispatch_source_handler_function_t)(void *, dispatch_source_t); +typedef void (*dispatch_source_finalizer_function_t)(void *, dispatch_source_t); +typedef dispatch_source_handler_function_t dispatch_event_handler_function_t; + +DISPATCH_DECL(dispatch_source_attr); + +#define DISPATCH_SOURCE_CREATE_SUSPENDED ((dispatch_source_attr_t)~0ul) + +#ifdef __BLOCKS__ +typedef void (^dispatch_queue_finalizer_t)(dispatch_queue_t queue); +#endif + +typedef void (*dispatch_queue_finalizer_function_t)(void *, dispatch_queue_t); + +__BEGIN_DECLS + +/*! + * @function dispatch_queue_attr_create + * + * @abstract + * Creates a new dispatch queue attribute structure. These attributes may be + * provided at creation time to modify the default behavior of the queue. + * + * @discussion + * The values present in this structure are copied to newly created queues. + * The same attribute structure may be provided to multiple calls to + * dispatch_queue_create() but only the values in the structure at the time the + * call is made will be used. + * + * @result + * The new dispatch queue attribute structure, initialized to default values. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_queue_attr_t +dispatch_queue_attr_create(void); + +/*! + * @function dispatch_queue_attr_set_priority + * + * @abstract + * Set the priority level for a dispatch queue. + * + * @discussion + * Priority levels may be: + * - DISPATCH_QUEUE_PRIORITY_HIGH + * - DISPATCH_QUEUE_PRIORITY_DEFAULT + * - DISPATCH_QUEUE_PRIORITY_LOW + * Queues set to high priority will be processed + * before queues set to default priority or low priority. + * Queues set to low priority will be processed only if all + * high priority and default priority queues are empty. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_queue_attr_set_priority(dispatch_queue_attr_t attr, int priority); + +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +long +dispatch_queue_attr_set_finalizer( + dispatch_queue_attr_t attr, + dispatch_queue_finalizer_t finalizer); +#endif + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_queue_attr_set_finalizer_f(dispatch_queue_attr_t attr, void *context, dispatch_queue_finalizer_function_t finalizer); + +/*! + * @function dispatch_get_concurrent_queue + * + * @abstract + * Returns a well-known global concurrent queue of a given priority level. + * + * @discussion + * Blocks submitted to the returned queue may be invoked concurrently with + * respect to each other. + * + * These queues are useful for performing one-shot asynchronous operations, + * e.g. dispatch_async() to an "anonymous" queue; or for performing parallel + * loops concurrently on multiple processors, e.g. dispatch_apply(). + * + * The dispatch queues returned by this function are managed by the system for + * the lifetime of the application, and need not be retained or released + * directly by the application. Furthermore, dispatch_suspend() and + * dispatch_queue_resume() are not supported on these global queues, and will + * be ignored. + * + * @param priority + * The requested priority level for the queue (default is zero): + * - DISPATCH_QUEUE_PRIORITY_HIGH + * - DISPATCH_QUEUE_PRIORITY_DEFAULT + * - DISPATCH_QUEUE_PRIORITY_LOW + * + * @result + * Returns a concurrent dispatch queue for use with dispatch_async(), + * dispatch_apply(), et al. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_queue_t +dispatch_get_concurrent_queue(long priority); + +DISPATCH_PUBLIC_API //DISPATCH_DEPRECATED +void +dispatch_queue_attr_set_flags(dispatch_queue_attr_t attr, uint64_t flags); + +#ifdef __BLOCKS__ +DISPATCH_PUBLIC_API DISPATCH_NONNULL1 DISPATCH_NONNULL2 DISPATCH_DEPRECATED +dispatch_item_t +dispatch_call(dispatch_queue_t, dispatch_legacy_block_t work, dispatch_legacy_block_t completion) +__asm__("_dispatch_call2"); +#endif /* __BLOCKS__ */ + +DISPATCH_PUBLIC_API DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_DEPRECATED +dispatch_queue_t +dispatch_queue_get_current(void); + +// Use: dispatch_retain +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED +void +dispatch_queue_retain(dispatch_queue_t); + +// Use: dispatch_release +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED +void +dispatch_queue_release(dispatch_queue_t); + +// Use: dispatch_resume +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED +void +dispatch_queue_resume(dispatch_queue_t); + +// Use: dispatch_release +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED +void +dispatch_source_release(dispatch_source_t); + +// Use: dispatch_suspend +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED +void +dispatch_source_suspend(dispatch_source_t); + +// Use: dispatch_resume +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED +void +dispatch_source_resume(dispatch_source_t); + +// Use: dispatch_release +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED +void +dispatch_queue_attr_release(dispatch_queue_attr_t); + +// Use: dispatch_release +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED +void +dispatch_source_attr_release(dispatch_source_attr_t); + +// Use: dispatch_source_get_handle +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_DEPRECATED +sigset_t +dispatch_event_get_signals(dispatch_event_t event); + +// Use: dispatch_get_context +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL //DISPATCH_DEPRECATED +void * +dispatch_queue_get_context(dispatch_queue_t queue); + +// Use: dispatch_set_context +DISPATCH_PUBLIC_API DISPATCH_NONNULL1 //DISPATCH_DEPRECATED +void +dispatch_queue_set_context(dispatch_queue_t queue, void *context); + +// Use: dispatch_get_context +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL //DISPATCH_DEPRECATED +void * +dispatch_source_get_context(dispatch_source_t source); + +// Use: dispatch_set_context +DISPATCH_PUBLIC_API DISPATCH_NONNULL1 //DISPATCH_DEPRECATED +void +dispatch_source_set_context(dispatch_source_t source, void * context); + +// Use: dispatch_source_merge_data +DISPATCH_PUBLIC_API DISPATCH_NONNULL_ALL DISPATCH_DEPRECATED +void +dispatch_source_custom_trigger(dispatch_source_t ds); + +// Use: dispatch_source_cancel +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_cancel(dispatch_source_t); + +// Use: dispatch_source_testcancel +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +long +dispatch_testcancel(dispatch_source_t); + +// Use: dispatch_source_set_timer +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +long +dispatch_source_timer_set_time(dispatch_source_t ds, + uint64_t nanoseconds, + uint64_t leeway); + +// Use: dispatch_source_merge_data +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_source_trigger(dispatch_source_t source, unsigned long value); + +enum { + DISPATCH_ERROR_DOMAIN_NO_ERROR = 0, + DISPATCH_ERROR_DOMAIN_POSIX = 1, + DISPATCH_ERROR_DOMAIN_MACH = 2, +}; + +// Obsolete +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_WARN_RESULT DISPATCH_NOTHROW +long +dispatch_source_get_error(dispatch_source_t source, long* error); + +// Use: dispatch_source_get_handle +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +mach_port_t +dispatch_source_get_machport(dispatch_source_t source); + +// Use: dispatch_source_get_handle +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +pid_t +dispatch_source_get_descriptor(dispatch_source_t source); + +// Use: dispatch_source_get_handle +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +pid_t +dispatch_source_get_pid(dispatch_source_t source); + +// Use: dispatch_source_get_mask +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +uint64_t +dispatch_source_get_flags(dispatch_source_t source); + +// LEGACY: dispatch_event_t == dispatch_source_t +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_source_t +dispatch_event_get_source(dispatch_event_t event); + +// Use: dispatch_source_get_error +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_WARN_RESULT DISPATCH_NOTHROW +long +dispatch_event_get_error(dispatch_event_t event, long* error); + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +uint64_t +dispatch_event_get_nanoseconds(dispatch_event_t event); + +// Use: dispatch_source_get_handle +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +long +dispatch_event_get_signal(dispatch_event_t event); + +// Use: dispatch_source_get_data +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +uint64_t +dispatch_event_get_flags(dispatch_event_t event); + +// Use: dispatch_source_get_data +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_NOTHROW +size_t +dispatch_event_get_bytes_available(dispatch_event_t event); + +// Use: dispatch_source_get_data +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +unsigned long +dispatch_event_get_count(dispatch_event_t event); + +// Obsolete +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_source_attr_t +dispatch_source_attr_create(void); + +// Obsolete +#if defined(__BLOCKS__) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NOTHROW +dispatch_source_finalizer_t +dispatch_source_attr_get_finalizer(dispatch_source_attr_t attr); +#endif /* __BLOCKS__ */ + +// Obsolete +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_source_attr_t +dispatch_source_attr_copy(dispatch_source_attr_t proto); + +// Obsolete +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +long +dispatch_source_attr_set_finalizer( + dispatch_source_attr_t attr, + dispatch_source_finalizer_t finalizer); +#endif /* __BLOCKS__ */ + +// Obsolete +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_source_attr_set_finalizer_f( + dispatch_source_attr_t attr, + void *context, + dispatch_source_finalizer_function_t finalizer); + +// Obsolete +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_source_attr_set_context( + dispatch_source_attr_t attr, + void *context); + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL4 DISPATCH_NONNULL5 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_mig_create( + mach_port_t mport, + size_t max_size, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_mig_callback_t mig_callback); + +enum { + DISPATCH_TIMER_WALL_CLOCK = 0x4, +}; + +enum { + DISPATCH_TIMER_INTERVAL = 0x0, + DISPATCH_TIMER_ONESHOT = 0x1, + DISPATCH_TIMER_ABSOLUTE = 0x3, +}; + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, ...) +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL5 DISPATCH_NONNULL6 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_timer_create( + uint64_t flags, + uint64_t nanoseconds, + uint64_t leeway, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_source_handler_t handler); +#endif /* __BLOCKS__ */ + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL5 DISPATCH_NONNULL7 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_timer_create_f( + uint64_t flags, + uint64_t nanoseconds, + uint64_t leeway, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_source_handler_function_t handler); + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, ...) +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_signal_create( + unsigned long signo, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_source_handler_t handler); +#endif /* __BLOCKS__ */ + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL5 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_signal_create_f( + unsigned long sig, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_source_handler_function_t handler); + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, ...) +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL4 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_read_create( + int descriptor, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_source_handler_t handler); +#endif /* __BLOCKS__ */ + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL5 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_read_create_f( + int descriptor, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_source_handler_function_t handler); + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, ...) +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL4 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_write_create( + int descriptor, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_source_handler_t handler); +#endif /* __BLOCKS__ */ + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_WRITE, ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL5 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_write_create_f( + int descriptor, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_source_handler_function_t handler); + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, ...) +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL4 DISPATCH_NONNULL5 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_vnode_create( + int descriptor, + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_source_handler_t handler); +#endif /* __BLOCKS__ */ + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL4 DISPATCH_NONNULL6 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_vnode_create_f( + int descriptor, + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_source_handler_function_t handler); + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, ...) +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL4 DISPATCH_NONNULL5 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_proc_create( + pid_t pid, + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_source_handler_t handler); +#endif /* __BLOCKS__ */ + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL4 DISPATCH_NONNULL6 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_proc_create_f( + pid_t pid, + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_source_handler_function_t handler); + +enum { + DISPATCH_MACHPORT_DEAD = 0x1, + DISPATCH_MACHPORT_RECV = 0x2, + DISPATCH_MACHPORT_DELETED = 0x4, +}; + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, ...) +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_machport_create( + mach_port_t mport, + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_source_handler_t handler); +#endif /* __BLOCKS__ */ + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_machport_create_f( + mach_port_t mport, + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_source_handler_function_t handler); + +enum { + DISPATCH_SOURCE_DATA_ADD = 1, + DISPATCH_SOURCE_DATA_OR, +}; + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA..., ...) +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL4 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_data_create( + unsigned long behavior, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_source_handler_t handler); +#endif /* __BLOCKS__ */ + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA..., ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL5 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_data_create_f( + unsigned long behavior, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_source_handler_function_t handler); + +enum { + DISPATCH_SOURCE_CUSTOM_ADD = DISPATCH_SOURCE_DATA_ADD, + DISPATCH_SOURCE_CUSTOM_OR = DISPATCH_SOURCE_DATA_OR, +}; + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA..., ...) +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL2 DISPATCH_NONNULL3 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_custom_create( + unsigned long behavior, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_event_handler_t handler); +#endif /* __BLOCKS__ */ + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA..., ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL2 DISPATCH_NONNULL4 DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_custom_create_f( + unsigned long behavior, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_event_handler_function_t handler); + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_VFS, ...) +#if defined(__BLOCKS__) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL4 +dispatch_source_t +dispatch_source_vfs_create( + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + dispatch_source_handler_t handler); +#endif /* __BLOCKS__ */ + +// Use: dispatch_source_create(DISPATCH_SOURCE_TYPE_VFS, ...) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NONNULL3 DISPATCH_NONNULL5 +dispatch_source_t +dispatch_source_vfs_create_f( + uint64_t flags, + dispatch_source_attr_t attr, + dispatch_queue_t queue, + void *h_context, + dispatch_source_handler_function_t handler); + +/* + * Raw Mach message support from MIG source. + * + * It is possible to use the following callback style with the MIG source to + * obtain the raw mach message (and send no reply) similar to CFMachPort. + * (For more specific CFMachPort compatibility, see below). + * + * void handle_mach_msg(mach_msg_header *msg) { ... } + * ... + * DISPATCH_MACHPORT_CALLBACK_DECL(mig_compat_callback, handle_mach_msg); + * ... + * mig = dispatch_source_mig_create(mp, MY_MAX_MSG_SIZE, NULL, + * queue, mig_compat_callback); + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +boolean_t +_dispatch_machport_callback(mach_msg_header_t *msg, mach_msg_header_t *reply, void (*callback)(mach_msg_header_t *)); + +#define DISPATCH_MACHPORT_CALLBACK_DECL(new_callback, existing_callback) \ +__private_extern__ boolean_t \ +new_callback(mach_msg_header_t *msg, mach_msg_header_t *reply) \ +{ return _dispatch_machport_callback(msg, reply, existing_callback); } + +/* + * CFMachPort compatibility. + * + * It is possible to use existing CFMachPort callbacks with dispatch mig sources + * by delcaring the following shim and using the shim as the mig server callback + * to dispatch_source_mig_create(). + * The CFMachPortRef "port" parameter of the CFMachPortCallBack will be NULL. + * If mach_port_set_context() is used, that value will be passed into the "info" + * parameter of the CFMachPortCallBack. + * + * DISPATCH_CFMACHPORT_CALLBACK_DECL(mig_callback, MyCFMachPortCallBack); + * + * ... + * { + * kr = mach_port_set_context(mach_task_self(), mp, (mach_vm_address_t)context); + * mig = dispatch_source_mig_create(mp, MY_MAX_MSG_SIZE, NULL, + * queue, mig_callback); + */ +struct __CFMachPort; + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +boolean_t +_dispatch_CFMachPortCallBack(mach_msg_header_t *msg, mach_msg_header_t *reply, void (*callback)(struct __CFMachPort *, void *msg, signed long size, void *)); + +#define DISPATCH_CFMACHPORT_CALLBACK_DECL(new_callback, existing_callback) \ +__private_extern__ boolean_t \ +new_callback(mach_msg_header_t *msg, mach_msg_header_t *reply) \ +{ return _dispatch_CFMachPortCallBack(msg, reply, existing_callback); } + +__END_DECLS + +#endif diff --git a/src/object.c b/src/object.c new file mode 100644 index 0000000..8746495 --- /dev/null +++ b/src/object.c @@ -0,0 +1,200 @@ +/* + * 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@ + */ + +#include "internal.h" + + +void +dispatch_debug(dispatch_object_t dou, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + + dispatch_debugv(dou._do, msg, ap); + + va_end(ap); +} + +void +dispatch_debugv(dispatch_object_t dou, const char *msg, va_list ap) +{ + char buf[4096]; + size_t offs; + + if (dou._do && dou._do->do_vtable->do_debug) { + offs = dx_debug(dou._do, buf, sizeof(buf)); + } else { + offs = snprintf(buf, sizeof(buf), "NULL vtable slot"); + } + + snprintf(buf + offs, sizeof(buf) - offs, ": %s", msg); + + _dispatch_logv(buf, ap); +} + +void +dispatch_retain(dispatch_object_t dou) +{ + if (dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) { + return; // global object + } + if ((dispatch_atomic_inc(&dou._do->do_xref_cnt) - 1) == 0) { + DISPATCH_CLIENT_CRASH("Resurrection of an object"); + } +} + +void +_dispatch_retain(dispatch_object_t dou) +{ + if (dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) { + return; // global object + } + if ((dispatch_atomic_inc(&dou._do->do_ref_cnt) - 1) == 0) { + DISPATCH_CLIENT_CRASH("Resurrection of an object"); + } +} + +void +dispatch_release(dispatch_object_t dou) +{ + typeof(dou._do->do_xref_cnt) oldval; + + if (dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) { + return; + } + + oldval = dispatch_atomic_dec(&dou._do->do_xref_cnt) + 1; + + if (fastpath(oldval > 1)) { + return; + } + if (oldval == 1) { +#ifndef DISPATCH_NO_LEGACY + if (dou._do->do_vtable == (void*)&_dispatch_source_kevent_vtable) { + return _dispatch_source_legacy_xref_release(dou._ds); + } +#endif + if (slowpath(DISPATCH_OBJECT_SUSPENDED(dou._do))) { + // Arguments for and against this assert are within 6705399 + DISPATCH_CLIENT_CRASH("Release of a suspended object"); + } + return _dispatch_release(dou._do); + } + DISPATCH_CLIENT_CRASH("Over-release of an object"); +} + +void +_dispatch_dispose(dispatch_object_t dou) +{ + dispatch_queue_t tq = dou._do->do_targetq; + dispatch_function_t func = dou._do->do_finalizer; + void *ctxt = dou._do->do_ctxt; + + dou._do->do_vtable = (void *)0x200; + + free(dou._do); + + if (func && ctxt) { + dispatch_async_f(tq, ctxt, func); + } + _dispatch_release(tq); +} + +void +_dispatch_release(dispatch_object_t dou) +{ + typeof(dou._do->do_ref_cnt) oldval; + + if (dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT) { + return; // global object + } + + oldval = dispatch_atomic_dec(&dou._do->do_ref_cnt) + 1; + + if (fastpath(oldval > 1)) { + return; + } + if (oldval == 1) { + if (dou._do->do_next != DISPATCH_OBJECT_LISTLESS) { + DISPATCH_CRASH("release while enqueued"); + } + if (dou._do->do_xref_cnt) { + DISPATCH_CRASH("release while external references exist"); + } + + return dx_dispose(dou._do); + } + DISPATCH_CRASH("over-release"); +} + +void * +dispatch_get_context(dispatch_object_t dou) +{ + return dou._do->do_ctxt; +} + +void +dispatch_set_context(dispatch_object_t dou, void *context) +{ + if (dou._do->do_ref_cnt != DISPATCH_OBJECT_GLOBAL_REFCNT) { + dou._do->do_ctxt = context; + } +} + +void +dispatch_set_finalizer_f(dispatch_object_t dou, dispatch_function_t finalizer) +{ + dou._do->do_finalizer = finalizer; +} + +void +dispatch_suspend(dispatch_object_t dou) +{ + if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { + return; + } + dispatch_atomic_add(&dou._do->do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_INTERVAL); +} + +void +dispatch_resume(dispatch_object_t dou) +{ + if (slowpath(dou._do->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { + return; + } + switch (dispatch_atomic_sub(&dou._do->do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_INTERVAL) + DISPATCH_OBJECT_SUSPEND_INTERVAL) { + case DISPATCH_OBJECT_SUSPEND_INTERVAL: + _dispatch_wakeup(dou._do); + break; + case 0: + DISPATCH_CLIENT_CRASH("Over-resume of an object"); + break; + default: + break; + } +} + +size_t +dispatch_object_debug_attr(dispatch_object_t dou, char* buf, size_t bufsiz) +{ + return snprintf(buf, bufsiz, "refcnt = 0x%x, suspend_cnt = 0x%x, ", + dou._do->do_ref_cnt, dou._do->do_suspend_cnt); +} diff --git a/src/object.h b/src/object.h new file mode 100644 index 0000000..febc960 --- /dev/null +++ b/src/object.h @@ -0,0 +1,195 @@ +/* + * 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@ + */ + +#ifndef __DISPATCH_OBJECT__ +#define __DISPATCH_OBJECT__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +__BEGIN_DECLS + +/*! + * @function dispatch_debug + * + * @abstract + * Programmatically log debug information about a dispatch object. + * + * @param object + * The object to introspect. + * + * @param message + * The message to log above and beyond the introspection. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL2 DISPATCH_NOTHROW __attribute__((__format__(printf,2,3))) +void +dispatch_debug(dispatch_object_t object, const char *message, ...); + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL2 DISPATCH_NOTHROW __attribute__((__format__(printf,2,0))) +void +dispatch_debugv(dispatch_object_t object, const char *message, va_list ap); + +/*! + * @function dispatch_retain + * + * @abstract + * Increment the reference count of a dispatch object. + * + * @discussion + * Calls to dispatch_retain() must be balanced with calls to + * dispatch_release(). + * + * @param object + * The object to retain. + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_retain(dispatch_object_t object); + +/*! + * @function dispatch_release + * + * @abstract + * Decrement the reference count of a dispatch object. + * + * @discussion + * A dispatch object is asynchronously deallocated once all references are + * released (i.e. the reference count becomes zero). The system does not + * guarantee that a given client is the last or only reference to a given + * object. + * + * @param object + * The object to release. + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_release(dispatch_object_t object); + +/*! + * @function dispatch_get_context + * + * @abstract + * Returns the application defined context of the object. + * + * @param object + * The result of passing NULL in this parameter is undefined. + * + * @result + * The context of the object; may be NULL. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +void * +dispatch_get_context(dispatch_object_t object); + +/*! + * @function dispatch_set_context + * + * @abstract + * Associates an application defined context with the object. + * + * @param object + * The result of passing NULL in this parameter is undefined. + * + * @param context + * The new client defined context for the object. This may be NULL. + * + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NOTHROW //DISPATCH_NONNULL1 +void +dispatch_set_context(dispatch_object_t object, void *context); + +/*! + * @function dispatch_set_finalizer_f + * + * @abstract + * Set the finalizer function for a dispatch object. + * + * @param + * The dispatch object to modify. + * The result of passing NULL in this parameter is undefined. + * + * @param + * The finalizer function pointer. + * + * @discussion + * A dispatch object's finalizer will be invoked on the object's target queue + * after all references to the object have been released. This finalizer may be + * used by the application to release any resources associated with the object, + * such as freeing the object's context. + * The context parameter passed to the finalizer function is the current + * context of the dispatch object at the time the finalizer call is made. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NOTHROW //DISPATCH_NONNULL1 +void +dispatch_set_finalizer_f(dispatch_object_t object, + dispatch_function_t finalizer); + +/*! + * @function dispatch_suspend + * + * @abstract + * Suspends the invocation of blocks on a dispatch object. + * + * @discussion + * A suspended object will not invoke any blocks associated with it. The + * suspension of an object will occur after any running block associated with + * the object completes. + * + * Calls to dispatch_suspend() must be balanced with calls + * to dispatch_resume(). + * + * @param object + * The object to be suspended. + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_suspend(dispatch_object_t object); + +/*! + * @function dispatch_resume + * + * @abstract + * Resumes the invocation of blocks on a dispatch object. + * + * @param object + * The object to be resumed. + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_resume(dispatch_object_t object); + +__END_DECLS + +#endif diff --git a/src/object_internal.h b/src/object_internal.h new file mode 100644 index 0000000..cc048be --- /dev/null +++ b/src/object_internal.h @@ -0,0 +1,110 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_OBJECT_INTERNAL__ +#define __DISPATCH_OBJECT_INTERNAL__ + +enum { + _DISPATCH_CONTINUATION_TYPE = 0x00000, // meta-type for continuations + _DISPATCH_QUEUE_TYPE = 0x10000, // meta-type for queues + _DISPATCH_SOURCE_TYPE = 0x20000, // meta-type for sources + _DISPATCH_SEMAPHORE_TYPE = 0x30000, // meta-type for semaphores + _DISPATCH_ATTR_TYPE = 0x10000000, // meta-type for attribute structures + + DISPATCH_CONTINUATION_TYPE = _DISPATCH_CONTINUATION_TYPE, + + DISPATCH_QUEUE_ATTR_TYPE = _DISPATCH_QUEUE_TYPE | _DISPATCH_ATTR_TYPE, + + DISPATCH_QUEUE_TYPE = 1 | _DISPATCH_QUEUE_TYPE, + DISPATCH_QUEUE_GLOBAL_TYPE = 2 | _DISPATCH_QUEUE_TYPE, + DISPATCH_QUEUE_MGR_TYPE = 3 | _DISPATCH_QUEUE_TYPE, + + DISPATCH_SEMAPHORE_TYPE = _DISPATCH_SEMAPHORE_TYPE, + + DISPATCH_SOURCE_ATTR_TYPE = _DISPATCH_SOURCE_TYPE | _DISPATCH_ATTR_TYPE, + + DISPATCH_SOURCE_KEVENT_TYPE = 1 | _DISPATCH_SOURCE_TYPE, +}; + +#define DISPATCH_VTABLE_HEADER(x) \ + unsigned long const do_type; \ + const char *const do_kind; \ + size_t (*const do_debug)(struct x *, char *, size_t); \ + struct dispatch_queue_s *(*const do_invoke)(struct x *); \ + bool (*const do_probe)(struct x *); \ + void (*const do_dispose)(struct x *) + +#define dx_type(x) (x)->do_vtable->do_type +#define dx_kind(x) (x)->do_vtable->do_kind +#define dx_debug(x, y, z) (x)->do_vtable->do_debug((x), (y), (z)) +#define dx_dispose(x) (x)->do_vtable->do_dispose(x) +#define dx_invoke(x) (x)->do_vtable->do_invoke(x) +#define dx_probe(x) (x)->do_vtable->do_probe(x) + +#define DISPATCH_STRUCT_HEADER(x, y) \ + const struct y *do_vtable; \ + struct x *volatile do_next; \ + unsigned int do_ref_cnt; \ + unsigned int do_xref_cnt; \ + unsigned int do_suspend_cnt; \ + struct dispatch_queue_s *do_targetq; \ + void *do_ctxt; \ + void *do_finalizer + +#define DISPATCH_OBJECT_GLOBAL_REFCNT (~0u) +#define DISPATCH_OBJECT_SUSPEND_LOCK 1u // "word and bit" must be a power of two to be safely subtracted +#define DISPATCH_OBJECT_SUSPEND_INTERVAL 2u +#define DISPATCH_OBJECT_SUSPENDED(x) ((x)->do_suspend_cnt >= DISPATCH_OBJECT_SUSPEND_INTERVAL) +#ifdef __LP64__ +// the bottom nibble must not be zero, the rest of the bits should be random +// we sign extend the 64-bit version so that a better instruction encoding is generated on Intel +#define DISPATCH_OBJECT_LISTLESS ((void *)0xffffffff89abcdef) +#else +#define DISPATCH_OBJECT_LISTLESS ((void *)0x89abcdef) +#endif + +#define _dispatch_trysuspend(x) __sync_bool_compare_and_swap(&(x)->do_suspend_cnt, 0, DISPATCH_OBJECT_SUSPEND_INTERVAL) +// _dispatch_source_invoke() relies on this testing the whole suspend count +// word, not just the lock bit. In other words, no point taking the lock +// if the source is suspended or canceled. +#define _dispatch_trylock(x) dispatch_atomic_cmpxchg(&(x)->do_suspend_cnt, 0, DISPATCH_OBJECT_SUSPEND_LOCK) + +struct dispatch_object_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_object_s); +}; + +struct dispatch_object_s { + DISPATCH_STRUCT_HEADER(dispatch_object_s, dispatch_object_vtable_s); +}; + +size_t dispatch_object_debug_attr(dispatch_object_t dou, char* buf, size_t bufsiz); + +void _dispatch_retain(dispatch_object_t dou); +void _dispatch_release(dispatch_object_t dou); +void _dispatch_dispose(dispatch_object_t dou); +dispatch_queue_t _dispatch_wakeup(dispatch_object_t dou); + +#endif diff --git a/src/once.c b/src/once.c new file mode 100644 index 0000000..9046c06 --- /dev/null +++ b/src/once.c @@ -0,0 +1,104 @@ +/* + * 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@ + */ + +#include "internal.h" + +#undef dispatch_once +#undef dispatch_once_f + +#ifdef __BLOCKS__ +void +dispatch_once(dispatch_once_t *val, void (^block)(void)) +{ + struct Block_basic *bb = (void *)block; + + dispatch_once_f(val, block, (void *)bb->Block_invoke); +} +#endif + +DISPATCH_NOINLINE +void +dispatch_once_f(dispatch_once_t *val, void *ctxt, void (*func)(void *)) +{ + volatile long *vval = val; + + if (dispatch_atomic_cmpxchg(val, 0l, 1l)) { + func(ctxt); + + // The next barrier must be long and strong. + // + // The scenario: SMP systems with weakly ordered memory models + // and aggressive out-of-order instruction execution. + // + // The problem: + // + // The dispatch_once*() wrapper macro causes the callee's + // instruction stream to look like this (pseudo-RISC): + // + // load r5, pred-addr + // cmpi r5, -1 + // beq 1f + // call dispatch_once*() + // 1f: + // load r6, data-addr + // + // May be re-ordered like so: + // + // load r6, data-addr + // load r5, pred-addr + // cmpi r5, -1 + // beq 1f + // call dispatch_once*() + // 1f: + // + // Normally, a barrier on the read side is used to workaround + // the weakly ordered memory model. But barriers are expensive + // and we only need to synchronize once! After func(ctxt) + // completes, the predicate will be marked as "done" and the + // branch predictor will correctly skip the call to + // dispatch_once*(). + // + // A far faster alternative solution: Defeat the speculative + // read-ahead of peer CPUs. + // + // Modern architectures will throw away speculative results + // once a branch mis-prediction occurs. Therefore, if we can + // ensure that the predicate is not marked as being complete + // until long after the last store by func(ctxt), then we have + // defeated the read-ahead of peer CPUs. + // + // In other words, the last "store" by func(ctxt) must complete + // and then N cycles must elapse before ~0l is stored to *val. + // The value of N is whatever is sufficient to defeat the + // read-ahead mechanism of peer CPUs. + // + // On some CPUs, the most fully synchronizing instruction might + // need to be issued. + + dispatch_atomic_barrier(); + *val = ~0l; + } else { + do { + _dispatch_hardware_pause(); + } while (*vval != ~0l); + + dispatch_atomic_barrier(); + } +} diff --git a/src/once.h b/src/once.h new file mode 100644 index 0000000..8cd25d6 --- /dev/null +++ b/src/once.h @@ -0,0 +1,77 @@ +/* + * 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@ + */ + +#ifndef __DISPATCH_ONCE__ +#define __DISPATCH_ONCE__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +__BEGIN_DECLS + +/*! + * @typedef dispatch_once_t + * + * @abstract + * A predicate for use with dispatch_once(). It must be initialized to zero. + * Note: static and global variables default to zero. + */ +typedef long dispatch_once_t; + +/*! + * @function dispatch_once + * + * @abstract + * Execute a block once and only once. + * + * @param predicate + * A pointer to a dispatch_once_t that is used to test whether the block has + * completed or not. + * + * @param block + * The block to execute once. + * + * @discussion + * Always call dispatch_once() before using or testing any variables that are + * initialized by the block. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_once(dispatch_once_t *predicate, dispatch_block_t block); +#ifdef __GNUC__ +#define dispatch_once(x, ...) do { if (__builtin_expect(*(x), ~0l) != ~0l) dispatch_once((x), (__VA_ARGS__)); } while (0) +#endif +#endif + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_once_f(dispatch_once_t *predicate, void *context, void (*function)(void *)); +#ifdef __GNUC__ +#define dispatch_once_f(x, y, z) do { if (__builtin_expect(*(x), ~0l) != ~0l) dispatch_once_f((x), (y), (z)); } while (0) +#endif + +__END_DECLS + +#endif diff --git a/src/os_shims.h b/src/os_shims.h new file mode 100644 index 0000000..7efd28e --- /dev/null +++ b/src/os_shims.h @@ -0,0 +1,152 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_OS_SHIMS__ +#define __DISPATCH_OS_SHIMS__ + +#include +#include +#include + +__private_extern__ const char *__crashreporter_info__; + +static const unsigned long dispatch_queue_key = __PTK_LIBDISPATCH_KEY0; +static const unsigned long dispatch_sema4_key = __PTK_LIBDISPATCH_KEY1; +static const unsigned long dispatch_cache_key = __PTK_LIBDISPATCH_KEY2; +static const unsigned long dispatch_bcounter_key = __PTK_LIBDISPATCH_KEY3; +//__PTK_LIBDISPATCH_KEY4 +//__PTK_LIBDISPATCH_KEY5 + + +#define SIMULATE_5491082 1 +#ifndef _PTHREAD_TSD_OFFSET +#define _PTHREAD_TSD_OFFSET 0 +#endif + +static inline void +_dispatch_thread_setspecific(unsigned long k, void *v) +{ +#if defined(SIMULATE_5491082) && defined(__i386__) + asm("movl %1, %%gs:%0" : "=m" (*(void **)(k * sizeof(void *) + _PTHREAD_TSD_OFFSET)) : "ri" (v) : "memory"); +#elif defined(SIMULATE_5491082) && defined(__x86_64__) + asm("movq %1, %%gs:%0" : "=m" (*(void **)(k * sizeof(void *) + _PTHREAD_TSD_OFFSET)) : "rn" (v) : "memory"); +#else + int res; + if (_pthread_has_direct_tsd()) { + res = _pthread_setspecific_direct(k, v); + } else { + res = pthread_setspecific(k, v); + } + dispatch_assert_zero(res); +#endif +} + +static inline void * +_dispatch_thread_getspecific(unsigned long k) +{ +#if defined(SIMULATE_5491082) && (defined(__i386__) || defined(__x86_64__)) + void *rval; + asm("mov %%gs:%1, %0" : "=r" (rval) : "m" (*(void **)(k * sizeof(void *) + _PTHREAD_TSD_OFFSET))); + return rval; +#else + if (_pthread_has_direct_tsd()) { + return _pthread_getspecific_direct(k); + } else { + return pthread_getspecific(k); + } +#endif +} + +static inline void +_dispatch_thread_key_init_np(unsigned long k, void (*d)(void *)) +{ + dispatch_assert_zero(pthread_key_init_np((int)k, d)); +} + +#define _dispatch_thread_self pthread_self + + +#if DISPATCH_PERF_MON + +#if defined(SIMULATE_5491082) && (defined(__i386__) || defined(__x86_64__)) +#ifdef __LP64__ +#define _dispatch_workitem_inc() asm("incq %%gs:%0" : "+m" \ + (*(void **)(dispatch_bcounter_key * sizeof(void *) + _PTHREAD_TSD_OFFSET)) :: "cc") +#define _dispatch_workitem_dec() asm("decq %%gs:%0" : "+m" \ + (*(void **)(dispatch_bcounter_key * sizeof(void *) + _PTHREAD_TSD_OFFSET)) :: "cc") +#else +#define _dispatch_workitem_inc() asm("incl %%gs:%0" : "+m" \ + (*(void **)(dispatch_bcounter_key * sizeof(void *) + _PTHREAD_TSD_OFFSET)) :: "cc") +#define _dispatch_workitem_dec() asm("decl %%gs:%0" : "+m" \ + (*(void **)(dispatch_bcounter_key * sizeof(void *) + _PTHREAD_TSD_OFFSET)) :: "cc") +#endif +#else +static inline void +_dispatch_workitem_inc(void) +{ + unsigned long cnt = (unsigned long)_dispatch_thread_getspecific(dispatch_bcounter_key); + _dispatch_thread_setspecific(dispatch_bcounter_key, (void *)++cnt); +} +static inline void +_dispatch_workitem_dec(void) +{ + unsigned long cnt = (unsigned long)_dispatch_thread_getspecific(dispatch_bcounter_key); + _dispatch_thread_setspecific(dispatch_bcounter_key, (void *)--cnt); +} +#endif + +// C99 doesn't define flsll() or ffsll() +#ifdef __LP64__ +#define flsll(x) flsl(x) +#else +static inline unsigned int +flsll(uint64_t val) +{ + union { + struct { +#ifdef __BIG_ENDIAN__ + unsigned int hi, low; +#else + unsigned int low, hi; +#endif + } words; + uint64_t word; + } _bucket = { + .word = val, + }; + if (_bucket.words.hi) { + return fls(_bucket.words.hi) + 32; + } + return fls(_bucket.words.low); +} +#endif + +#else +#define _dispatch_workitem_inc() +#define _dispatch_workitem_dec() +#endif // DISPATCH_PERF_MON + +#endif diff --git a/src/private.h b/src/private.h new file mode 100644 index 0000000..8d817fe --- /dev/null +++ b/src/private.h @@ -0,0 +1,114 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_PRIVATE__ +#define __DISPATCH_PRIVATE__ + +#include +#include +#include +#include +#include +#include +#include + +#ifndef __DISPATCH_BUILDING_DISPATCH__ +#include_next + +// Workaround +#ifndef __DISPATCH_PUBLIC__ +#include "/usr/include/dispatch/dispatch.h" +#endif + +#ifndef __DISPATCH_INDIRECT__ +#define __DISPATCH_INDIRECT__ +#endif + +#include +#include +#include + +#ifndef DISPATCH_NO_LEGACY +#include +#endif + +#undef __DISPATCH_INDIRECT__ + +#endif /* !__DISPATCH_BUILDING_DISPATCH__ */ + +/* LEGACY: Use DISPATCH_API_VERSION */ +#define LIBDISPATCH_VERSION DISPATCH_API_VERSION + +__BEGIN_DECLS + +DISPATCH_NOTHROW +void +libdispatch_init(void); + +#define DISPATCH_COCOA_COMPAT 1 +#if DISPATCH_COCOA_COMPAT + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NOTHROW +mach_port_t +_dispatch_get_main_queue_port_4CF(void); + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NOTHROW +void +_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg); + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern void (*dispatch_begin_thread_4GC)(void); + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern void (*dispatch_end_thread_4GC)(void); + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern void *(*_dispatch_begin_NSAutoReleasePool)(void); + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern void (*_dispatch_end_NSAutoReleasePool)(void *); + +#endif + +/* pthreads magic */ + +DISPATCH_NOTHROW void dispatch_atfork_prepare(void); +DISPATCH_NOTHROW void dispatch_atfork_parent(void); +DISPATCH_NOTHROW void dispatch_atfork_child(void); +DISPATCH_NOTHROW void dispatch_init_pthread(pthread_t); + +/* + * Extract the context pointer from a mach message trailer. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +void * +dispatch_mach_msg_get_context(mach_msg_header_t *msg); + +__END_DECLS + +#endif diff --git a/src/protocol.defs b/src/protocol.defs new file mode 100644 index 0000000..e6bd400 --- /dev/null +++ b/src/protocol.defs @@ -0,0 +1,91 @@ +/* + * Copyright (c) 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@ + */ +/* + * Copyright (c) 2008-2009 Apple Inc. All rights reserved. + */ + +#include +#include + +// '64' is used to align with Mach notifications and so that we don't fight with the notify symbols in Libsystem +subsystem libdispatch_internal_protocol 64; + +serverprefix _dispatch_; +userprefix _dispatch_send_; + +skip; /* was MACH_NOTIFY_FIRST: 64 */ + +/* MACH_NOTIFY_PORT_DELETED: 65 */ +simpleroutine +mach_notify_port_deleted( + _notify : mach_port_move_send_once_t; + _name : mach_port_name_t +); + +skip; /* was MACH_NOTIFY_MSG_ACCEPTED: 66 */ + +skip; /* was NOTIFY_OWNERSHIP_RIGHTS: 67 */ + +skip; /* was NOTIFY_RECEIVE_RIGHTS: 68 */ + +/* MACH_NOTIFY_PORT_DESTROYED: 69 */ +simpleroutine +mach_notify_port_destroyed( + _notify : mach_port_move_send_once_t; + _rights : mach_port_move_receive_t +); + +/* MACH_NOTIFY_NO_SENDERS: 70 */ +simpleroutine +mach_notify_no_senders( + _notify : mach_port_move_send_once_t; + _mscnt : mach_port_mscount_t +); + +/* MACH_NOTIFY_SEND_ONCE: 71 */ +simpleroutine +mach_notify_send_once( + _notify : mach_port_move_send_once_t +); + +/* MACH_NOTIFY_DEAD_NAME: 72 */ +simpleroutine +mach_notify_dead_name( + _notify : mach_port_move_send_once_t; + _name : mach_port_name_t +); + +/* highly unlikely additional Mach notifications */ +skip; +skip; +skip; +skip; +skip; + +simpleroutine +wakeup_main_thread( + _port : mach_port_t; + WaitTime _waitTimeout : natural_t +); + +simpleroutine +consume_send_once_right( + _port : mach_port_move_send_once_t +); diff --git a/src/queue.c b/src/queue.c new file mode 100644 index 0000000..a3e8936 --- /dev/null +++ b/src/queue.c @@ -0,0 +1,2080 @@ +/* + * 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@ + */ + +#include "internal.h" +#include "protocol.h" + +void +dummy_function(void) +{ +} + +long +dummy_function_r0(void) +{ + return 0; +} + +static bool _dispatch_select_workaround; +static fd_set _dispatch_rfds; +static fd_set _dispatch_wfds; +static void *_dispatch_rfd_ptrs[FD_SETSIZE]; +static void *_dispatch_wfd_ptrs[FD_SETSIZE]; + + +static struct dispatch_semaphore_s _dispatch_thread_mediator[] = { + { + .do_vtable = &_dispatch_semaphore_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + }, + { + .do_vtable = &_dispatch_semaphore_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + }, + { + .do_vtable = &_dispatch_semaphore_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + }, + { + .do_vtable = &_dispatch_semaphore_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + }, + { + .do_vtable = &_dispatch_semaphore_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + }, + { + .do_vtable = &_dispatch_semaphore_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + }, +}; + +static struct dispatch_queue_s _dispatch_root_queues[]; + +static inline dispatch_queue_t +_dispatch_get_root_queue(long priority, bool overcommit) +{ + if (overcommit) switch (priority) { + case DISPATCH_QUEUE_PRIORITY_LOW: + return &_dispatch_root_queues[1]; + case DISPATCH_QUEUE_PRIORITY_DEFAULT: + return &_dispatch_root_queues[3]; + case DISPATCH_QUEUE_PRIORITY_HIGH: + return &_dispatch_root_queues[5]; + } + switch (priority) { + case DISPATCH_QUEUE_PRIORITY_LOW: + return &_dispatch_root_queues[0]; + case DISPATCH_QUEUE_PRIORITY_DEFAULT: + return &_dispatch_root_queues[2]; + case DISPATCH_QUEUE_PRIORITY_HIGH: + return &_dispatch_root_queues[4]; + default: + return NULL; + } +} + +#ifdef __BLOCKS__ +dispatch_block_t +_dispatch_Block_copy(dispatch_block_t db) +{ + dispatch_block_t rval; + + while (!(rval = Block_copy(db))) { + sleep(1); + } + + return rval; +} +#define _dispatch_Block_copy(x) ((typeof(x))_dispatch_Block_copy(x)) + +void +_dispatch_call_block_and_release(void *block) +{ + void (^b)(void) = block; + b(); + Block_release(b); +} + +void +_dispatch_call_block_and_release2(void *block, void *ctxt) +{ + void (^b)(void*) = block; + b(ctxt); + Block_release(b); +} + +#endif /* __BLOCKS__ */ + +struct dispatch_queue_attr_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_queue_attr_s); +}; + +struct dispatch_queue_attr_s { + DISPATCH_STRUCT_HEADER(dispatch_queue_attr_s, dispatch_queue_attr_vtable_s); + + // Public: + int qa_priority; + void* finalizer_ctxt; + dispatch_queue_finalizer_function_t finalizer_func; + + // Private: + unsigned long qa_flags; +}; + +static int _dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset); + +#define _dispatch_queue_trylock(dq) dispatch_atomic_cmpxchg(&(dq)->dq_running, 0, 1) +static inline void _dispatch_queue_unlock(dispatch_queue_t dq); +static void _dispatch_queue_invoke(dispatch_queue_t dq); +static void _dispatch_queue_serial_drain_till_empty(dispatch_queue_t dq); +static bool _dispatch_queue_wakeup_global(dispatch_queue_t dq); +static struct dispatch_object_s *_dispatch_queue_concurrent_drain_one(dispatch_queue_t dq); + +static bool _dispatch_program_is_probably_callback_driven; + +#if DISPATCH_COCOA_COMPAT +void (*dispatch_begin_thread_4GC)(void) = dummy_function; +void (*dispatch_end_thread_4GC)(void) = dummy_function; +void *(*_dispatch_begin_NSAutoReleasePool)(void) = (void *)dummy_function; +void (*_dispatch_end_NSAutoReleasePool)(void *) = (void *)dummy_function; +static void _dispatch_queue_wakeup_main(void); + +static dispatch_once_t _dispatch_main_q_port_pred; +static bool main_q_is_draining; +static mach_port_t main_q_port; +#endif + +static void _dispatch_cache_cleanup2(void *value); +static void _dispatch_force_cache_cleanup(void); + +static const struct dispatch_queue_vtable_s _dispatch_queue_vtable = { + .do_type = DISPATCH_QUEUE_TYPE, + .do_kind = "queue", + .do_dispose = _dispatch_queue_dispose, + .do_invoke = (void *)dummy_function_r0, + .do_probe = (void *)dummy_function_r0, + .do_debug = dispatch_queue_debug, +}; + +static const struct dispatch_queue_vtable_s _dispatch_queue_root_vtable = { + .do_type = DISPATCH_QUEUE_GLOBAL_TYPE, + .do_kind = "global-queue", + .do_debug = dispatch_queue_debug, + .do_probe = _dispatch_queue_wakeup_global, +}; + +#define MAX_THREAD_COUNT 255 + +struct dispatch_root_queue_context_s { + pthread_workqueue_t dgq_kworkqueue; + uint32_t dgq_pending; + uint32_t dgq_thread_pool_size; + dispatch_semaphore_t dgq_thread_mediator; +}; + +#define DISPATCH_ROOT_QUEUE_COUNT (DISPATCH_QUEUE_PRIORITY_COUNT * 2) +static struct dispatch_root_queue_context_s _dispatch_root_queue_contexts[] = { + { + .dgq_thread_mediator = &_dispatch_thread_mediator[0], + .dgq_thread_pool_size = MAX_THREAD_COUNT, + }, + { + .dgq_thread_mediator = &_dispatch_thread_mediator[1], + .dgq_thread_pool_size = MAX_THREAD_COUNT, + }, + { + .dgq_thread_mediator = &_dispatch_thread_mediator[2], + .dgq_thread_pool_size = MAX_THREAD_COUNT, + }, + { + .dgq_thread_mediator = &_dispatch_thread_mediator[3], + .dgq_thread_pool_size = MAX_THREAD_COUNT, + }, + { + .dgq_thread_mediator = &_dispatch_thread_mediator[4], + .dgq_thread_pool_size = MAX_THREAD_COUNT, + }, + { + .dgq_thread_mediator = &_dispatch_thread_mediator[5], + .dgq_thread_pool_size = MAX_THREAD_COUNT, + }, +}; + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +// dq_running is set to 2 so that barrier operations go through the slow path +static struct dispatch_queue_s _dispatch_root_queues[] = { + { + .do_vtable = &_dispatch_queue_root_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .do_ctxt = &_dispatch_root_queue_contexts[0], + + .dq_label = "com.apple.root.low-priority", + .dq_running = 2, + .dq_width = UINT32_MAX, + .dq_serialnum = 4, + }, + { + .do_vtable = &_dispatch_queue_root_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .do_ctxt = &_dispatch_root_queue_contexts[1], + + .dq_label = "com.apple.root.low-overcommit-priority", + .dq_running = 2, + .dq_width = UINT32_MAX, + .dq_serialnum = 5, + }, + { + .do_vtable = &_dispatch_queue_root_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .do_ctxt = &_dispatch_root_queue_contexts[2], + + .dq_label = "com.apple.root.default-priority", + .dq_running = 2, + .dq_width = UINT32_MAX, + .dq_serialnum = 6, + }, + { + .do_vtable = &_dispatch_queue_root_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .do_ctxt = &_dispatch_root_queue_contexts[3], + + .dq_label = "com.apple.root.default-overcommit-priority", + .dq_running = 2, + .dq_width = UINT32_MAX, + .dq_serialnum = 7, + }, + { + .do_vtable = &_dispatch_queue_root_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .do_ctxt = &_dispatch_root_queue_contexts[4], + + .dq_label = "com.apple.root.high-priority", + .dq_running = 2, + .dq_width = UINT32_MAX, + .dq_serialnum = 8, + }, + { + .do_vtable = &_dispatch_queue_root_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .do_ctxt = &_dispatch_root_queue_contexts[5], + + .dq_label = "com.apple.root.high-overcommit-priority", + .dq_running = 2, + .dq_width = UINT32_MAX, + .dq_serialnum = 9, + }, +}; + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +struct dispatch_queue_s _dispatch_main_q = { + .do_vtable = &_dispatch_queue_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .do_targetq = &_dispatch_root_queues[DISPATCH_ROOT_QUEUE_COUNT / 2], + + .dq_label = "com.apple.main-thread", + .dq_running = 1, + .dq_width = 1, + .dq_serialnum = 1, +}; + +#if DISPATCH_PERF_MON +static OSSpinLock _dispatch_stats_lock; +static size_t _dispatch_bad_ratio; +static struct { + uint64_t time_total; + uint64_t count_total; + uint64_t thread_total; +} _dispatch_stats[65]; // ffs*/fls*() returns zero when no bits are set +static void _dispatch_queue_merge_stats(uint64_t start); +#endif + +static void *_dispatch_worker_thread(void *context); +static void _dispatch_worker_thread2(void *context); + +malloc_zone_t *_dispatch_ccache_zone; + +static inline void +_dispatch_continuation_free(dispatch_continuation_t dc) +{ + dispatch_continuation_t prev_dc = _dispatch_thread_getspecific(dispatch_cache_key); + dc->do_next = prev_dc; + _dispatch_thread_setspecific(dispatch_cache_key, dc); +} + +static inline void +_dispatch_continuation_pop(dispatch_object_t dou) +{ + dispatch_continuation_t dc = dou._dc; + dispatch_group_t dg; + + if (DISPATCH_OBJ_IS_VTABLE(dou._do)) { + return _dispatch_queue_invoke(dou._dq); + } + + // Add the item back to the cache before calling the function. This + // allows the 'hot' continuation to be used for a quick callback. + // + // The ccache version is per-thread. + // Therefore, the object has not been reused yet. + // This generates better assembly. + if ((long)dou._do->do_vtable & DISPATCH_OBJ_ASYNC_BIT) { + _dispatch_continuation_free(dc); + } + if ((long)dou._do->do_vtable & DISPATCH_OBJ_GROUP_BIT) { + dg = dc->dc_group; + } else { + dg = NULL; + } + dc->dc_func(dc->dc_ctxt); + if (dg) { + dispatch_group_leave(dg); + _dispatch_release(dg); + } +} + +struct dispatch_object_s * +_dispatch_queue_concurrent_drain_one(dispatch_queue_t dq) +{ + struct dispatch_object_s *head, *next, *const mediator = (void *)~0ul; + + // The mediator value acts both as a "lock" and a signal + head = dispatch_atomic_xchg(&dq->dq_items_head, mediator); + + if (slowpath(head == NULL)) { + // The first xchg on the tail will tell the enqueueing thread that it + // is safe to blindly write out to the head pointer. A cmpxchg honors + // the algorithm. + dispatch_atomic_cmpxchg(&dq->dq_items_head, mediator, NULL); + _dispatch_debug("no work on global work queue"); + return NULL; + } + + if (slowpath(head == mediator)) { + // This thread lost the race for ownership of the queue. + // + // The ratio of work to libdispatch overhead must be bad. This + // scenario implies that there are too many threads in the pool. + // Create a new pending thread and then exit this thread. + // The kernel will grant a new thread when the load subsides. + _dispatch_debug("Contention on queue: %p", dq); + _dispatch_queue_wakeup_global(dq); +#if DISPATCH_PERF_MON + dispatch_atomic_inc(&_dispatch_bad_ratio); +#endif + return NULL; + } + + // Restore the head pointer to a sane value before returning. + // If 'next' is NULL, then this item _might_ be the last item. + next = fastpath(head->do_next); + + if (slowpath(!next)) { + dq->dq_items_head = NULL; + + if (dispatch_atomic_cmpxchg(&dq->dq_items_tail, head, NULL)) { + // both head and tail are NULL now + goto out; + } + + // There must be a next item now. This thread won't wait long. + while (!(next = head->do_next)) { + _dispatch_hardware_pause(); + } + } + + dq->dq_items_head = next; + _dispatch_queue_wakeup_global(dq); +out: + return head; +} + +dispatch_queue_t +dispatch_get_current_queue(void) +{ + return _dispatch_queue_get_current() ?: _dispatch_get_root_queue(0, true); +} + +#undef dispatch_get_main_queue +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +dispatch_queue_t dispatch_get_main_queue(void); + +dispatch_queue_t +dispatch_get_main_queue(void) +{ + return &_dispatch_main_q; +} +#define dispatch_get_main_queue() (&_dispatch_main_q) + +struct _dispatch_hw_config_s _dispatch_hw_config; + +static void +_dispatch_queue_set_width_init(void) +{ + size_t valsz = sizeof(uint32_t); + + errno = 0; + sysctlbyname("hw.activecpu", &_dispatch_hw_config.cc_max_active, &valsz, NULL, 0); + dispatch_assume_zero(errno); + dispatch_assume(valsz == sizeof(uint32_t)); + + errno = 0; + sysctlbyname("hw.logicalcpu_max", &_dispatch_hw_config.cc_max_logical, &valsz, NULL, 0); + dispatch_assume_zero(errno); + dispatch_assume(valsz == sizeof(uint32_t)); + + errno = 0; + sysctlbyname("hw.physicalcpu_max", &_dispatch_hw_config.cc_max_physical, &valsz, NULL, 0); + dispatch_assume_zero(errno); + dispatch_assume(valsz == sizeof(uint32_t)); +} + +void +dispatch_queue_set_width(dispatch_queue_t dq, long width) +{ + int w = (int)width; // intentional truncation + uint32_t tmp; + + if (slowpath(dq->do_ref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { + return; + } + if (w == 1 || w == 0) { + dq->dq_width = 1; + return; + } + if (w > 0) { + tmp = w; + } else switch (w) { + case DISPATCH_QUEUE_WIDTH_MAX_PHYSICAL_CPUS: + tmp = _dispatch_hw_config.cc_max_physical; + break; + case DISPATCH_QUEUE_WIDTH_ACTIVE_CPUS: + tmp = _dispatch_hw_config.cc_max_active; + break; + default: + // fall through + case DISPATCH_QUEUE_WIDTH_MAX_LOGICAL_CPUS: + tmp = _dispatch_hw_config.cc_max_logical; + break; + } + // multiply by two since the running count is inc/dec by two (the low bit == barrier) + dq->dq_width = tmp * 2; + + // XXX if the queue has items and the width is increased, we should try to wake the queue +} + +// skip zero +// 1 - main_q +// 2 - mgr_q +// 3 - _unused_ +// 4,5,6,7,8,9 - global queues +// we use 'xadd' on Intel, so the initial value == next assigned +static unsigned long _dispatch_queue_serial_numbers = 10; + +// Note to later developers: ensure that any initialization changes are +// made for statically allocated queues (i.e. _dispatch_main_q). +inline void +_dispatch_queue_init(dispatch_queue_t dq) +{ + dq->do_vtable = &_dispatch_queue_vtable; + dq->do_next = DISPATCH_OBJECT_LISTLESS; + dq->do_ref_cnt = 1; + dq->do_xref_cnt = 1; + dq->do_targetq = _dispatch_get_root_queue(0, true); + dq->dq_running = 0; + dq->dq_width = 1; + dq->dq_serialnum = dispatch_atomic_inc(&_dispatch_queue_serial_numbers) - 1; +} + +dispatch_queue_t +dispatch_queue_create(const char *label, dispatch_queue_attr_t attr) +{ + dispatch_queue_t dq; + size_t label_len; + + if (!label) { + label = ""; + } + + label_len = strlen(label); + if (label_len < (DISPATCH_QUEUE_MIN_LABEL_SIZE - 1)) { + label_len = (DISPATCH_QUEUE_MIN_LABEL_SIZE - 1); + } + + // XXX switch to malloc() + dq = calloc(1ul, sizeof(struct dispatch_queue_s) - DISPATCH_QUEUE_MIN_LABEL_SIZE + label_len + 1); + if (slowpath(!dq)) { + return dq; + } + + _dispatch_queue_init(dq); + strcpy(dq->dq_label, label); + +#ifndef DISPATCH_NO_LEGACY + if (slowpath(attr)) { + dq->do_targetq = _dispatch_get_root_queue(attr->qa_priority, attr->qa_flags & DISPATCH_QUEUE_OVERCOMMIT); + dq->dq_finalizer_ctxt = attr->finalizer_ctxt; + dq->dq_finalizer_func = attr->finalizer_func; +#ifdef __BLOCKS__ + if (attr->finalizer_func == (void*)_dispatch_call_block_and_release2) { + // if finalizer_ctxt is a Block, retain it. + dq->dq_finalizer_ctxt = Block_copy(dq->dq_finalizer_ctxt); + if (!(dq->dq_finalizer_ctxt)) { + goto out_bad; + } + } +#endif + } +#endif + + return dq; + +out_bad: + free(dq); + return NULL; +} + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +void +_dispatch_queue_dispose(dispatch_queue_t dq) +{ + if (slowpath(dq == _dispatch_queue_get_current())) { + DISPATCH_CRASH("Release of a queue by itself"); + } + if (slowpath(dq->dq_items_tail)) { + DISPATCH_CRASH("Release of a queue while items are enqueued"); + } + +#ifndef DISPATCH_NO_LEGACY + if (dq->dq_finalizer_func) { + dq->dq_finalizer_func(dq->dq_finalizer_ctxt, dq); + } +#endif + + // trash the tail queue so that use after free will crash + dq->dq_items_tail = (void *)0x200; + + _dispatch_dispose(dq); +} + +DISPATCH_NOINLINE +static void +_dispatch_barrier_async_f_slow(dispatch_queue_t dq, void *context, dispatch_function_t func) +{ + dispatch_continuation_t dc = fastpath(_dispatch_continuation_alloc_from_heap()); + + dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_BARRIER_BIT); + dc->dc_func = func; + dc->dc_ctxt = context; + + _dispatch_queue_push(dq, dc); +} + +#ifdef __BLOCKS__ +void +dispatch_barrier_async(dispatch_queue_t dq, void (^work)(void)) +{ + dispatch_barrier_async_f(dq, _dispatch_Block_copy(work), _dispatch_call_block_and_release); +} +#endif + +DISPATCH_NOINLINE +void +dispatch_barrier_async_f(dispatch_queue_t dq, void *context, dispatch_function_t func) +{ + dispatch_continuation_t dc = fastpath(_dispatch_continuation_alloc_cacheonly()); + + if (!dc) { + return _dispatch_barrier_async_f_slow(dq, context, func); + } + + dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT | DISPATCH_OBJ_BARRIER_BIT); + dc->dc_func = func; + dc->dc_ctxt = context; + + _dispatch_queue_push(dq, dc); +} + +DISPATCH_NOINLINE +static void +_dispatch_async_f_slow(dispatch_queue_t dq, void *context, dispatch_function_t func) +{ + dispatch_continuation_t dc = fastpath(_dispatch_continuation_alloc_from_heap()); + + dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT; + dc->dc_func = func; + dc->dc_ctxt = context; + + _dispatch_queue_push(dq, dc); +} + +#ifdef __BLOCKS__ +void +dispatch_async(dispatch_queue_t dq, void (^work)(void)) +{ + dispatch_async_f(dq, _dispatch_Block_copy(work), _dispatch_call_block_and_release); +} +#endif + +DISPATCH_NOINLINE +void +dispatch_async_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) +{ + dispatch_continuation_t dc = fastpath(_dispatch_continuation_alloc_cacheonly()); + + // unlike dispatch_sync_f(), we do NOT need to check the queue width, + // the "drain" function will do this test + + if (!dc) { + return _dispatch_async_f_slow(dq, ctxt, func); + } + + dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT; + dc->dc_func = func; + dc->dc_ctxt = ctxt; + + _dispatch_queue_push(dq, dc); +} + +struct dispatch_barrier_sync_slow2_s { + dispatch_function_t dbss2_func; + dispatch_function_t dbss2_ctxt; + dispatch_semaphore_t dbss2_sema; +}; + +static void +_dispatch_barrier_sync_f_slow_invoke(void *ctxt) +{ + struct dispatch_barrier_sync_slow2_s *dbss2 = ctxt; + + dbss2->dbss2_func(dbss2->dbss2_ctxt); + dispatch_semaphore_signal(dbss2->dbss2_sema); +} + +DISPATCH_NOINLINE +static void +_dispatch_barrier_sync_f_slow(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) +{ + struct dispatch_barrier_sync_slow2_s dbss2 = { + .dbss2_func = func, + .dbss2_ctxt = ctxt, + .dbss2_sema = _dispatch_get_thread_semaphore(), + }; + struct dispatch_barrier_sync_slow_s { + DISPATCH_CONTINUATION_HEADER(dispatch_barrier_sync_slow_s); + } dbss = { + .do_vtable = (void *)DISPATCH_OBJ_BARRIER_BIT, + .dc_func = _dispatch_barrier_sync_f_slow_invoke, + .dc_ctxt = &dbss2, + }; + + _dispatch_queue_push(dq, (void *)&dbss); + + while (dispatch_semaphore_wait(dbss2.dbss2_sema, dispatch_time(0, 3ull * NSEC_PER_SEC))) { + if (DISPATCH_OBJECT_SUSPENDED(dq)) { + continue; + } + if (_dispatch_queue_trylock(dq)) { + _dispatch_queue_drain(dq); + _dispatch_queue_unlock(dq); + } + } + _dispatch_put_thread_semaphore(dbss2.dbss2_sema); +} + +#ifdef __BLOCKS__ +void +dispatch_barrier_sync(dispatch_queue_t dq, void (^work)(void)) +{ + struct Block_basic *bb = (void *)work; + + dispatch_barrier_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke); +} +#endif + +DISPATCH_NOINLINE +void +dispatch_barrier_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) +{ + dispatch_queue_t old_dq = _dispatch_thread_getspecific(dispatch_queue_key); + + // 1) ensure that this thread hasn't enqueued anything ahead of this call + // 2) the queue is not suspended + // 3) the queue is not weird + if (slowpath(dq->dq_items_tail) + || slowpath(DISPATCH_OBJECT_SUSPENDED(dq)) + || slowpath(!_dispatch_queue_trylock(dq))) { + return _dispatch_barrier_sync_f_slow(dq, ctxt, func); + } + + _dispatch_thread_setspecific(dispatch_queue_key, dq); + func(ctxt); + _dispatch_workitem_inc(); + _dispatch_thread_setspecific(dispatch_queue_key, old_dq); + _dispatch_queue_unlock(dq); +} + +static void +_dispatch_sync_f_slow2(void *ctxt) +{ + dispatch_queue_t dq = _dispatch_queue_get_current(); + dispatch_atomic_add(&dq->dq_running, 2); + dispatch_semaphore_signal(ctxt); +} + +DISPATCH_NOINLINE +static void +_dispatch_sync_f_slow(dispatch_queue_t dq) +{ + // the global root queues do not need strict ordering + if (dq->do_targetq == NULL) { + dispatch_atomic_add(&dq->dq_running, 2); + return; + } + + struct dispatch_sync_slow_s { + DISPATCH_CONTINUATION_HEADER(dispatch_sync_slow_s); + } dss = { + .do_vtable = NULL, + .dc_func = _dispatch_sync_f_slow2, + .dc_ctxt = _dispatch_get_thread_semaphore(), + }; + + // XXX FIXME -- concurrent queues can be come serial again + _dispatch_queue_push(dq, (void *)&dss); + + dispatch_semaphore_wait(dss.dc_ctxt, DISPATCH_TIME_FOREVER); + _dispatch_put_thread_semaphore(dss.dc_ctxt); +} + +#ifdef __BLOCKS__ +void +dispatch_sync(dispatch_queue_t dq, void (^work)(void)) +{ + struct Block_basic *bb = (void *)work; + dispatch_sync_f(dq, work, (dispatch_function_t)bb->Block_invoke); +} +#endif + +DISPATCH_NOINLINE +void +dispatch_sync_f(dispatch_queue_t dq, void *ctxt, dispatch_function_t func) +{ + typeof(dq->dq_running) prev_cnt; + dispatch_queue_t old_dq; + + if (dq->dq_width == 1) { + return dispatch_barrier_sync_f(dq, ctxt, func); + } + + // 1) ensure that this thread hasn't enqueued anything ahead of this call + // 2) the queue is not suspended + if (slowpath(dq->dq_items_tail) || slowpath(DISPATCH_OBJECT_SUSPENDED(dq))) { + _dispatch_sync_f_slow(dq); + } else { + prev_cnt = dispatch_atomic_add(&dq->dq_running, 2) - 2; + + if (slowpath(prev_cnt & 1)) { + if (dispatch_atomic_sub(&dq->dq_running, 2) == 0) { + _dispatch_wakeup(dq); + } + _dispatch_sync_f_slow(dq); + } + } + + old_dq = _dispatch_thread_getspecific(dispatch_queue_key); + _dispatch_thread_setspecific(dispatch_queue_key, dq); + func(ctxt); + _dispatch_workitem_inc(); + _dispatch_thread_setspecific(dispatch_queue_key, old_dq); + + if (slowpath(dispatch_atomic_sub(&dq->dq_running, 2) == 0)) { + _dispatch_wakeup(dq); + } +} + +const char * +dispatch_queue_get_label(dispatch_queue_t dq) +{ + return dq->dq_label; +} + +#if DISPATCH_COCOA_COMPAT +static void +_dispatch_main_q_port_init(void *ctxt __attribute__((unused))) +{ + kern_return_t kr; + + kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &main_q_port); + DISPATCH_VERIFY_MIG(kr); + dispatch_assume_zero(kr); + kr = mach_port_insert_right(mach_task_self(), main_q_port, main_q_port, MACH_MSG_TYPE_MAKE_SEND); + DISPATCH_VERIFY_MIG(kr); + dispatch_assume_zero(kr); + + _dispatch_program_is_probably_callback_driven = true; + _dispatch_safe_fork = false; +} + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +DISPATCH_NOINLINE +static void +_dispatch_queue_set_mainq_drain_state(bool arg) +{ + main_q_is_draining = arg; +} +#endif + +void +dispatch_main(void) +{ + if (pthread_main_np()) { + _dispatch_program_is_probably_callback_driven = true; + pthread_exit(NULL); + DISPATCH_CRASH("pthread_exit() returned"); + } + DISPATCH_CLIENT_CRASH("dispatch_main() must be called on the main thread"); +} + +static void +_dispatch_sigsuspend(void *ctxt __attribute__((unused))) +{ + static const sigset_t mask; + + for (;;) { + sigsuspend(&mask); + } +} + +DISPATCH_NOINLINE +static void +_dispatch_queue_cleanup2(void) +{ + dispatch_atomic_dec(&_dispatch_main_q.dq_running); + + if (dispatch_atomic_sub(&_dispatch_main_q.do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_LOCK) == 0) { + _dispatch_wakeup(&_dispatch_main_q); + } + + // overload the "probably" variable to mean that dispatch_main() or + // similar non-POSIX API was called + // this has to run before the DISPATCH_COCOA_COMPAT below + if (_dispatch_program_is_probably_callback_driven) { + dispatch_async_f(_dispatch_get_root_queue(0, 0), NULL, _dispatch_sigsuspend); + sleep(1); // workaround 6778970 + } + +#if DISPATCH_COCOA_COMPAT + dispatch_once_f(&_dispatch_main_q_port_pred, NULL, _dispatch_main_q_port_init); + + mach_port_t mp = main_q_port; + kern_return_t kr; + + main_q_port = 0; + + if (mp) { + kr = mach_port_deallocate(mach_task_self(), mp); + DISPATCH_VERIFY_MIG(kr); + dispatch_assume_zero(kr); + kr = mach_port_mod_refs(mach_task_self(), mp, MACH_PORT_RIGHT_RECEIVE, -1); + DISPATCH_VERIFY_MIG(kr); + dispatch_assume_zero(kr); + } +#endif +} + +dispatch_queue_t +dispatch_get_concurrent_queue(long pri) +{ + if (pri > 0) { + pri = DISPATCH_QUEUE_PRIORITY_HIGH; + } else if (pri < 0) { + pri = DISPATCH_QUEUE_PRIORITY_LOW; + } + return _dispatch_get_root_queue(pri, false); +} + +static void +_dispatch_queue_cleanup(void *ctxt) +{ + if (ctxt == &_dispatch_main_q) { + return _dispatch_queue_cleanup2(); + } + // POSIX defines that destructors are only called if 'ctxt' is non-null + DISPATCH_CRASH("Premature thread exit while a dispatch queue is running"); +} + +dispatch_queue_t +dispatch_get_global_queue(long priority, unsigned long flags) +{ + if (flags & ~DISPATCH_QUEUE_OVERCOMMIT) { + return NULL; + } + return _dispatch_get_root_queue(priority, flags & DISPATCH_QUEUE_OVERCOMMIT); +} + +#define countof(x) (sizeof(x) / sizeof(x[0])) +void +libdispatch_init(void) +{ + dispatch_assert(DISPATCH_QUEUE_PRIORITY_COUNT == 3); + dispatch_assert(DISPATCH_ROOT_QUEUE_COUNT == 6); + + dispatch_assert(DISPATCH_QUEUE_PRIORITY_LOW == -DISPATCH_QUEUE_PRIORITY_HIGH); + dispatch_assert(countof(_dispatch_root_queues) == DISPATCH_ROOT_QUEUE_COUNT); + dispatch_assert(countof(_dispatch_thread_mediator) == DISPATCH_ROOT_QUEUE_COUNT); + dispatch_assert(countof(_dispatch_root_queue_contexts) == DISPATCH_ROOT_QUEUE_COUNT); + + _dispatch_thread_key_init_np(dispatch_queue_key, _dispatch_queue_cleanup); + _dispatch_thread_key_init_np(dispatch_sema4_key, (void (*)(void *))dispatch_release); // use the extern release + _dispatch_thread_key_init_np(dispatch_cache_key, _dispatch_cache_cleanup2); +#if DISPATCH_PERF_MON + _dispatch_thread_key_init_np(dispatch_bcounter_key, NULL); +#endif + + _dispatch_thread_setspecific(dispatch_queue_key, &_dispatch_main_q); + + _dispatch_queue_set_width_init(); +} + +void +_dispatch_queue_unlock(dispatch_queue_t dq) +{ + if (slowpath(dispatch_atomic_dec(&dq->dq_running))) { + return; + } + + _dispatch_wakeup(dq); +} + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +dispatch_queue_t +_dispatch_wakeup(dispatch_object_t dou) +{ + dispatch_queue_t tq; + + if (slowpath(DISPATCH_OBJECT_SUSPENDED(dou._do))) { + return NULL; + } + if (!dx_probe(dou._do) && !dou._dq->dq_items_tail) { + return NULL; + } + + if (!_dispatch_trylock(dou._do)) { +#if DISPATCH_COCOA_COMPAT + if (dou._dq == &_dispatch_main_q) { + _dispatch_queue_wakeup_main(); + } +#endif + return NULL; + } + _dispatch_retain(dou._do); + tq = dou._do->do_targetq; + _dispatch_queue_push(tq, dou._do); + return tq; // libdispatch doesn't need this, but the Instrument DTrace probe does +} + +#if DISPATCH_COCOA_COMPAT +DISPATCH_NOINLINE +void +_dispatch_queue_wakeup_main(void) +{ + kern_return_t kr; + + dispatch_once_f(&_dispatch_main_q_port_pred, NULL, _dispatch_main_q_port_init); + + kr = _dispatch_send_wakeup_main_thread(main_q_port, 0); + + switch (kr) { + case MACH_SEND_TIMEOUT: + case MACH_SEND_TIMED_OUT: + case MACH_SEND_INVALID_DEST: + break; + default: + dispatch_assume_zero(kr); + break; + } + + _dispatch_safe_fork = false; +} +#endif + +static inline int +_dispatch_rootq2wq_pri(long idx) +{ +#ifdef WORKQ_DEFAULT_PRIOQUEUE + switch (idx) { + case 0: + case 1: + return WORKQ_LOW_PRIOQUEUE; + case 2: + case 3: + default: + return WORKQ_DEFAULT_PRIOQUEUE; + case 4: + case 5: + return WORKQ_HIGH_PRIOQUEUE; + } +#else + return pri; +#endif +} + +static void +_dispatch_root_queues_init(void *context __attribute__((unused))) +{ + bool disable_wq = getenv("LIBDISPATCH_DISABLE_KWQ"); + pthread_workqueue_attr_t pwq_attr; + kern_return_t kr; + int i, r; + + r = pthread_workqueue_attr_init_np(&pwq_attr); + dispatch_assume_zero(r); + + for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) { + r = pthread_workqueue_attr_setqueuepriority_np(&pwq_attr, _dispatch_rootq2wq_pri(i)); + dispatch_assume_zero(r); + r = pthread_workqueue_attr_setovercommit_np(&pwq_attr, i & 1); + dispatch_assume_zero(r); +// some software hangs if the non-overcommitting queues do not overcommit when threads block +#if 0 + if (!(i & 1)) { + dispatch_root_queue_contexts[i].dgq_thread_pool_size = _dispatch_hw_config.cc_max_active; + } +#endif + + r = 0; + if (disable_wq || (r = pthread_workqueue_create_np(&_dispatch_root_queue_contexts[i].dgq_kworkqueue, &pwq_attr))) { + if (r != ENOTSUP) { + dispatch_assume_zero(r); + } + // override the default FIFO behavior for the pool semaphores + kr = semaphore_create(mach_task_self(), &_dispatch_thread_mediator[i].dsema_port, SYNC_POLICY_LIFO, 0); + DISPATCH_VERIFY_MIG(kr); + dispatch_assume_zero(kr); + dispatch_assume(_dispatch_thread_mediator[i].dsema_port); + } else { + dispatch_assume(_dispatch_root_queue_contexts[i].dgq_kworkqueue); + } + } + + r = pthread_workqueue_attr_destroy_np(&pwq_attr); + dispatch_assume_zero(r); +} + +bool +_dispatch_queue_wakeup_global(dispatch_queue_t dq) +{ + static dispatch_once_t pred; + struct dispatch_root_queue_context_s *qc = dq->do_ctxt; + pthread_workitem_handle_t wh; + unsigned int gen_cnt; + pthread_t pthr; + int r, t_count; + + if (!dq->dq_items_tail) { + return false; + } + + _dispatch_safe_fork = false; + + dispatch_debug_queue(dq, __PRETTY_FUNCTION__); + + dispatch_once_f(&pred, NULL, _dispatch_root_queues_init); + + if (qc->dgq_kworkqueue) { + if (dispatch_atomic_cmpxchg(&qc->dgq_pending, 0, 1)) { + _dispatch_debug("requesting new worker thread"); + + r = pthread_workqueue_additem_np(qc->dgq_kworkqueue, _dispatch_worker_thread2, dq, &wh, &gen_cnt); + dispatch_assume_zero(r); + } else { + _dispatch_debug("work thread request still pending on global queue: %p", dq); + } + goto out; + } + + if (dispatch_semaphore_signal(qc->dgq_thread_mediator)) { + goto out; + } + + do { + t_count = qc->dgq_thread_pool_size; + if (!t_count) { + _dispatch_debug("The thread pool is full: %p", dq); + goto out; + } + } while (!dispatch_atomic_cmpxchg(&qc->dgq_thread_pool_size, t_count, t_count - 1)); + + while ((r = pthread_create(&pthr, NULL, _dispatch_worker_thread, dq))) { + if (r != EAGAIN) { + dispatch_assume_zero(r); + } + sleep(1); + } + r = pthread_detach(pthr); + dispatch_assume_zero(r); + +out: + return false; +} + +void +_dispatch_queue_serial_drain_till_empty(dispatch_queue_t dq) +{ +#if DISPATCH_PERF_MON + uint64_t start = mach_absolute_time(); +#endif + _dispatch_queue_drain(dq); +#if DISPATCH_PERF_MON + _dispatch_queue_merge_stats(start); +#endif + _dispatch_force_cache_cleanup(); +} + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +DISPATCH_NOINLINE +void +_dispatch_queue_invoke(dispatch_queue_t dq) +{ + dispatch_queue_t tq = dq->do_targetq; + + if (!slowpath(DISPATCH_OBJECT_SUSPENDED(dq)) && fastpath(_dispatch_queue_trylock(dq))) { + _dispatch_queue_drain(dq); + if (tq == dq->do_targetq) { + tq = dx_invoke(dq); + } else { + tq = dq->do_targetq; + } + // We do not need to check the result. + // When the suspend-count lock is dropped, then the check will happen. + dispatch_atomic_dec(&dq->dq_running); + if (tq) { + return _dispatch_queue_push(tq, dq); + } + } + + dq->do_next = DISPATCH_OBJECT_LISTLESS; + if (dispatch_atomic_sub(&dq->do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_LOCK) == 0) { + if (dq->dq_running == 0) { + _dispatch_wakeup(dq); // verify that the queue is idle + } + } + _dispatch_release(dq); // added when the queue is put on the list +} + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +static void +_dispatch_set_target_queue2(void *ctxt) +{ + dispatch_queue_t prev_dq, dq = _dispatch_queue_get_current(); + + prev_dq = dq->do_targetq; + dq->do_targetq = ctxt; + _dispatch_release(prev_dq); +} + +void +dispatch_set_target_queue(dispatch_object_t dou, dispatch_queue_t dq) +{ + if (slowpath(dou._do->do_xref_cnt == DISPATCH_OBJECT_GLOBAL_REFCNT)) { + return; + } + // NOTE: we test for NULL target queues internally to detect root queues + // therefore, if the retain crashes due to a bad input, that is OK + _dispatch_retain(dq); + dispatch_barrier_async_f(dou._dq, dq, _dispatch_set_target_queue2); +} + +static void +_dispatch_async_f_redirect2(void *_ctxt) +{ + struct dispatch_continuation_s *dc = _ctxt; + struct dispatch_continuation_s *other_dc = dc->dc_data[1]; + dispatch_queue_t old_dq, dq = dc->dc_data[0]; + + old_dq = _dispatch_thread_getspecific(dispatch_queue_key); + _dispatch_thread_setspecific(dispatch_queue_key, dq); + _dispatch_continuation_pop(other_dc); + _dispatch_thread_setspecific(dispatch_queue_key, old_dq); + + if (dispatch_atomic_sub(&dq->dq_running, 2) == 0) { + _dispatch_wakeup(dq); + } + _dispatch_release(dq); +} + +static void +_dispatch_async_f_redirect(dispatch_queue_t dq, struct dispatch_object_s *other_dc) +{ + dispatch_continuation_t dc = (void *)other_dc; + dispatch_queue_t root_dq = dq; + + if (dc->dc_func == _dispatch_sync_f_slow2) { + return dc->dc_func(dc->dc_ctxt); + } + + dispatch_atomic_add(&dq->dq_running, 2); + _dispatch_retain(dq); + + dc = _dispatch_continuation_alloc_cacheonly() ?: _dispatch_continuation_alloc_from_heap(); + + dc->do_vtable = (void *)DISPATCH_OBJ_ASYNC_BIT; + dc->dc_func = _dispatch_async_f_redirect2; + dc->dc_ctxt = dc; + dc->dc_data[0] = dq; + dc->dc_data[1] = other_dc; + + do { + root_dq = root_dq->do_targetq; + } while (root_dq->do_targetq); + + _dispatch_queue_push(root_dq, dc); +} + + +void +_dispatch_queue_drain(dispatch_queue_t dq) +{ + dispatch_queue_t orig_tq, old_dq = _dispatch_thread_getspecific(dispatch_queue_key); + struct dispatch_object_s *dc = NULL, *next_dc = NULL; + + orig_tq = dq->do_targetq; + + _dispatch_thread_setspecific(dispatch_queue_key, dq); + + while (dq->dq_items_tail) { + while (!fastpath(dq->dq_items_head)) { + _dispatch_hardware_pause(); + } + + dc = dq->dq_items_head; + dq->dq_items_head = NULL; + + do { + // Enqueue is TIGHTLY controlled, we won't wait long. + do { + next_dc = fastpath(dc->do_next); + } while (!next_dc && !dispatch_atomic_cmpxchg(&dq->dq_items_tail, dc, NULL)); + if (DISPATCH_OBJECT_SUSPENDED(dq)) { + goto out; + } + if (dq->dq_running > dq->dq_width) { + goto out; + } + if (orig_tq != dq->do_targetq) { + goto out; + } + if (fastpath(dq->dq_width == 1)) { + _dispatch_continuation_pop(dc); + _dispatch_workitem_inc(); + } else if ((long)dc->do_vtable & DISPATCH_OBJ_BARRIER_BIT) { + if (dq->dq_running > 1) { + goto out; + } + _dispatch_continuation_pop(dc); + _dispatch_workitem_inc(); + } else { + _dispatch_async_f_redirect(dq, dc); + } + } while ((dc = next_dc)); + } + +out: + // if this is not a complete drain, we must undo some things + if (slowpath(dc)) { + // 'dc' must NOT be "popped" + // 'dc' might be the last item + if (next_dc || dispatch_atomic_cmpxchg(&dq->dq_items_tail, NULL, dc)) { + dq->dq_items_head = dc; + } else { + while (!(next_dc = dq->dq_items_head)) { + _dispatch_hardware_pause(); + } + dq->dq_items_head = dc; + dc->do_next = next_dc; + } + } + + _dispatch_thread_setspecific(dispatch_queue_key, old_dq); +} + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +void * +_dispatch_worker_thread(void *context) +{ + dispatch_queue_t dq = context; + struct dispatch_root_queue_context_s *qc = dq->do_ctxt; + sigset_t mask; + int r; + + // workaround tweaks the kernel workqueue does for us + r = sigfillset(&mask); + dispatch_assume_zero(r); + r = _dispatch_pthread_sigmask(SIG_BLOCK, &mask, NULL); + dispatch_assume_zero(r); + + do { + _dispatch_worker_thread2(context); + // we use 65 seconds in case there are any timers that run once a minute + } while (dispatch_semaphore_wait(qc->dgq_thread_mediator, dispatch_time(0, 65ull * NSEC_PER_SEC)) == 0); + + dispatch_atomic_inc(&qc->dgq_thread_pool_size); + if (dq->dq_items_tail) { + _dispatch_queue_wakeup_global(dq); + } + + return NULL; +} + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +void +_dispatch_worker_thread2(void *context) +{ + struct dispatch_object_s *item; + dispatch_queue_t dq = context; + struct dispatch_root_queue_context_s *qc = dq->do_ctxt; + + if (_dispatch_thread_getspecific(dispatch_queue_key)) { + DISPATCH_CRASH("Premature thread recycling"); + } + + _dispatch_thread_setspecific(dispatch_queue_key, dq); + qc->dgq_pending = 0; + +#if DISPATCH_COCOA_COMPAT + // ensure that high-level memory management techniques do not leak/crash + dispatch_begin_thread_4GC(); + void *pool = _dispatch_begin_NSAutoReleasePool(); +#endif + +#if DISPATCH_PERF_MON + uint64_t start = mach_absolute_time(); +#endif + while ((item = fastpath(_dispatch_queue_concurrent_drain_one(dq)))) { + _dispatch_continuation_pop(item); + } +#if DISPATCH_PERF_MON + _dispatch_queue_merge_stats(start); +#endif + +#if DISPATCH_COCOA_COMPAT + _dispatch_end_NSAutoReleasePool(pool); + dispatch_end_thread_4GC(); +#endif + + _dispatch_thread_setspecific(dispatch_queue_key, NULL); + + _dispatch_force_cache_cleanup(); +} + +#if DISPATCH_PERF_MON +void +_dispatch_queue_merge_stats(uint64_t start) +{ + uint64_t avg, delta = mach_absolute_time() - start; + unsigned long count, bucket; + + count = (size_t)_dispatch_thread_getspecific(dispatch_bcounter_key); + _dispatch_thread_setspecific(dispatch_bcounter_key, NULL); + + if (count) { + avg = delta / count; + bucket = flsll(avg); + } else { + bucket = 0; + } + + // 64-bit counters on 32-bit require a lock or a queue + OSSpinLockLock(&_dispatch_stats_lock); + + _dispatch_stats[bucket].time_total += delta; + _dispatch_stats[bucket].count_total += count; + _dispatch_stats[bucket].thread_total++; + + OSSpinLockUnlock(&_dispatch_stats_lock); +} +#endif + +size_t +dispatch_queue_debug_attr(dispatch_queue_t dq, char* buf, size_t bufsiz) +{ + return snprintf(buf, bufsiz, "parent = %p ", dq->do_targetq); +} + +size_t +dispatch_queue_debug(dispatch_queue_t dq, char* buf, size_t bufsiz) +{ + size_t offset = 0; + offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", dq->dq_label, dq); + offset += dispatch_object_debug_attr(dq, &buf[offset], bufsiz - offset); + offset += dispatch_queue_debug_attr(dq, &buf[offset], bufsiz - offset); + offset += snprintf(&buf[offset], bufsiz - offset, "}"); + return offset; +} + +#if DISPATCH_DEBUG +void +dispatch_debug_queue(dispatch_queue_t dq, const char* str) { + if (fastpath(dq)) { + dispatch_debug(dq, "%s", str); + } else { + _dispatch_log("queue[NULL]: %s", str); + } +} +#endif + +#if DISPATCH_COCOA_COMPAT +void +_dispatch_main_queue_callback_4CF(mach_msg_header_t *msg __attribute__((unused))) +{ + if (main_q_is_draining) { + return; + } + _dispatch_queue_set_mainq_drain_state(true); + _dispatch_queue_serial_drain_till_empty(&_dispatch_main_q); + _dispatch_queue_set_mainq_drain_state(false); +} + +mach_port_t +_dispatch_get_main_queue_port_4CF(void) +{ + dispatch_once_f(&_dispatch_main_q_port_pred, NULL, _dispatch_main_q_port_init); + return main_q_port; +} +#endif + +static void +dispatch_queue_attr_dispose(dispatch_queue_attr_t attr) +{ + dispatch_queue_attr_set_finalizer(attr, NULL); + _dispatch_dispose(attr); +} + +static const struct dispatch_queue_attr_vtable_s dispatch_queue_attr_vtable = { + .do_type = DISPATCH_QUEUE_ATTR_TYPE, + .do_kind = "queue-attr", + .do_dispose = dispatch_queue_attr_dispose, +}; + +dispatch_queue_attr_t +dispatch_queue_attr_create(void) +{ + dispatch_queue_attr_t a = calloc(1, sizeof(struct dispatch_queue_attr_s)); + + if (a) { + a->do_vtable = &dispatch_queue_attr_vtable; + a->do_next = DISPATCH_OBJECT_LISTLESS; + a->do_ref_cnt = 1; + a->do_xref_cnt = 1; + a->do_targetq = _dispatch_get_root_queue(0, 0); + a->qa_flags = DISPATCH_QUEUE_OVERCOMMIT; + } + return a; +} + +void +dispatch_queue_attr_set_flags(dispatch_queue_attr_t attr, uint64_t flags) +{ + dispatch_assert_zero(flags & ~DISPATCH_QUEUE_FLAGS_MASK); + attr->qa_flags = (unsigned long)flags & DISPATCH_QUEUE_FLAGS_MASK; +} + +void +dispatch_queue_attr_set_priority(dispatch_queue_attr_t attr, int priority) +{ + dispatch_debug_assert(attr, "NULL pointer"); + dispatch_debug_assert(priority <= DISPATCH_QUEUE_PRIORITY_HIGH && priority >= DISPATCH_QUEUE_PRIORITY_LOW, "Invalid priority"); + + if (priority > 0) { + priority = DISPATCH_QUEUE_PRIORITY_HIGH; + } else if (priority < 0) { + priority = DISPATCH_QUEUE_PRIORITY_LOW; + } + + attr->qa_priority = priority; +} + +void +dispatch_queue_attr_set_finalizer_f(dispatch_queue_attr_t attr, + void *context, dispatch_queue_finalizer_function_t finalizer) +{ +#ifdef __BLOCKS__ + if (attr->finalizer_func == (void*)_dispatch_call_block_and_release2) { + Block_release(attr->finalizer_ctxt); + } +#endif + attr->finalizer_ctxt = context; + attr->finalizer_func = finalizer; +} + +#ifdef __BLOCKS__ +long +dispatch_queue_attr_set_finalizer(dispatch_queue_attr_t attr, + dispatch_queue_finalizer_t finalizer) +{ + void *ctxt; + dispatch_queue_finalizer_function_t func; + + if (finalizer) { + if (!(ctxt = Block_copy(finalizer))) { + return 1; + } + func = (void *)_dispatch_call_block_and_release2; + } else { + ctxt = NULL; + func = NULL; + } + + dispatch_queue_attr_set_finalizer_f(attr, ctxt, func); + + return 0; +} +#endif + +static void +_dispatch_ccache_init(void *context __attribute__((unused))) +{ + _dispatch_ccache_zone = malloc_create_zone(0, 0); + dispatch_assert(_dispatch_ccache_zone); + malloc_set_zone_name(_dispatch_ccache_zone, "DispatchContinuations"); +} + +dispatch_continuation_t +_dispatch_continuation_alloc_from_heap(void) +{ + static dispatch_once_t pred; + dispatch_continuation_t dc; + + dispatch_once_f(&pred, NULL, _dispatch_ccache_init); + + while (!(dc = fastpath(malloc_zone_calloc(_dispatch_ccache_zone, 1, ROUND_UP_TO_CACHELINE_SIZE(sizeof(*dc)))))) { + sleep(1); + } + + return dc; +} + +void +_dispatch_force_cache_cleanup(void) +{ + dispatch_continuation_t dc = _dispatch_thread_getspecific(dispatch_cache_key); + if (dc) { + _dispatch_thread_setspecific(dispatch_cache_key, NULL); + _dispatch_cache_cleanup2(dc); + } +} + +DISPATCH_NOINLINE +void +_dispatch_cache_cleanup2(void *value) +{ + dispatch_continuation_t dc, next_dc = value; + + while ((dc = next_dc)) { + next_dc = dc->do_next; + malloc_zone_free(_dispatch_ccache_zone, dc); + } +} + +static char _dispatch_build[16]; + +static void +_dispatch_bug_init(void *context __attribute__((unused))) +{ + int mib[] = { CTL_KERN, KERN_OSVERSION }; + size_t bufsz = sizeof(_dispatch_build); + + sysctl(mib, 2, _dispatch_build, &bufsz, NULL, 0); +} + +void +_dispatch_bug(size_t line, long val) +{ + static dispatch_once_t pred; + static void *last_seen; + void *ra = __builtin_return_address(0); + + dispatch_once_f(&pred, NULL, _dispatch_bug_init); + if (last_seen != ra) { + last_seen = ra; + _dispatch_log("BUG in libdispatch: %s - %lu - 0x%lx", _dispatch_build, line, val); + } +} + +void +_dispatch_abort(size_t line, long val) +{ + _dispatch_bug(line, val); + abort(); +} + +void +_dispatch_log(const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + + _dispatch_logv(msg, ap); + + va_end(ap); +} + +void +_dispatch_logv(const char *msg, va_list ap) +{ +#if DISPATCH_DEBUG + static FILE *logfile, *tmp; + char newbuf[strlen(msg) + 2]; + char path[PATH_MAX]; + + sprintf(newbuf, "%s\n", msg); + + if (!logfile) { + snprintf(path, sizeof(path), "/var/tmp/libdispatch.%d.log", getpid()); + tmp = fopen(path, "a"); + assert(tmp); + if (!dispatch_atomic_cmpxchg(&logfile, NULL, tmp)) { + fclose(tmp); + } else { + struct timeval tv; + gettimeofday(&tv, NULL); + fprintf(logfile, "=== log file opened for %s[%u] at %ld.%06u ===\n", + getprogname() ?: "", getpid(), tv.tv_sec, tv.tv_usec); + } + } + vfprintf(logfile, newbuf, ap); + fflush(logfile); +#else + vsyslog(LOG_NOTICE, msg, ap); +#endif +} + +int +_dispatch_pthread_sigmask(int how, sigset_t *set, sigset_t *oset) +{ + int r; + + /* Workaround: 6269619 Not all signals can be delivered on any thread */ + + r = sigdelset(set, SIGILL); + dispatch_assume_zero(r); + r = sigdelset(set, SIGTRAP); + dispatch_assume_zero(r); + r = sigdelset(set, SIGEMT); + dispatch_assume_zero(r); + r = sigdelset(set, SIGFPE); + dispatch_assume_zero(r); + r = sigdelset(set, SIGBUS); + dispatch_assume_zero(r); + r = sigdelset(set, SIGSEGV); + dispatch_assume_zero(r); + r = sigdelset(set, SIGSYS); + dispatch_assume_zero(r); + r = sigdelset(set, SIGPIPE); + dispatch_assume_zero(r); + + return pthread_sigmask(how, set, oset); +} + +bool _dispatch_safe_fork = true; + +void +dispatch_atfork_prepare(void) +{ +} + +void +dispatch_atfork_parent(void) +{ +} + +void +dispatch_atfork_child(void) +{ + void *crash = (void *)0x100; + size_t i; + + if (_dispatch_safe_fork) { + return; + } + + _dispatch_main_q.dq_items_head = crash; + _dispatch_main_q.dq_items_tail = crash; + + _dispatch_mgr_q.dq_items_head = crash; + _dispatch_mgr_q.dq_items_tail = crash; + + for (i = 0; i < DISPATCH_ROOT_QUEUE_COUNT; i++) { + _dispatch_root_queues[i].dq_items_head = crash; + _dispatch_root_queues[i].dq_items_tail = crash; + } +} + +void +dispatch_init_pthread(pthread_t pthr __attribute__((unused))) +{ +} + +static int _dispatch_kq; + +static void +_dispatch_get_kq_init(void *context __attribute__((unused))) +{ + static const struct kevent kev = { + .ident = 1, + .filter = EVFILT_USER, + .flags = EV_ADD|EV_CLEAR, + }; + + _dispatch_kq = kqueue(); + _dispatch_safe_fork = false; + // in case we fall back to select() + FD_SET(_dispatch_kq, &_dispatch_rfds); + + if (_dispatch_kq == -1) { + dispatch_assert_zero(errno); + } + + dispatch_assume_zero(kevent(_dispatch_kq, &kev, 1, NULL, 0, NULL)); + + _dispatch_queue_push(_dispatch_mgr_q.do_targetq, &_dispatch_mgr_q); +} + +static int +_dispatch_get_kq(void) +{ + static dispatch_once_t pred; + + dispatch_once_f(&pred, NULL, _dispatch_get_kq_init); + + return _dispatch_kq; +} + +static void +_dispatch_mgr_thread2(struct kevent *kev, size_t cnt) +{ + size_t i; + + for (i = 0; i < cnt; i++) { + // EVFILT_USER isn't used by sources + if (kev[i].filter == EVFILT_USER) { + // If _dispatch_mgr_thread2() ever is changed to return to the + // caller, then this should become _dispatch_queue_drain() + _dispatch_queue_serial_drain_till_empty(&_dispatch_mgr_q); + } else { + _dispatch_source_drain_kevent(&kev[i]); + } + } +} + +static dispatch_queue_t +_dispatch_mgr_invoke(dispatch_queue_t dq) +{ + static const struct timespec timeout_immediately = { 0, 0 }; + struct timespec timeout; + const struct timespec *timeoutp; + struct timeval sel_timeout, *sel_timeoutp; + fd_set tmp_rfds, tmp_wfds; + struct kevent kev[1]; + int k_cnt, k_err, i, r; + + _dispatch_thread_setspecific(dispatch_queue_key, dq); + + for (;;) { + _dispatch_run_timers(); + + timeoutp = _dispatch_get_next_timer_fire(&timeout); + + if (_dispatch_select_workaround) { + FD_COPY(&_dispatch_rfds, &tmp_rfds); + FD_COPY(&_dispatch_wfds, &tmp_wfds); + if (timeoutp) { + sel_timeout.tv_sec = timeoutp->tv_sec; + sel_timeout.tv_usec = (typeof(sel_timeout.tv_usec))(timeoutp->tv_nsec / 1000u); + sel_timeoutp = &sel_timeout; + } else { + sel_timeoutp = NULL; + } + + r = select(FD_SETSIZE, &tmp_rfds, &tmp_wfds, NULL, sel_timeoutp); + if (r == -1) { + if (errno != EBADF) { + dispatch_assume_zero(errno); + continue; + } + for (i = 0; i < FD_SETSIZE; i++) { + if (i == _dispatch_kq) { + continue; + } + if (!FD_ISSET(i, &_dispatch_rfds) && !FD_ISSET(i, &_dispatch_wfds)) { + continue; + } + r = dup(i); + if (r != -1) { + close(r); + } else { + FD_CLR(i, &_dispatch_rfds); + FD_CLR(i, &_dispatch_wfds); + _dispatch_rfd_ptrs[i] = 0; + _dispatch_wfd_ptrs[i] = 0; + } + } + continue; + } + + if (r > 0) { + for (i = 0; i < FD_SETSIZE; i++) { + if (i == _dispatch_kq) { + continue; + } + if (FD_ISSET(i, &tmp_rfds)) { + FD_CLR(i, &_dispatch_rfds); // emulate EV_DISABLE + EV_SET(&kev[0], i, EVFILT_READ, EV_ADD|EV_ENABLE|EV_DISPATCH, 0, 1, _dispatch_rfd_ptrs[i]); + _dispatch_rfd_ptrs[i] = 0; + _dispatch_mgr_thread2(kev, 1); + } + if (FD_ISSET(i, &tmp_wfds)) { + FD_CLR(i, &_dispatch_wfds); // emulate EV_DISABLE + EV_SET(&kev[0], i, EVFILT_WRITE, EV_ADD|EV_ENABLE|EV_DISPATCH, 0, 1, _dispatch_wfd_ptrs[i]); + _dispatch_wfd_ptrs[i] = 0; + _dispatch_mgr_thread2(kev, 1); + } + } + } + + timeoutp = &timeout_immediately; + } + + k_cnt = kevent(_dispatch_kq, NULL, 0, kev, sizeof(kev) / sizeof(kev[0]), timeoutp); + k_err = errno; + + switch (k_cnt) { + case -1: + if (k_err == EBADF) { + DISPATCH_CLIENT_CRASH("Do not close random Unix descriptors"); + } + dispatch_assume_zero(k_err); + continue; + default: + _dispatch_mgr_thread2(kev, (size_t)k_cnt); + // fall through + case 0: + _dispatch_force_cache_cleanup(); + continue; + } + } + + return NULL; +} + +static bool +_dispatch_mgr_wakeup(dispatch_queue_t dq) +{ + static const struct kevent kev = { + .ident = 1, + .filter = EVFILT_USER, +#ifdef EV_TRIGGER + .flags = EV_TRIGGER, +#endif +#ifdef NOTE_TRIGGER + .fflags = NOTE_TRIGGER, +#endif + }; + + _dispatch_debug("waking up the _dispatch_mgr_q: %p", dq); + + _dispatch_update_kq(&kev); + + return false; +} + +void +_dispatch_update_kq(const struct kevent *kev) +{ + struct kevent kev_copy = *kev; + kev_copy.flags |= EV_RECEIPT; + + if (kev_copy.flags & EV_DELETE) { + switch (kev_copy.filter) { + case EVFILT_READ: + if (FD_ISSET((int)kev_copy.ident, &_dispatch_rfds)) { + FD_CLR((int)kev_copy.ident, &_dispatch_rfds); + _dispatch_rfd_ptrs[kev_copy.ident] = 0; + return; + } + case EVFILT_WRITE: + if (FD_ISSET((int)kev_copy.ident, &_dispatch_wfds)) { + FD_CLR((int)kev_copy.ident, &_dispatch_wfds); + _dispatch_wfd_ptrs[kev_copy.ident] = 0; + return; + } + default: + break; + } + } + + int rval = kevent(_dispatch_get_kq(), &kev_copy, 1, &kev_copy, 1, NULL); + if (rval == -1) { + // If we fail to register with kevents, for other reasons aside from + // changelist elements. + dispatch_assume_zero(errno); + //kev_copy.flags |= EV_ERROR; + //kev_copy.data = error; + return; + } + + // The following select workaround only applies to adding kevents + if (!(kev->flags & EV_ADD)) { + return; + } + + switch (kev_copy.data) { + case 0: + return; + case EBADF: + break; + default: + // If an error occurred while registering with kevent, and it was + // because of a kevent changelist processing && the kevent involved + // either doing a read or write, it would indicate we were trying + // to register a /dev/* port; fall back to select + switch (kev_copy.filter) { + case EVFILT_READ: + _dispatch_select_workaround = true; + FD_SET((int)kev_copy.ident, &_dispatch_rfds); + _dispatch_rfd_ptrs[kev_copy.ident] = kev_copy.udata; + break; + case EVFILT_WRITE: + _dispatch_select_workaround = true; + FD_SET((int)kev_copy.ident, &_dispatch_wfds); + _dispatch_wfd_ptrs[kev_copy.ident] = kev_copy.udata; + break; + default: + _dispatch_source_drain_kevent(&kev_copy); + break; + } + break; + } +} + +static const struct dispatch_queue_vtable_s _dispatch_queue_mgr_vtable = { + .do_type = DISPATCH_QUEUE_MGR_TYPE, + .do_kind = "mgr-queue", + .do_invoke = _dispatch_mgr_invoke, + .do_debug = dispatch_queue_debug, + .do_probe = _dispatch_mgr_wakeup, +}; + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +struct dispatch_queue_s _dispatch_mgr_q = { + .do_vtable = &_dispatch_queue_mgr_vtable, + .do_ref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_xref_cnt = DISPATCH_OBJECT_GLOBAL_REFCNT, + .do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_LOCK, + .do_targetq = &_dispatch_root_queues[DISPATCH_ROOT_QUEUE_COUNT - 1], + + .dq_label = "com.apple.libdispatch-manager", + .dq_width = 1, + .dq_serialnum = 2, +}; + +const struct dispatch_queue_offsets_s dispatch_queue_offsets = { + .dqo_version = 3, + .dqo_label = offsetof(struct dispatch_queue_s, dq_label), + .dqo_label_size = sizeof(_dispatch_main_q.dq_label), + .dqo_flags = 0, + .dqo_flags_size = 0, + .dqo_width = offsetof(struct dispatch_queue_s, dq_width), + .dqo_width_size = sizeof(_dispatch_main_q.dq_width), + .dqo_serialnum = offsetof(struct dispatch_queue_s, dq_serialnum), + .dqo_serialnum_size = sizeof(_dispatch_main_q.dq_serialnum), + .dqo_running = offsetof(struct dispatch_queue_s, dq_running), + .dqo_running_size = sizeof(_dispatch_main_q.dq_running), +}; + +#ifdef __BLOCKS__ +void +dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t work) +{ + // test before the copy of the block + if (when == DISPATCH_TIME_FOREVER) { +#if DISPATCH_DEBUG + DISPATCH_CLIENT_CRASH("dispatch_after() called with 'when' == infinity"); +#endif + return; + } + dispatch_after_f(when, queue, _dispatch_Block_copy(work), _dispatch_call_block_and_release); +} +#endif + +DISPATCH_NOINLINE +void +dispatch_after_f(dispatch_time_t when, dispatch_queue_t queue, void *ctxt, void (*func)(void *)) +{ + uint64_t delta; + if (when == DISPATCH_TIME_FOREVER) { +#if DISPATCH_DEBUG + DISPATCH_CLIENT_CRASH("dispatch_after_f() called with 'when' == infinity"); +#endif + return; + } + + // this function can and should be optimized to not use a dispatch source +again: + delta = _dispatch_timeout(when); + if (delta == 0) { + return dispatch_async_f(queue, ctxt, func); + } + if (!dispatch_source_timer_create(DISPATCH_TIMER_INTERVAL, delta, 0, NULL, queue, ^(dispatch_source_t ds) { + long err_dom, err_val; + if ((err_dom = dispatch_source_get_error(ds, &err_val))) { + dispatch_assert(err_dom == DISPATCH_ERROR_DOMAIN_POSIX); + dispatch_assert(err_val == ECANCELED); + func(ctxt); + dispatch_release(ds); // MUST NOT be _dispatch_release() + } else { + dispatch_source_cancel(ds); + } + })) { + goto again; + } +} diff --git a/src/queue.h b/src/queue.h new file mode 100644 index 0000000..6b55696 --- /dev/null +++ b/src/queue.h @@ -0,0 +1,568 @@ +/* + * 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@ + */ + +#ifndef __DISPATCH_QUEUE__ +#define __DISPATCH_QUEUE__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +/*! + * @header + * + * Dispatch is an abstract model for expressing concurrency via simple but + * powerful API. + * + * At the core, dispatch provides serial FIFO queues to which blocks may be + * submitted. Blocks submitted to these dispatch queues are invoked on a pool + * of threads fully managed by the system. No guarantee is made regarding + * which thread a block will be invoked on; however, it is guaranteed that only + * one block submitted to the FIFO dispatch queue will be invoked at a time. + * + * When multiple queues have blocks to be processed, the system is free to + * allocate additional threads to invoke the blocks concurrently. When the + * queues become empty, these threads are automatically released. + */ + +/*! + * @typedef dispatch_queue_t + * + * @abstract + * Dispatch queues invoke blocks submitted to them serially in FIFO order. A + * queue will only invoke one block at a time, but independent queues may each + * invoke their blocks concurrently with respect to each other. + * + * @discussion + * Dispatch queues are lightweight objects to which blocks may be submitted. + * The system manages a pool of threads which process dispatch queues and + * invoke blocks submitted to them. + * + * Conceptually a dispatch queue may have its own thread of execution, and + * interaction between queues is highly asynchronous. + * + * Dispatch queues are reference counted via calls to dispatch_retain() and + * dispatch_release(). Pending blocks submitted to a queue also hold a + * reference to the queue until they have finished. Once all references to a + * queue have been released, the queue will be deallocated by the system. + */ +DISPATCH_DECL(dispatch_queue); + +/*! + * @typedef dispatch_queue_attr_t + * + * @abstract + * Attribute and policy extensions for dispatch queues. + */ +DISPATCH_DECL(dispatch_queue_attr); + +/*! + * @typedef dispatch_block_t + * + * @abstract + * The prototype of blocks submitted to dispatch queues, which take no + * arguments and have no return value. + * + * @discussion + * The declaration of a block allocates storage on the stack. Therefore, this + * is an invalid construct: + * + * dispatch_block_t block; + * + * if (x) { + * block = ^{ printf("true\n"); }; + * } else { + * block = ^{ printf("false\n"); }; + * } + * block(); // unsafe!!! + * + * What is happening behind the scenes: + * + * if (x) { + * struct Block __tmp_1 = ...; // setup details + * block = &__tmp_1; + * } else { + * struct Block __tmp_2 = ...; // setup details + * block = &__tmp_2; + * } + * + * As the example demonstrates, the address of a stack variable is escaping the + * scope in which it is allocated. That is a classic C bug. + */ +#ifdef __BLOCKS__ +typedef void (^dispatch_block_t)(void); +#endif + +__BEGIN_DECLS + +/*! + * @function dispatch_async + * + * @abstract + * Submits a block for asynchronous execution on a dispatch queue. + * + * @discussion + * The dispatch_async() function is the fundamental mechanism for submitting + * blocks to a dispatch queue. + * + * Calls to dispatch_async() always return immediately after the block has + * been submitted, and never wait for the block to be invoked. + * + * The target queue determines whether the block will be invoked serially or + * concurrently with respect to other blocks submitted to that same queue. + * Serial queues are processed concurrently with with respect to each other. + * + * @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_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_async(dispatch_queue_t queue, dispatch_block_t block); +#endif + +/*! + * @function dispatch_async_f + * + * @abstract + * Submits a function for asynchronous execution on a dispatch queue. + * + * @discussion + * See dispatch_async() 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_async_f(). + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_async_f(dispatch_queue_t queue, + void *context, + dispatch_function_t work); + +/*! + * @function dispatch_sync + * + * @abstract + * Submits a block for synchronous execution on a dispatch queue. + * + * @discussion + * Submits a block to a dispatch queue like dispatch_async(), however + * dispatch_sync() will not return until the block has finished. + * + * Calls to dispatch_sync() targeting the current queue will result + * in dead-lock. Use of dispatch_sync() is also subject to the same + * multi-party dead-lock problems that may result from the use of a mutex. + * Use of dispatch_async() is preferred. + * + * Unlike dispatch_async(), no retain is performed on the target queue. Because + * calls to this function are synchronous, the dispatch_sync() "borrows" the + * reference of the caller. + * + * As an optimization, dispatch_sync() invokes the block on the current + * thread when possible. + * + * @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_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_sync(dispatch_queue_t queue, dispatch_block_t block); +#endif + +/*! + * @function dispatch_sync_f + * + * @abstract + * Submits a function for synchronous execution on a dispatch queue. + * + * @discussion + * See dispatch_sync() 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_sync_f(). + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_sync_f(dispatch_queue_t queue, + void *context, + dispatch_function_t work); + +/*! + * @function dispatch_apply + * + * @abstract + * Submits a block to a dispatch queue for multiple invocations. + * + * @discussion + * Submits a block to a dispatch queue for multiple invocations. This function + * waits for the task block to complete before returning. If the target queue + * is a concurrent queue returned by dispatch_get_concurrent_queue(), the block + * may be invoked concurrently, and it must therefore be reentrant safe. + * + * Each invocation of the block will be passed the current index of iteration. + * + * @param iterations + * The number of iterations to perform. + * + * @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 the specified number of iterations. + * The result of passing NULL in this parameter is undefined. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)); +#endif + +/*! + * @function dispatch_apply_f + * + * @abstract + * Submits a function to a dispatch queue for multiple invocations. + * + * @discussion + * See dispatch_apply() for details. + * + * @param iterations + * The number of iterations to perform. + * + * @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_apply_f(). The second parameter passed to this function is the + * current index of iteration. + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL2 DISPATCH_NONNULL4 DISPATCH_NOTHROW +void +dispatch_apply_f(size_t iterations, dispatch_queue_t queue, + void *context, + void (*work)(void *, size_t)); + +/*! + * @function dispatch_get_current_queue + * + * @abstract + * Returns the queue on which the currently executing block is running. + * + * @discussion + * Returns the queue on which the currently executing block is running. + * + * When dispatch_get_current_queue() is called outside of the context of a + * submitted block, it will return the default concurrent queue. + * + * @result + * Returns the current queue. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_queue_t +dispatch_get_current_queue(void); + +/*! + * @function dispatch_get_main_queue + * + * @abstract + * Returns the default queue that is bound to the main thread. + * + * @discussion + * In order to invoke blocks submitted to the main queue, the application must + * call dispatch_main(), NSApplicationMain(), or use a CFRunLoop on the main + * thread. + * + * @result + * Returns the main queue. This queue is created automatically on behalf of + * the main thread before main() is called. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern struct dispatch_queue_s _dispatch_main_q; +#define dispatch_get_main_queue() (&_dispatch_main_q) + +/*! + * @enum dispatch_queue_priority_t + * + * @constant DISPATCH_QUEUE_PRIORITY_HIGH + * Items dispatched to the queue will run at high priority, + * i.e. the queue will be scheduled for execution before + * any default priority or low priority queue. + * + * @constant DISPATCH_QUEUE_PRIORITY_DEFAULT + * Items dispatched to the queue will run at the default + * priority, i.e. the queue will be scheduled for execution + * after all high priority queues have been scheduled, but + * before any low priority queues have been scheduled. + * + * @constant DISPATCH_QUEUE_PRIORITY_LOW + * Items dispatched to the queue will run at low priority, + * i.e. the queue will be scheduled for execution after all + * default priority and high priority queues have been + * scheduled. + */ +enum { + DISPATCH_QUEUE_PRIORITY_HIGH = 2, + DISPATCH_QUEUE_PRIORITY_DEFAULT = 0, + DISPATCH_QUEUE_PRIORITY_LOW = -2, +}; + +/*! + * @function dispatch_get_global_queue + * + * @abstract + * Returns a well-known global concurrent queue of a given priority level. + * + * @discussion + * The well-known global concurrent queues may not be modified. Calls to + * dispatch_suspend(), dispatch_resume(), dispatch_set_context(), etc., will + * have no effect when used with queues returned by this function. + * + * @param priority + * A priority defined in dispatch_queue_priority_t + * + * @param flags + * Reserved for future use. Passing any value other than zero may result in + * a NULL return value. + * + * @result + * Returns the requested global queue. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_queue_t +dispatch_get_global_queue(long priority, unsigned long flags); + +/*! + * @function dispatch_queue_create + * + * @abstract + * Creates a new dispatch queue to which blocks may be submitted. + * + * @discussion + * Dispatch queues invoke blocks serially in FIFO order. + * + * When the dispatch queue is no longer needed, it should be released + * with dispatch_release(). Note that any pending blocks submitted + * to a queue will hold a reference to that queue. Therefore a queue + * will not be deallocated until all pending blocks have finished. + * + * @param label + * A string label to attach to the queue. + * This parameter is optional and may be NULL. + * + * @param attr + * Unused. Pass NULL for now. + * + * @result + * The newly created dispatch queue. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_WARN_RESULT DISPATCH_NOTHROW +dispatch_queue_t +dispatch_queue_create(const char *label, dispatch_queue_attr_t attr); + +/*! + * @function dispatch_queue_get_label + * + * @abstract + * Returns the label of the queue that was specified when the + * queue was created. + * + * @param queue + * The result of passing NULL in this parameter is undefined. + * + * @result + * The label of the queue. The result may be NULL. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_PURE DISPATCH_WARN_RESULT DISPATCH_NOTHROW +const char * +dispatch_queue_get_label(dispatch_queue_t queue); + +/*! + * @function dispatch_set_target_queue + * + * @abstract + * Sets the target queue for the given object. + * + * @discussion + * An object's target queue is responsible for processing the object. + * + * A dispatch queue's priority is inherited by its target queue. Use the + * dispatch_get_global_queue() function to obtain suitable target queue + * of the desired priority. + * + * A dispatch source's target queue specifies where its event handler and + * cancellation handler blocks will be submitted. + * + * The result of calling dispatch_set_target_queue() on any other type of + * dispatch object is undefined. + * + * @param object + * The object to modify. + * The result of passing NULL in this parameter is undefined. + * + * @param queue + * The new target queue for the object. The queue is retained, and the + * previous one, if any, is released. + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue); + +/*! + * @function dispatch_main + * + * @abstract + * Execute blocks submitted to the main queue. + * + * @discussion + * This function "parks" the main thread and waits for blocks to be submitted + * to the main queue. This function never returns. + * + * Applications that call NSApplicationMain() or CFRunLoopRun() on the + * main thread do not need to call dispatch_main(). + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NOTHROW DISPATCH_NORETURN +void +dispatch_main(void); + +/*! + * @function dispatch_after + * + * @abstract + * Schedule a block for execution on a given queue at a specified time. + * + * @discussion + * Passing DISPATCH_TIME_NOW as the "when" parameter is supported, but not as + * optimal as calling dispatch_async() instead. Passing DISPATCH_TIME_FOREVER + * is undefined. + * + * @param when + * A temporal milestone returned by dispatch_time() or dispatch_walltime(). + * + * @param queue + * A queue to which the given block will be submitted at the specified time. + * The result of passing NULL in this parameter is undefined. + * + * @param block + * The block of code to execute. + * The result of passing NULL in this parameter is undefined. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL2 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_after(dispatch_time_t when, + dispatch_queue_t queue, + dispatch_block_t block); +#endif + +/*! + * @function dispatch_after_f + * + * @abstract + * Schedule a function for execution on a given queue at a specified time. + * + * @discussion + * See dispatch_after() for details. + * + * @param when + * A temporal milestone returned by dispatch_time() or dispatch_walltime(). + * + * @param queue + * A queue to which the given function will be submitted at the specified time. + * 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_after_f(). + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL2 DISPATCH_NONNULL4 DISPATCH_NOTHROW +void +dispatch_after_f(dispatch_time_t when, + dispatch_queue_t queue, + void *context, + dispatch_function_t work); + +__END_DECLS + +#endif diff --git a/src/queue_internal.h b/src/queue_internal.h new file mode 100644 index 0000000..05237c2 --- /dev/null +++ b/src/queue_internal.h @@ -0,0 +1,136 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_QUEUE_INTERNAL__ +#define __DISPATCH_QUEUE_INTERNAL__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +// If dc_vtable is less than 127, then the object is a continuation. +// Otherwise, the object has a private layout and memory management rules. The +// first two words must align with normal objects. +#define DISPATCH_CONTINUATION_HEADER(x) \ + const void * do_vtable; \ + struct x *volatile do_next; \ + dispatch_function_t dc_func; \ + void * dc_ctxt + +#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) ((unsigned long)(x)->do_vtable > 127ul) + +struct dispatch_continuation_s { + DISPATCH_CONTINUATION_HEADER(dispatch_continuation_s); + dispatch_group_t dc_group; + void * dc_data[3]; +}; + +typedef struct dispatch_continuation_s *dispatch_continuation_t; + + +struct dispatch_queue_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_queue_s); +}; + +#define DISPATCH_QUEUE_MIN_LABEL_SIZE 64 + +#define DISPATCH_QUEUE_HEADER \ + uint32_t dq_running; \ + uint32_t dq_width; \ + struct dispatch_object_s *dq_items_tail; \ + struct dispatch_object_s *volatile dq_items_head; \ + unsigned long dq_serialnum; \ + void *dq_finalizer_ctxt; \ + dispatch_queue_finalizer_function_t dq_finalizer_func + +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 +}; + +extern struct dispatch_queue_s _dispatch_mgr_q; + +void _dispatch_queue_init(dispatch_queue_t dq); +void _dispatch_queue_drain(dispatch_queue_t dq); +void _dispatch_queue_dispose(dispatch_queue_t dq); + +__attribute__((always_inline)) +static inline 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(&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 { + dq->dq_items_head = head; + _dispatch_wakeup(dq); + } +} + +#define _dispatch_queue_push(x, y) _dispatch_queue_push_list((x), (y), (y)) + +#define DISPATCH_QUEUE_PRIORITY_COUNT 3 + +#if DISPATCH_DEBUG +void dispatch_debug_queue(dispatch_queue_t dq, const char* str); +#else +static inline void dispatch_debug_queue(dispatch_queue_t dq __attribute__((unused)), const char* str __attribute__((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 inline dispatch_queue_t +_dispatch_queue_get_current(void) +{ + return _dispatch_thread_getspecific(dispatch_queue_key); +} + +__private_extern__ malloc_zone_t *_dispatch_ccache_zone; +dispatch_continuation_t _dispatch_continuation_alloc_from_heap(void); + +static inline dispatch_continuation_t +_dispatch_continuation_alloc_cacheonly(void) +{ + dispatch_continuation_t dc = fastpath(_dispatch_thread_getspecific(dispatch_cache_key)); + if (dc) { + _dispatch_thread_setspecific(dispatch_cache_key, dc->do_next); + } + return dc; +} + +#endif diff --git a/src/queue_private.h b/src/queue_private.h new file mode 100644 index 0000000..85f87c0 --- /dev/null +++ b/src/queue_private.h @@ -0,0 +1,122 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_QUEUE_PRIVATE__ +#define __DISPATCH_QUEUE_PRIVATE__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +__BEGIN_DECLS + + +/*! + * @enum dispatch_queue_flags_t + * + * @constant DISPATCH_QUEUE_OVERCOMMIT + * The queue will create a new thread for invoking blocks, regardless of how + * busy the computer is. + */ +enum { + DISPATCH_QUEUE_OVERCOMMIT = 0x2ull, +}; + +#define DISPATCH_QUEUE_FLAGS_MASK (DISPATCH_QUEUE_OVERCOMMIT) + +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); +#endif + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_barrier_sync_f(dispatch_queue_t dq, void *context, dispatch_function_t work); + +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); +#endif + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NONNULL3 DISPATCH_NOTHROW +void +dispatch_barrier_async_f(dispatch_queue_t dq, void *context, dispatch_function_t work); + +/*! + * @function dispatch_queue_set_width + * + * @abstract + * Set the width of concurrency for a given queue. The default width of a + * privately allocated queue is one. + * + * @param queue + * The queue to adjust. Passing the main queue, a default concurrent queue or + * any other default queue will be ignored. + * + * @param width + * The new maximum width of concurrency depending on available resources. + * If zero is passed, then the value is promoted to one. + * Negative values are magic values that map to automatic width values. + * Unknown negative values default to DISPATCH_QUEUE_WIDTH_MAX_LOGICAL_CPUS. + */ +#define DISPATCH_QUEUE_WIDTH_ACTIVE_CPUS -1 +#define DISPATCH_QUEUE_WIDTH_MAX_PHYSICAL_CPUS -2 +#define DISPATCH_QUEUE_WIDTH_MAX_LOGICAL_CPUS -3 + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_queue_set_width(dispatch_queue_t dq, long width); + + + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_queue_offsets_s { + // always add new fields at the end + const uint16_t dqo_version; + const uint16_t dqo_label; + const uint16_t dqo_label_size; + const uint16_t dqo_flags; + const uint16_t dqo_flags_size; + const uint16_t dqo_serialnum; + const uint16_t dqo_serialnum_size; + const uint16_t dqo_width; + const uint16_t dqo_width_size; + const uint16_t dqo_running; + const uint16_t dqo_running_size; +} dispatch_queue_offsets; + + +__END_DECLS + +#endif diff --git a/src/semaphore.c b/src/semaphore.c new file mode 100644 index 0000000..9e36d4d --- /dev/null +++ b/src/semaphore.c @@ -0,0 +1,532 @@ +/* + * 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@ + */ + +#include "internal.h" + +// semaphores are too fundamental to use the dispatch_assume*() macros +#define DISPATCH_SEMAPHORE_VERIFY_KR(x) do { \ + if (x) { \ + DISPATCH_CRASH("flawed group/semaphore logic"); \ + } \ + } while (0) + +struct dispatch_semaphore_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_semaphore_s); +}; + +static void _dispatch_semaphore_dispose(dispatch_semaphore_t dsema); +static size_t _dispatch_semaphore_debug(dispatch_semaphore_t dsema, char *buf, size_t bufsiz); +static long _dispatch_group_wake(dispatch_semaphore_t dsema); + +const struct dispatch_semaphore_vtable_s _dispatch_semaphore_vtable = { + .do_type = DISPATCH_SEMAPHORE_TYPE, + .do_kind = "semaphore", + .do_dispose = _dispatch_semaphore_dispose, + .do_debug = _dispatch_semaphore_debug, +}; + +dispatch_semaphore_t +_dispatch_get_thread_semaphore(void) +{ + dispatch_semaphore_t dsema; + + dsema = fastpath(_dispatch_thread_getspecific(dispatch_sema4_key)); + if (!dsema) { + while (!(dsema = dispatch_semaphore_create(0))) { + sleep(1); + } + } + _dispatch_thread_setspecific(dispatch_sema4_key, NULL); + return dsema; +} + +void +_dispatch_put_thread_semaphore(dispatch_semaphore_t dsema) +{ + dispatch_semaphore_t old_sema = _dispatch_thread_getspecific(dispatch_sema4_key); + _dispatch_thread_setspecific(dispatch_sema4_key, dsema); + if (old_sema) { + dispatch_release(old_sema); + } +} + +dispatch_group_t +dispatch_group_create(void) +{ + return (dispatch_group_t)dispatch_semaphore_create(LONG_MAX); +} + +dispatch_semaphore_t +dispatch_semaphore_create(long value) +{ + dispatch_semaphore_t dsema; + + // If the internal value is negative, then the absolute of the value is + // equal to the number of waiting threads. Therefore it is bogus to + // initialize the semaphore with a negative value. + if (value < 0) { + return NULL; + } + + dsema = calloc(1, sizeof(struct dispatch_semaphore_s)); + + if (fastpath(dsema)) { + dsema->do_vtable = &_dispatch_semaphore_vtable; + dsema->do_next = DISPATCH_OBJECT_LISTLESS; + dsema->do_ref_cnt = 1; + dsema->do_xref_cnt = 1; + dsema->do_targetq = dispatch_get_global_queue(0, 0); + dsema->dsema_value = value; + dsema->dsema_orig = value; + } + + return dsema; +} + +static void +_dispatch_semaphore_create_port(semaphore_t *s4) +{ + kern_return_t kr; + semaphore_t tmp; + + if (*s4) { + return; + } + + // lazily allocate the semaphore port + + // Someday: + // 1) Switch to a doubly-linked FIFO in user-space. + // 2) User-space timers for the timeout. + // 3) Use the per-thread semaphore port. + + while (dispatch_assume_zero(kr = semaphore_create(mach_task_self(), &tmp, SYNC_POLICY_FIFO, 0))) { + DISPATCH_VERIFY_MIG(kr); + sleep(1); + } + + if (!dispatch_atomic_cmpxchg(s4, 0, tmp)) { + kr = semaphore_destroy(mach_task_self(), tmp); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + } + + _dispatch_safe_fork = false; +} + +DISPATCH_NOINLINE +static long +_dispatch_semaphore_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout) +{ + mach_timespec_t _timeout; + kern_return_t kr; + uint64_t nsec; + long orig; + +again: + // Mach semaphores appear to sometimes spuriously wake up. Therefore, + // we keep a parallel count of the number of times a Mach semaphore is + // signaled. + while ((orig = dsema->dsema_sent_ksignals)) { + if (dispatch_atomic_cmpxchg(&dsema->dsema_sent_ksignals, orig, orig - 1)) { + return 0; + } + } + + _dispatch_semaphore_create_port(&dsema->dsema_port); + + // From xnu/osfmk/kern/sync_sema.c: + // wait_semaphore->count = -1; /* we don't keep an actual count */ + // + // The code above does not match the documentation, and that fact is + // not surprising. The documented semantics are clumsy to use in any + // practical way. The above hack effectively tricks the rest of the + // Mach semaphore logic to behave like the libdispatch algorithm. + + switch (timeout) { + default: + do { + // timeout() already calculates relative time left + nsec = _dispatch_timeout(timeout); + _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC); + _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC); + kr = slowpath(semaphore_timedwait(dsema->dsema_port, _timeout)); + } while (kr == KERN_ABORTED); + + if (kr != KERN_OPERATION_TIMED_OUT) { + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + break; + } + // Fall through and try to undo what the fast path did to dsema->dsema_value + case DISPATCH_TIME_NOW: + while ((orig = dsema->dsema_value) < 0) { + if (dispatch_atomic_cmpxchg(&dsema->dsema_value, orig, orig + 1)) { + return KERN_OPERATION_TIMED_OUT; + } + } + // Another thread called semaphore_signal(). + // Fall through and drain the wakeup. + case DISPATCH_TIME_FOREVER: + do { + kr = semaphore_wait(dsema->dsema_port); + } while (kr == KERN_ABORTED); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + break; + } + + goto again; +} + +DISPATCH_NOINLINE +void +dispatch_group_enter(dispatch_group_t dg) +{ + dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg; +#if defined(__OPTIMIZE__) && defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) + // This assumes: + // 1) Way too much about the optimizer of GCC. + // 2) There will never be more than LONG_MAX threads. + // Therefore: no overflow detection + asm( +#ifdef __LP64__ + "lock decq %0\n\t" +#else + "lock decl %0\n\t" +#endif + "js 1f\n\t" + "xor %%eax, %%eax\n\t" + "ret\n\t" + "1:" + : "+m" (dsema->dsema_value) + : + : "cc" + ); + _dispatch_semaphore_wait_slow(dsema, DISPATCH_TIME_FOREVER); +#else + dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER); +#endif +} + +DISPATCH_NOINLINE +long +dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout) +{ +#if defined(__OPTIMIZE__) && defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) + // This assumes: + // 1) Way too much about the optimizer of GCC. + // 2) There will never be more than LONG_MAX threads. + // Therefore: no overflow detection + asm( +#ifdef __LP64__ + "lock decq %0\n\t" +#else + "lock decl %0\n\t" +#endif + "js 1f\n\t" + "xor %%eax, %%eax\n\t" + "ret\n\t" + "1:" + : "+m" (dsema->dsema_value) + : + : "cc" + ); +#else + if (dispatch_atomic_dec(&dsema->dsema_value) >= 0) { + return 0; + } +#endif + return _dispatch_semaphore_wait_slow(dsema, timeout); +} + +DISPATCH_NOINLINE +static long +_dispatch_semaphore_signal_slow(dispatch_semaphore_t dsema) +{ + kern_return_t kr; + + _dispatch_semaphore_create_port(&dsema->dsema_port); + + // Before dsema_sent_ksignals is incremented we can rely on the reference + // held by the waiter. However, once this value is incremented the waiter + // may return between the atomic increment and the semaphore_signal(), + // therefore an explicit reference must be held in order to safely access + // dsema after the atomic increment. + _dispatch_retain(dsema); + + dispatch_atomic_inc(&dsema->dsema_sent_ksignals); + + kr = semaphore_signal(dsema->dsema_port); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + + _dispatch_release(dsema); + + return 1; +} + +void +dispatch_group_leave(dispatch_group_t dg) +{ + dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg; + + dispatch_semaphore_signal(dsema); + + if (dsema->dsema_value == dsema->dsema_orig) { + _dispatch_group_wake(dsema); + } +} + +DISPATCH_NOINLINE +long +dispatch_semaphore_signal(dispatch_semaphore_t dsema) +{ +#if defined(__OPTIMIZE__) && defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) + // overflow detection + // this assumes way too much about the optimizer of GCC + asm( +#ifdef __LP64__ + "lock incq %0\n\t" +#else + "lock incl %0\n\t" +#endif + "jo 1f\n\t" + "jle 2f\n\t" + "xor %%eax, %%eax\n\t" + "ret\n\t" + "1:\n\t" + "int $4\n\t" + "2:" + : "+m" (dsema->dsema_value) + : + : "cc" + ); +#else + if (dispatch_atomic_inc(&dsema->dsema_value) > 0) { + return 0; + } +#endif + return _dispatch_semaphore_signal_slow(dsema); +} + +DISPATCH_NOINLINE +long +_dispatch_group_wake(dispatch_semaphore_t dsema) +{ + struct dispatch_sema_notify_s *tmp, *head = dispatch_atomic_xchg(&dsema->dsema_notify_head, NULL); + long rval = dispatch_atomic_xchg(&dsema->dsema_group_waiters, 0); + bool do_rel = head; + long kr; + + // wake any "group" waiter or notify blocks + + if (rval) { + _dispatch_semaphore_create_port(&dsema->dsema_waiter_port); + do { + kr = semaphore_signal(dsema->dsema_waiter_port); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + } while (--rval); + } + while (head) { + dispatch_async_f(head->dsn_queue, head->dsn_ctxt, head->dsn_func); + _dispatch_release(head->dsn_queue); + do { + tmp = head->dsn_next; + } while (!tmp && !dispatch_atomic_cmpxchg(&dsema->dsema_notify_tail, head, NULL)); + free(head); + head = tmp; + } + if (do_rel) { + _dispatch_release(dsema); + } + return 0; +} + +DISPATCH_NOINLINE +static long +_dispatch_group_wait_slow(dispatch_semaphore_t dsema, dispatch_time_t timeout) +{ + mach_timespec_t _timeout; + kern_return_t kr; + uint64_t nsec; + long orig; + +again: + // check before we cause another signal to be sent by incrementing dsema->dsema_group_waiters + if (dsema->dsema_value == dsema->dsema_orig) { + return _dispatch_group_wake(dsema); + } + // Mach semaphores appear to sometimes spuriously wake up. Therefore, + // we keep a parallel count of the number of times a Mach semaphore is + // signaled. + dispatch_atomic_inc(&dsema->dsema_group_waiters); + // check the values again in case we need to wake any threads + if (dsema->dsema_value == dsema->dsema_orig) { + return _dispatch_group_wake(dsema); + } + + _dispatch_semaphore_create_port(&dsema->dsema_waiter_port); + + // From xnu/osfmk/kern/sync_sema.c: + // wait_semaphore->count = -1; /* we don't keep an actual count */ + // + // The code above does not match the documentation, and that fact is + // not surprising. The documented semantics are clumsy to use in any + // practical way. The above hack effectively tricks the rest of the + // Mach semaphore logic to behave like the libdispatch algorithm. + + switch (timeout) { + default: + do { + nsec = _dispatch_timeout(timeout); + _timeout.tv_sec = (typeof(_timeout.tv_sec))(nsec / NSEC_PER_SEC); + _timeout.tv_nsec = (typeof(_timeout.tv_nsec))(nsec % NSEC_PER_SEC); + kr = slowpath(semaphore_timedwait(dsema->dsema_waiter_port, _timeout)); + } while (kr == KERN_ABORTED); + if (kr != KERN_OPERATION_TIMED_OUT) { + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + break; + } + // Fall through and try to undo the earlier change to dsema->dsema_group_waiters + case DISPATCH_TIME_NOW: + while ((orig = dsema->dsema_group_waiters)) { + if (dispatch_atomic_cmpxchg(&dsema->dsema_group_waiters, orig, orig - 1)) { + return KERN_OPERATION_TIMED_OUT; + } + } + // Another thread called semaphore_signal(). + // Fall through and drain the wakeup. + case DISPATCH_TIME_FOREVER: + do { + kr = semaphore_wait(dsema->dsema_waiter_port); + } while (kr == KERN_ABORTED); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + break; + } + + goto again; +} + +long +dispatch_group_wait(dispatch_group_t dg, dispatch_time_t timeout) +{ + dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg; + + if (dsema->dsema_value == dsema->dsema_orig) { + return 0; + } + if (timeout == 0) { + return KERN_OPERATION_TIMED_OUT; + } + return _dispatch_group_wait_slow(dsema, timeout); +} + +#ifdef __BLOCKS__ +void +dispatch_group_notify(dispatch_group_t dg, dispatch_queue_t dq, dispatch_block_t db) +{ + dispatch_group_notify_f(dg, dq, _dispatch_Block_copy(db), _dispatch_call_block_and_release); +} +#endif + +void +dispatch_group_notify_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt, void (*func)(void *)) +{ + dispatch_semaphore_t dsema = (dispatch_semaphore_t)dg; + struct dispatch_sema_notify_s *dsn, *prev; + + // FIXME -- this should be updated to use the continuation cache + while (!(dsn = malloc(sizeof(*dsn)))) { + sleep(1); + } + + dsn->dsn_next = NULL; + dsn->dsn_queue = dq; + dsn->dsn_ctxt = ctxt; + dsn->dsn_func = func; + _dispatch_retain(dq); + + prev = dispatch_atomic_xchg(&dsema->dsema_notify_tail, dsn); + if (fastpath(prev)) { + prev->dsn_next = dsn; + } else { + _dispatch_retain(dg); + dsema->dsema_notify_head = dsn; + if (dsema->dsema_value == dsema->dsema_orig) { + _dispatch_group_wake(dsema); + } + } +} + +void +_dispatch_semaphore_dispose(dispatch_semaphore_t dsema) +{ + kern_return_t kr; + + if (dsema->dsema_value < dsema->dsema_orig) { + DISPATCH_CLIENT_CRASH("Semaphore/group object deallocated while in use"); + } + + if (dsema->dsema_port) { + kr = semaphore_destroy(mach_task_self(), dsema->dsema_port); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + } + if (dsema->dsema_waiter_port) { + kr = semaphore_destroy(mach_task_self(), dsema->dsema_waiter_port); + DISPATCH_SEMAPHORE_VERIFY_KR(kr); + } + + _dispatch_dispose(dsema); +} + +size_t +_dispatch_semaphore_debug(dispatch_semaphore_t dsema, char *buf, size_t bufsiz) +{ + size_t offset = 0; + offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", dx_kind(dsema), dsema); + offset += dispatch_object_debug_attr(dsema, &buf[offset], bufsiz - offset); + offset += snprintf(&buf[offset], bufsiz - offset, "port = 0x%u, value = %ld, orig = %ld }", + dsema->dsema_port, dsema->dsema_value, dsema->dsema_orig); + return offset; +} + +#ifdef __BLOCKS__ +void +dispatch_group_async(dispatch_group_t dg, dispatch_queue_t dq, dispatch_block_t db) +{ + dispatch_group_async_f(dg, dq, _dispatch_Block_copy(db), _dispatch_call_block_and_release); +} +#endif + +DISPATCH_NOINLINE +void +dispatch_group_async_f(dispatch_group_t dg, dispatch_queue_t dq, void *ctxt, void (*func)(void *)) +{ + dispatch_continuation_t dc; + + _dispatch_retain(dg); + dispatch_group_enter(dg); + + dc = _dispatch_continuation_alloc_cacheonly() ?: _dispatch_continuation_alloc_from_heap(); + + dc->do_vtable = (void *)(DISPATCH_OBJ_ASYNC_BIT|DISPATCH_OBJ_GROUP_BIT); + dc->dc_func = func; + dc->dc_ctxt = ctxt; + dc->dc_group = dg; + + _dispatch_queue_push(dq, dc); +} diff --git a/src/semaphore.h b/src/semaphore.h new file mode 100644 index 0000000..882b567 --- /dev/null +++ b/src/semaphore.h @@ -0,0 +1,112 @@ +/* + * 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@ + */ + +#ifndef __DISPATCH_SEMAPHORE__ +#define __DISPATCH_SEMAPHORE__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +/*! + * @typedef dispatch_semaphore_t + * + * @abstract + * A counting semaphore. + */ +DISPATCH_DECL(dispatch_semaphore); + +__BEGIN_DECLS + +/*! + * @function dispatch_semaphore_create + * + * @abstract + * Creates new counting semaphore with an initial value. + * + * @discussion + * Passing zero for the value is useful for when two threads need to reconcile + * the completion of a particular event. Passing a value greather than zero is + * useful for managing a finite pool of resources, where the pool size is equal + * to the value. + * + * @param value + * The starting value for the semaphore. Passing a value less than zero will + * cause NULL to be returned. + * + * @result + * The newly created semaphore, or NULL on failure. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NOTHROW +dispatch_semaphore_t +dispatch_semaphore_create(long value); + +/*! + * @function dispatch_semaphore_wait + * + * @abstract + * Wait (decrement) for a semaphore. + * + * @discussion + * Decrement the counting semaphore. If the resulting value is less than zero, + * this function waits in FIFO order for a signal to occur before returning. + * + * @param dsema + * The semaphore. The result of passing NULL in this parameter is undefined. + * + * @param timeout + * When to timeout (see dispatch_time). As a convenience, there are the + * DISPATCH_TIME_NOW and DISPATCH_TIME_FOREVER constants. + * + * @result + * Returns zero on success, or non-zero if the timeout occurred. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +long +dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout); + +/*! + * @function dispatch_semaphore_signal + * + * @abstract + * Signal (increment) a semaphore. + * + * @discussion + * Increment the counting semaphore. If the previous value was less than zero, + * this function wakes a waiting thread before returning. + * + * @param dsema The counting semaphore. + * The result of passing NULL in this parameter is undefined. + * + * @result + * This function returns non-zero if a thread is woken. Otherwise, zero is + * returned. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +long +dispatch_semaphore_signal(dispatch_semaphore_t dsema); + +__END_DECLS + +#endif /* __DISPATCH_SEMAPHORE__ */ diff --git a/src/semaphore_internal.h b/src/semaphore_internal.h new file mode 100644 index 0000000..3af28c0 --- /dev/null +++ b/src/semaphore_internal.h @@ -0,0 +1,51 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_SEMAPHORE_INTERNAL__ +#define __DISPATCH_SEMAPHORE_INTERNAL__ + +struct dispatch_sema_notify_s { + struct dispatch_sema_notify_s *dsn_next; + dispatch_queue_t dsn_queue; + void *dsn_ctxt; + void (*dsn_func)(void *); +}; + +struct dispatch_semaphore_s { + DISPATCH_STRUCT_HEADER(dispatch_semaphore_s, dispatch_semaphore_vtable_s); + long dsema_value; + long dsema_orig; + size_t dsema_sent_ksignals; + semaphore_t dsema_port; + semaphore_t dsema_waiter_port; + size_t dsema_group_waiters; + struct dispatch_sema_notify_s *dsema_notify_head; + struct dispatch_sema_notify_s *dsema_notify_tail; +}; + +extern const struct dispatch_semaphore_vtable_s _dispatch_semaphore_vtable; + +#endif diff --git a/src/shims.c b/src/shims.c new file mode 100644 index 0000000..a02d453 --- /dev/null +++ b/src/shims.c @@ -0,0 +1,65 @@ +/* + * 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@ + */ + +#include "internal.h" + +void * +dispatch_mach_msg_get_context(mach_msg_header_t *msg) +{ + mach_msg_context_trailer_t *tp; + void *context = NULL; + + tp = (mach_msg_context_trailer_t *)((uint8_t *)msg + round_msg(msg->msgh_size)); + if (tp->msgh_trailer_size >= (mach_msg_size_t)sizeof(mach_msg_context_trailer_t)) { + context = (void *)(uintptr_t)tp->msgh_context; + } + + return context; +} + +/* + * Raw Mach message support + */ +boolean_t +_dispatch_machport_callback(mach_msg_header_t *msg, mach_msg_header_t *reply, + void (*callback)(mach_msg_header_t *)) +{ + mig_reply_setup(msg, reply); + ((mig_reply_error_t*)reply)->RetCode = MIG_NO_REPLY; + + callback(msg); + + return TRUE; +} + +/* + * CFMachPort compatibility + */ +boolean_t +_dispatch_CFMachPortCallBack(mach_msg_header_t *msg, mach_msg_header_t *reply, + void (*callback)(struct __CFMachPort *, void *msg, signed long size, void *)) +{ + mig_reply_setup(msg, reply); + ((mig_reply_error_t*)reply)->RetCode = MIG_NO_REPLY; + + callback(NULL, msg, msg->msgh_size, dispatch_mach_msg_get_context(msg)); + + return TRUE; +} diff --git a/src/source.c b/src/source.c new file mode 100644 index 0000000..7259b0b --- /dev/null +++ b/src/source.c @@ -0,0 +1,1995 @@ +/* + * 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@ + */ + +#include "internal.h" +#include "protocol.h" +#include "protocolServer.h" +#include + +#define DISPATCH_EVFILT_TIMER (-EVFILT_SYSCOUNT - 1) +#define DISPATCH_EVFILT_CUSTOM_ADD (-EVFILT_SYSCOUNT - 2) +#define DISPATCH_EVFILT_CUSTOM_OR (-EVFILT_SYSCOUNT - 3) +#define DISPATCH_EVFILT_SYSCOUNT (EVFILT_SYSCOUNT + 3) + +#define DISPATCH_TIMER_INDEX_WALL 0 +#define DISPATCH_TIMER_INDEX_MACH 1 +static struct dispatch_kevent_s _dispatch_kevent_timer[] = { + { + .dk_kevent = { + .ident = DISPATCH_TIMER_INDEX_WALL, + .filter = DISPATCH_EVFILT_TIMER, + .udata = &_dispatch_kevent_timer[0], + }, + .dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_timer[0].dk_sources), + }, + { + .dk_kevent = { + .ident = DISPATCH_TIMER_INDEX_MACH, + .filter = DISPATCH_EVFILT_TIMER, + .udata = &_dispatch_kevent_timer[1], + }, + .dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_timer[1].dk_sources), + }, +}; +#define DISPATCH_TIMER_COUNT (sizeof _dispatch_kevent_timer / sizeof _dispatch_kevent_timer[0]) + +static struct dispatch_kevent_s _dispatch_kevent_data_or = { + .dk_kevent = { + .filter = DISPATCH_EVFILT_CUSTOM_OR, + .flags = EV_CLEAR, + .udata = &_dispatch_kevent_data_or, + }, + .dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_data_or.dk_sources), +}; +static struct dispatch_kevent_s _dispatch_kevent_data_add = { + .dk_kevent = { + .filter = DISPATCH_EVFILT_CUSTOM_ADD, + .udata = &_dispatch_kevent_data_add, + }, + .dk_sources = TAILQ_HEAD_INITIALIZER(_dispatch_kevent_data_add.dk_sources), +}; + +#ifndef DISPATCH_NO_LEGACY +struct dispatch_source_attr_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_source_attr_s); +}; + +struct dispatch_source_attr_s { + DISPATCH_STRUCT_HEADER(dispatch_source_attr_s, dispatch_source_attr_vtable_s); + void* finalizer_ctxt; + dispatch_source_finalizer_function_t finalizer_func; + void* context; +}; +#endif /* DISPATCH_NO_LEGACY */ + +#define _dispatch_source_call_block ((void *)-1) +static void _dispatch_source_latch_and_call(dispatch_source_t ds); +static void _dispatch_source_cancel_callout(dispatch_source_t ds); +static bool _dispatch_source_probe(dispatch_source_t ds); +static void _dispatch_source_dispose(dispatch_source_t ds); +static void _dispatch_source_merge_kevent(dispatch_source_t ds, const struct kevent *ke); +static size_t _dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz); +static size_t dispatch_source_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz); +static dispatch_queue_t _dispatch_source_invoke(dispatch_source_t ds); + +static void _dispatch_kevent_merge(dispatch_source_t ds); +static void _dispatch_kevent_release(dispatch_source_t ds); +static void _dispatch_kevent_resume(dispatch_kevent_t dk, uint32_t new_flags, uint32_t del_flags); +static void _dispatch_kevent_machport_resume(dispatch_kevent_t dk, uint32_t new_flags, uint32_t del_flags); +static void _dispatch_kevent_machport_enable(dispatch_kevent_t dk); +static void _dispatch_kevent_machport_disable(dispatch_kevent_t dk); + +static void _dispatch_drain_mach_messages(struct kevent *ke); +static void _dispatch_timer_list_update(dispatch_source_t ds); + +static void +_dispatch_mach_notify_source_init(void *context __attribute__((unused))); + +static const char * +_evfiltstr(short filt) +{ + switch (filt) { +#define _evfilt2(f) case (f): return #f + _evfilt2(EVFILT_READ); + _evfilt2(EVFILT_WRITE); + _evfilt2(EVFILT_AIO); + _evfilt2(EVFILT_VNODE); + _evfilt2(EVFILT_PROC); + _evfilt2(EVFILT_SIGNAL); + _evfilt2(EVFILT_TIMER); + _evfilt2(EVFILT_MACHPORT); + _evfilt2(EVFILT_FS); + _evfilt2(EVFILT_USER); + _evfilt2(EVFILT_SESSION); + + _evfilt2(DISPATCH_EVFILT_TIMER); + _evfilt2(DISPATCH_EVFILT_CUSTOM_ADD); + _evfilt2(DISPATCH_EVFILT_CUSTOM_OR); + default: + return "EVFILT_missing"; + } +} + +#define DSL_HASH_SIZE 256u // must be a power of two +#define DSL_HASH(x) ((x) & (DSL_HASH_SIZE - 1)) + +static TAILQ_HEAD(, dispatch_kevent_s) _dispatch_sources[DSL_HASH_SIZE]; + +static dispatch_kevent_t +_dispatch_kevent_find(uintptr_t ident, short filter) +{ + uintptr_t hash = DSL_HASH(filter == EVFILT_MACHPORT ? MACH_PORT_INDEX(ident) : ident); + dispatch_kevent_t dki; + + TAILQ_FOREACH(dki, &_dispatch_sources[hash], dk_list) { + if (dki->dk_kevent.ident == ident && dki->dk_kevent.filter == filter) { + break; + } + } + return dki; +} + +static void +_dispatch_kevent_insert(dispatch_kevent_t dk) +{ + uintptr_t ident = dk->dk_kevent.ident; + uintptr_t hash = DSL_HASH(dk->dk_kevent.filter == EVFILT_MACHPORT ? MACH_PORT_INDEX(ident) : ident); + + TAILQ_INSERT_TAIL(&_dispatch_sources[hash], dk, dk_list); +} + +void +dispatch_source_cancel(dispatch_source_t ds) +{ +#if DISPATCH_DEBUG + dispatch_debug(ds, __FUNCTION__); +#endif + dispatch_atomic_or(&ds->ds_atomic_flags, DSF_CANCELED); + _dispatch_wakeup(ds); +} + +#ifndef DISPATCH_NO_LEGACY +void +_dispatch_source_legacy_xref_release(dispatch_source_t ds) +{ + if (ds->ds_is_legacy) { + if (!(ds->ds_timer.flags & DISPATCH_TIMER_ONESHOT)) { + dispatch_source_cancel(ds); + } + + // Clients often leave sources suspended at the last release + dispatch_atomic_and(&ds->do_suspend_cnt, DISPATCH_OBJECT_SUSPEND_LOCK); + } else if (slowpath(DISPATCH_OBJECT_SUSPENDED(ds))) { + // Arguments for and against this assert are within 6705399 + DISPATCH_CLIENT_CRASH("Release of a suspended object"); + } + _dispatch_wakeup(ds); + _dispatch_release(ds); +} +#endif /* DISPATCH_NO_LEGACY */ + +long +dispatch_source_testcancel(dispatch_source_t ds) +{ + return (bool)(ds->ds_atomic_flags & DSF_CANCELED); +} + + +unsigned long +dispatch_source_get_mask(dispatch_source_t ds) +{ + return ds->ds_pending_data_mask; +} + +uintptr_t +dispatch_source_get_handle(dispatch_source_t ds) +{ + return (int)ds->ds_ident_hack; +} + +unsigned long +dispatch_source_get_data(dispatch_source_t ds) +{ + return ds->ds_data; +} + +#if DISPATCH_DEBUG +void +dispatch_debug_kevents(struct kevent* kev, size_t count, const char* str) +{ + size_t i; + for (i = 0; i < count; ++i) { + _dispatch_log("kevent[%lu] = { ident = %p, filter = %s, flags = 0x%x, fflags = 0x%x, data = %p, udata = %p }: %s", + i, (void*)kev[i].ident, _evfiltstr(kev[i].filter), kev[i].flags, kev[i].fflags, (void*)kev[i].data, (void*)kev[i].udata, str); + } +} +#endif + +static size_t +_dispatch_source_kevent_debug(dispatch_source_t ds, char* buf, size_t bufsiz) +{ + size_t offset = _dispatch_source_debug(ds, buf, bufsiz); + offset += snprintf(&buf[offset], bufsiz - offset, "filter = %s }", + ds->ds_dkev ? _evfiltstr(ds->ds_dkev->dk_kevent.filter) : "????"); + return offset; +} + +static void +_dispatch_source_init_tail_queue_array(void *context __attribute__((unused))) +{ + unsigned int i; + for (i = 0; i < DSL_HASH_SIZE; i++) { + TAILQ_INIT(&_dispatch_sources[i]); + } + + TAILQ_INSERT_TAIL(&_dispatch_sources[DSL_HASH(DISPATCH_TIMER_INDEX_WALL)], &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_WALL], dk_list); + TAILQ_INSERT_TAIL(&_dispatch_sources[DSL_HASH(DISPATCH_TIMER_INDEX_MACH)], &_dispatch_kevent_timer[DISPATCH_TIMER_INDEX_MACH], dk_list); + TAILQ_INSERT_TAIL(&_dispatch_sources[0], &_dispatch_kevent_data_or, dk_list); + TAILQ_INSERT_TAIL(&_dispatch_sources[0], &_dispatch_kevent_data_add, dk_list); +} + +// Find existing kevents, and merge any new flags if necessary +void +_dispatch_kevent_merge(dispatch_source_t ds) +{ + static dispatch_once_t pred; + dispatch_kevent_t dk; + typeof(dk->dk_kevent.fflags) new_flags; + bool do_resume = false; + + if (ds->ds_is_installed) { + return; + } + ds->ds_is_installed = true; + + dispatch_once_f(&pred, NULL, _dispatch_source_init_tail_queue_array); + + dk = _dispatch_kevent_find(ds->ds_dkev->dk_kevent.ident, ds->ds_dkev->dk_kevent.filter); + + if (dk) { + // If an existing dispatch kevent is found, check to see if new flags + // need to be added to the existing kevent + new_flags = ~dk->dk_kevent.fflags & ds->ds_dkev->dk_kevent.fflags; + dk->dk_kevent.fflags |= ds->ds_dkev->dk_kevent.fflags; + free(ds->ds_dkev); + ds->ds_dkev = dk; + do_resume = new_flags; + } else { + dk = ds->ds_dkev; + _dispatch_kevent_insert(dk); + new_flags = dk->dk_kevent.fflags; + do_resume = true; + } + + TAILQ_INSERT_TAIL(&dk->dk_sources, ds, ds_list); + + // Re-register the kevent with the kernel if new flags were added + // by the dispatch kevent + if (do_resume) { + dk->dk_kevent.flags |= EV_ADD; + _dispatch_kevent_resume(ds->ds_dkev, new_flags, 0); + ds->ds_is_armed = true; + } +} + + +void +_dispatch_kevent_resume(dispatch_kevent_t dk, uint32_t new_flags, uint32_t del_flags) +{ + switch (dk->dk_kevent.filter) { + case DISPATCH_EVFILT_TIMER: + case DISPATCH_EVFILT_CUSTOM_ADD: + case DISPATCH_EVFILT_CUSTOM_OR: + // these types not registered with kevent + return; + case EVFILT_MACHPORT: + _dispatch_kevent_machport_resume(dk, new_flags, del_flags); + break; + case EVFILT_PROC: + if (dk->dk_kevent.flags & EV_ONESHOT) { + return; + } + // fall through + default: + _dispatch_update_kq(&dk->dk_kevent); + if (dk->dk_kevent.flags & EV_DISPATCH) { + dk->dk_kevent.flags &= ~EV_ADD; + } + break; + } +} + +dispatch_queue_t +_dispatch_source_invoke(dispatch_source_t ds) +{ + // This function performs all source actions. Each action is responsible + // for verifying that it takes place on the appropriate queue. If the + // current queue is not the correct queue for this action, the correct queue + // will be returned and the invoke will be re-driven on that queue. + + // The order of tests here in invoke and in probe should be consistent. + + dispatch_queue_t dq = _dispatch_queue_get_current(); + + if (!ds->ds_is_installed) { + // The source needs to be installed on the manager queue. + if (dq != &_dispatch_mgr_q) { + return &_dispatch_mgr_q; + } + _dispatch_kevent_merge(ds); + } else if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { + // The source has been cancelled and needs to be uninstalled from the + // manager queue. After uninstallation, the cancellation handler needs + // to be delivered to the target queue. + if (ds->ds_dkev) { + if (dq != &_dispatch_mgr_q) { + return &_dispatch_mgr_q; + } + _dispatch_kevent_release(ds); + return ds->do_targetq; + } else if (ds->ds_cancel_handler) { + if (dq != ds->do_targetq) { + return ds->do_targetq; + } + } + _dispatch_source_cancel_callout(ds); + } else if (ds->ds_pending_data) { + // The source has pending data to deliver via the event handler callback + // on the target queue. Some sources need to be rearmed on the manager + // queue after event delivery. + if (dq != ds->do_targetq) { + return ds->do_targetq; + } + _dispatch_source_latch_and_call(ds); + if (ds->ds_needs_rearm) { + return &_dispatch_mgr_q; + } + } else if (ds->ds_needs_rearm && !ds->ds_is_armed) { + // The source needs to be rearmed on the manager queue. + if (dq != &_dispatch_mgr_q) { + return &_dispatch_mgr_q; + } + _dispatch_kevent_resume(ds->ds_dkev, 0, 0); + ds->ds_is_armed = true; + } + + return NULL; +} + +bool +_dispatch_source_probe(dispatch_source_t ds) +{ + // This function determines whether the source needs to be invoked. + // The order of tests here in probe and in invoke should be consistent. + + if (!ds->ds_is_installed) { + // The source needs to be installed on the manager queue. + return true; + } else if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { + // The source needs to be uninstalled from the manager queue, or the + // cancellation handler needs to be delivered to the target queue. + // Note: cancellation assumes installation. + if (ds->ds_dkev || ds->ds_cancel_handler) { + return true; + } + } else if (ds->ds_pending_data) { + // The source has pending data to deliver to the target queue. + return true; + } else if (ds->ds_needs_rearm && !ds->ds_is_armed) { + // The source needs to be rearmed on the manager queue. + return true; + } + // Nothing to do. + return false; +} + +void +_dispatch_source_dispose(dispatch_source_t ds) +{ + _dispatch_queue_dispose((dispatch_queue_t)ds); +} + +static void +_dispatch_kevent_debugger2(void *context, dispatch_source_t unused __attribute__((unused))) +{ + struct sockaddr sa; + socklen_t sa_len = sizeof(sa); + int c, fd = (int)(long)context; + unsigned int i; + dispatch_kevent_t dk; + dispatch_source_t ds; + FILE *debug_stream; + + c = accept(fd, &sa, &sa_len); + if (c == -1) { + if (errno != EAGAIN) { + dispatch_assume_zero(errno); + } + return; + } +#if 0 + int r = fcntl(c, F_SETFL, 0); // disable non-blocking IO + if (r == -1) { + dispatch_assume_zero(errno); + } +#endif + debug_stream = fdopen(c, "a"); + if (!dispatch_assume(debug_stream)) { + close(c); + return; + } + + fprintf(debug_stream, "HTTP/1.0 200 OK\r\n"); + fprintf(debug_stream, "Content-type: text/html\r\n"); + fprintf(debug_stream, "Pragma: nocache\r\n"); + fprintf(debug_stream, "\r\n"); + fprintf(debug_stream, "\nPID %u\n\n

    \n", getpid()); + + //fprintf(debug_stream, "DKDKDKDKDKDKDK\n"); + + for (i = 0; i < DSL_HASH_SIZE; i++) { + if (TAILQ_EMPTY(&_dispatch_sources[i])) { + continue; + } + TAILQ_FOREACH(dk, &_dispatch_sources[i], dk_list) { + fprintf(debug_stream, "\t
  • DK %p ident %lu filter %s flags 0x%hx fflags 0x%x data 0x%lx udata %p\n", + dk, dk->dk_kevent.ident, _evfiltstr(dk->dk_kevent.filter), dk->dk_kevent.flags, + dk->dk_kevent.fflags, dk->dk_kevent.data, dk->dk_kevent.udata); + fprintf(debug_stream, "\t\t
      \n"); + TAILQ_FOREACH(ds, &dk->dk_sources, ds_list) { + fprintf(debug_stream, "\t\t\t
    • DS %p refcnt 0x%x suspend 0x%x data 0x%lx mask 0x%lx flags 0x%x
    • \n", + ds, ds->do_ref_cnt, ds->do_suspend_cnt, ds->ds_pending_data, ds->ds_pending_data_mask, + ds->ds_atomic_flags); + if (ds->do_suspend_cnt == DISPATCH_OBJECT_SUSPEND_LOCK) { + dispatch_queue_t dq = ds->do_targetq; + fprintf(debug_stream, "\t\t
      DQ: %p refcnt 0x%x suspend 0x%x label: %s\n", dq, dq->do_ref_cnt, dq->do_suspend_cnt, dq->dq_label); + } + } + fprintf(debug_stream, "\t\t
    \n"); + fprintf(debug_stream, "\t
  • \n"); + } + } + fprintf(debug_stream, "
\n\n\n"); + fflush(debug_stream); + fclose(debug_stream); +} + +static void +_dispatch_kevent_debugger(void *context __attribute__((unused))) +{ + union { + struct sockaddr_in sa_in; + struct sockaddr sa; + } sa_u = { + .sa_in = { + .sin_family = AF_INET, + .sin_addr = { htonl(INADDR_LOOPBACK), }, + }, + }; + dispatch_source_t ds; + const char *valstr; + int val, r, fd, sock_opt = 1; + socklen_t slen = sizeof(sa_u); + + if (issetugid()) { + return; + } + valstr = getenv("LIBDISPATCH_DEBUGGER"); + if (!valstr) { + return; + } + val = atoi(valstr); + if (val == 2) { + sa_u.sa_in.sin_addr.s_addr = 0; + } + fd = socket(PF_INET, SOCK_STREAM, 0); + if (fd == -1) { + dispatch_assume_zero(errno); + return; + } + r = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (void *)&sock_opt, (socklen_t) sizeof sock_opt); + if (r == -1) { + dispatch_assume_zero(errno); + goto out_bad; + } +#if 0 + r = fcntl(fd, F_SETFL, O_NONBLOCK); + if (r == -1) { + dispatch_assume_zero(errno); + goto out_bad; + } +#endif + r = bind(fd, &sa_u.sa, sizeof(sa_u)); + if (r == -1) { + dispatch_assume_zero(errno); + goto out_bad; + } + r = listen(fd, SOMAXCONN); + if (r == -1) { + dispatch_assume_zero(errno); + goto out_bad; + } + r = getsockname(fd, &sa_u.sa, &slen); + if (r == -1) { + dispatch_assume_zero(errno); + goto out_bad; + } + ds = dispatch_source_read_create_f(fd, NULL, &_dispatch_mgr_q, (void *)(long)fd, _dispatch_kevent_debugger2); + if (dispatch_assume(ds)) { + _dispatch_log("LIBDISPATCH: debug port: %hu", ntohs(sa_u.sa_in.sin_port)); + return; + } +out_bad: + close(fd); +} + +void +_dispatch_source_drain_kevent(struct kevent *ke) +{ + static dispatch_once_t pred; + dispatch_kevent_t dk = ke->udata; + dispatch_source_t dsi; + + dispatch_once_f(&pred, NULL, _dispatch_kevent_debugger); + + dispatch_debug_kevents(ke, 1, __func__); + + if (ke->filter == EVFILT_MACHPORT) { + return _dispatch_drain_mach_messages(ke); + } + dispatch_assert(dk); + + if (ke->flags & EV_ONESHOT) { + dk->dk_kevent.flags |= EV_ONESHOT; + } + + TAILQ_FOREACH(dsi, &dk->dk_sources, ds_list) { + _dispatch_source_merge_kevent(dsi, ke); + } +} + +static void +_dispatch_kevent_dispose(dispatch_kevent_t dk) +{ + uintptr_t key; + + switch (dk->dk_kevent.filter) { + case DISPATCH_EVFILT_TIMER: + case DISPATCH_EVFILT_CUSTOM_ADD: + case DISPATCH_EVFILT_CUSTOM_OR: + // these sources live on statically allocated lists + return; + case EVFILT_MACHPORT: + _dispatch_kevent_machport_resume(dk, 0, dk->dk_kevent.fflags); + break; + case EVFILT_PROC: + if (dk->dk_kevent.flags & EV_ONESHOT) { + break; // implicitly deleted + } + // fall through + default: + if (~dk->dk_kevent.flags & EV_DELETE) { + dk->dk_kevent.flags |= EV_DELETE; + _dispatch_update_kq(&dk->dk_kevent); + } + break; + } + + if (dk->dk_kevent.filter == EVFILT_MACHPORT) { + key = MACH_PORT_INDEX(dk->dk_kevent.ident); + } else { + key = dk->dk_kevent.ident; + } + + TAILQ_REMOVE(&_dispatch_sources[DSL_HASH(key)], dk, dk_list); + free(dk); +} + +void +_dispatch_kevent_release(dispatch_source_t ds) +{ + dispatch_kevent_t dk = ds->ds_dkev; + dispatch_source_t dsi; + uint32_t del_flags, fflags = 0; + + ds->ds_dkev = NULL; + + TAILQ_REMOVE(&dk->dk_sources, ds, ds_list); + + if (TAILQ_EMPTY(&dk->dk_sources)) { + _dispatch_kevent_dispose(dk); + } else { + TAILQ_FOREACH(dsi, &dk->dk_sources, ds_list) { + fflags |= (uint32_t)dsi->ds_pending_data_mask; + } + del_flags = (uint32_t)ds->ds_pending_data_mask & ~fflags; + if (del_flags) { + dk->dk_kevent.flags |= EV_ADD; + dk->dk_kevent.fflags = fflags; + _dispatch_kevent_resume(dk, 0, del_flags); + } + } + + ds->ds_is_armed = false; + ds->ds_needs_rearm = false; // re-arm is pointless and bad now + _dispatch_release(ds); // the retain is done at creation time +} + +void +_dispatch_source_merge_kevent(dispatch_source_t ds, const struct kevent *ke) +{ + struct kevent fake; + + if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { + return; + } + + // EVFILT_PROC may fail with ESRCH when the process exists but is a zombie. + // We simulate an exit event in this case. + if (ke->flags & EV_ERROR) { + if (ke->filter == EVFILT_PROC && ke->data == ESRCH) { + fake = *ke; + fake.flags &= ~EV_ERROR; + fake.fflags = NOTE_EXIT; + fake.data = 0; + ke = &fake; + } else { + // log the unexpected error + dispatch_assume_zero(ke->data); + return; + } + } + + if (ds->ds_is_level) { + // ke->data is signed and "negative available data" makes no sense + // zero bytes happens when EV_EOF is set + // 10A268 does not fail this assert with EVFILT_READ and a 10 GB file + dispatch_assert(ke->data >= 0l); + ds->ds_pending_data = ~ke->data; + } else if (ds->ds_is_adder) { + dispatch_atomic_add(&ds->ds_pending_data, ke->data); + } else { + dispatch_atomic_or(&ds->ds_pending_data, ke->fflags & ds->ds_pending_data_mask); + } + + // EV_DISPATCH and EV_ONESHOT sources are no longer armed after delivery + if (ds->ds_needs_rearm) { + ds->ds_is_armed = false; + } + + _dispatch_wakeup(ds); +} + +void +_dispatch_source_latch_and_call(dispatch_source_t ds) +{ + unsigned long prev; + + if ((ds->ds_atomic_flags & DSF_CANCELED) || (ds->do_xref_cnt == 0)) { + return; + } + prev = dispatch_atomic_xchg(&ds->ds_pending_data, 0); + if (ds->ds_is_level) { + ds->ds_data = ~prev; + } else { + ds->ds_data = prev; + } + if (dispatch_assume(prev)) { + if (ds->ds_handler_func) { + ds->ds_handler_func(ds->ds_handler_ctxt, ds); + } + } +} + +void +_dispatch_source_cancel_callout(dispatch_source_t ds) +{ + ds->ds_pending_data_mask = 0; + ds->ds_pending_data = 0; + ds->ds_data = 0; + +#ifdef __BLOCKS__ + if (ds->ds_handler_is_block) { + Block_release(ds->ds_handler_ctxt); + ds->ds_handler_is_block = false; + ds->ds_handler_func = NULL; + ds->ds_handler_ctxt = NULL; + } +#endif + + if (!ds->ds_cancel_handler) { + return; + } + if (ds->ds_cancel_is_block) { +#ifdef __BLOCKS__ + dispatch_block_t b = ds->ds_cancel_handler; + if (ds->ds_atomic_flags & DSF_CANCELED) { + b(); + } + Block_release(ds->ds_cancel_handler); + ds->ds_cancel_is_block = false; +#endif + } else { + dispatch_function_t f = ds->ds_cancel_handler; + if (ds->ds_atomic_flags & DSF_CANCELED) { + f(ds->do_ctxt); + } + } + ds->ds_cancel_handler = NULL; +} + +const struct dispatch_source_vtable_s _dispatch_source_kevent_vtable = { + .do_type = DISPATCH_SOURCE_KEVENT_TYPE, + .do_kind = "kevent-source", + .do_invoke = _dispatch_source_invoke, + .do_dispose = _dispatch_source_dispose, + .do_probe = _dispatch_source_probe, + .do_debug = _dispatch_source_kevent_debug, +}; + +void +dispatch_source_merge_data(dispatch_source_t ds, unsigned long val) +{ + struct kevent kev = { + .fflags = (typeof(kev.fflags))val, + .data = val, + }; + + dispatch_assert(ds->ds_dkev->dk_kevent.filter == DISPATCH_EVFILT_CUSTOM_ADD || + ds->ds_dkev->dk_kevent.filter == DISPATCH_EVFILT_CUSTOM_OR); + + _dispatch_source_merge_kevent(ds, &kev); +} + +size_t +dispatch_source_debug_attr(dispatch_source_t ds, char* buf, size_t bufsiz) +{ + dispatch_queue_t target = ds->do_targetq; + return snprintf(buf, bufsiz, + "target = %s[%p], pending_data = 0x%lx, pending_data_mask = 0x%lx, ", + target ? target->dq_label : "", target, + ds->ds_pending_data, ds->ds_pending_data_mask); +} + +size_t +_dispatch_source_debug(dispatch_source_t ds, char* buf, size_t bufsiz) +{ + size_t offset = 0; + offset += snprintf(&buf[offset], bufsiz - offset, "%s[%p] = { ", dx_kind(ds), ds); + offset += dispatch_object_debug_attr(ds, &buf[offset], bufsiz - offset); + offset += dispatch_source_debug_attr(ds, &buf[offset], bufsiz - offset); + return offset; +} + +#ifndef DISPATCH_NO_LEGACY +static void +dispatch_source_attr_dispose(dispatch_source_attr_t attr) +{ + // release the finalizer block if necessary + dispatch_source_attr_set_finalizer(attr, NULL); + _dispatch_dispose(attr); +} + +static const struct dispatch_source_attr_vtable_s dispatch_source_attr_vtable = { + .do_type = DISPATCH_SOURCE_ATTR_TYPE, + .do_kind = "source-attr", + .do_dispose = dispatch_source_attr_dispose, +}; + +dispatch_source_attr_t +dispatch_source_attr_create(void) +{ + dispatch_source_attr_t rval = calloc(1, sizeof(struct dispatch_source_attr_s)); + + if (rval) { + rval->do_vtable = &dispatch_source_attr_vtable; + rval->do_next = DISPATCH_OBJECT_LISTLESS; + rval->do_targetq = dispatch_get_global_queue(0, 0); + rval->do_ref_cnt = 1; + rval->do_xref_cnt = 1; + } + + return rval; +} + +void +dispatch_source_attr_set_finalizer_f(dispatch_source_attr_t attr, + void *context, dispatch_source_finalizer_function_t finalizer) +{ +#ifdef __BLOCKS__ + if (attr->finalizer_func == (void*)_dispatch_call_block_and_release2) { + Block_release(attr->finalizer_ctxt); + } +#endif + + attr->finalizer_ctxt = context; + attr->finalizer_func = finalizer; +} + +#ifdef __BLOCKS__ +long +dispatch_source_attr_set_finalizer(dispatch_source_attr_t attr, + dispatch_source_finalizer_t finalizer) +{ + void *ctxt; + dispatch_source_finalizer_function_t func; + + if (finalizer) { + if (!(ctxt = Block_copy(finalizer))) { + return 1; + } + func = (void *)_dispatch_call_block_and_release2; + } else { + ctxt = NULL; + func = NULL; + } + + dispatch_source_attr_set_finalizer_f(attr, ctxt, func); + + return 0; +} + +dispatch_source_finalizer_t +dispatch_source_attr_get_finalizer(dispatch_source_attr_t attr) +{ + if (attr->finalizer_func == (void*)_dispatch_call_block_and_release2) { + return (dispatch_source_finalizer_t)attr->finalizer_ctxt; + } else if (attr->finalizer_func == NULL) { + return NULL; + } else { + abort(); // finalizer is not a block... + } +} +#endif + +void +dispatch_source_attr_set_context(dispatch_source_attr_t attr, void *context) +{ + attr->context = context; +} + +dispatch_source_attr_t +dispatch_source_attr_copy(dispatch_source_attr_t proto) +{ + dispatch_source_attr_t rval = NULL; + + if (proto && (rval = malloc(sizeof(struct dispatch_source_attr_s)))) { + memcpy(rval, proto, sizeof(struct dispatch_source_attr_s)); +#ifdef __BLOCKS__ + if (rval->finalizer_func == (void*)_dispatch_call_block_and_release2) { + rval->finalizer_ctxt = Block_copy(rval->finalizer_ctxt); + } +#endif + } else if (!proto) { + rval = dispatch_source_attr_create(); + } + return rval; +} +#endif /* DISPATCH_NO_LEGACY */ + + +struct dispatch_source_type_s { + struct kevent ke; + uint64_t mask; +}; + +const struct dispatch_source_type_s _dispatch_source_type_timer = { + .ke = { + .filter = DISPATCH_EVFILT_TIMER, + }, + .mask = DISPATCH_TIMER_INTERVAL|DISPATCH_TIMER_ONESHOT|DISPATCH_TIMER_ABSOLUTE|DISPATCH_TIMER_WALL_CLOCK, +}; + +const struct dispatch_source_type_s _dispatch_source_type_read = { + .ke = { + .filter = EVFILT_READ, + .flags = EV_DISPATCH, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_write = { + .ke = { + .filter = EVFILT_WRITE, + .flags = EV_DISPATCH, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_proc = { + .ke = { + .filter = EVFILT_PROC, + .flags = EV_CLEAR, + }, + .mask = NOTE_EXIT|NOTE_FORK|NOTE_EXEC|NOTE_SIGNAL|NOTE_REAP, +}; + +const struct dispatch_source_type_s _dispatch_source_type_signal = { + .ke = { + .filter = EVFILT_SIGNAL, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_vnode = { + .ke = { + .filter = EVFILT_VNODE, + .flags = EV_CLEAR, + }, + .mask = NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND|NOTE_ATTRIB|NOTE_LINK|NOTE_RENAME|NOTE_REVOKE|NOTE_NONE, +}; + +const struct dispatch_source_type_s _dispatch_source_type_vfs = { + .ke = { + .filter = EVFILT_FS, + .flags = EV_CLEAR, + }, + .mask = VQ_NOTRESP|VQ_NEEDAUTH|VQ_LOWDISK|VQ_MOUNT|VQ_UNMOUNT|VQ_DEAD|VQ_ASSIST|VQ_NOTRESPLOCK|VQ_UPDATE|VQ_VERYLOWDISK, +}; + +const struct dispatch_source_type_s _dispatch_source_type_mach_send = { + .ke = { + .filter = EVFILT_MACHPORT, + .flags = EV_DISPATCH, + .fflags = DISPATCH_MACHPORT_DEAD, + }, + .mask = DISPATCH_MACH_SEND_DEAD, +}; + +const struct dispatch_source_type_s _dispatch_source_type_mach_recv = { + .ke = { + .filter = EVFILT_MACHPORT, + .flags = EV_DISPATCH, + .fflags = DISPATCH_MACHPORT_RECV, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_data_add = { + .ke = { + .filter = DISPATCH_EVFILT_CUSTOM_ADD, + }, +}; + +const struct dispatch_source_type_s _dispatch_source_type_data_or = { + .ke = { + .filter = DISPATCH_EVFILT_CUSTOM_OR, + .flags = EV_CLEAR, + .fflags = ~0, + }, +}; + +dispatch_source_t +dispatch_source_create(dispatch_source_type_t type, + uintptr_t handle, + unsigned long mask, + dispatch_queue_t q) +{ + const struct kevent *proto_kev = &type->ke; + dispatch_source_t ds = NULL; + dispatch_kevent_t dk = NULL; + + // input validation + if (type == NULL || (mask & ~type->mask)) { + goto out_bad; + } + + switch (type->ke.filter) { + case EVFILT_SIGNAL: + if (handle >= NSIG) { + goto out_bad; + } + break; + case EVFILT_FS: + case DISPATCH_EVFILT_CUSTOM_ADD: + case DISPATCH_EVFILT_CUSTOM_OR: + case DISPATCH_EVFILT_TIMER: + if (handle) { + goto out_bad; + } + break; + default: + break; + } + + ds = calloc(1ul, sizeof(struct dispatch_source_s)); + if (slowpath(!ds)) { + goto out_bad; + } + dk = calloc(1ul, sizeof(struct dispatch_kevent_s)); + if (slowpath(!dk)) { + goto out_bad; + } + + dk->dk_kevent = *proto_kev; + dk->dk_kevent.ident = handle; + dk->dk_kevent.flags |= EV_ADD|EV_ENABLE; + dk->dk_kevent.fflags |= (uint32_t)mask; + dk->dk_kevent.udata = dk; + TAILQ_INIT(&dk->dk_sources); + + // Initialize as a queue first, then override some settings below. + _dispatch_queue_init((dispatch_queue_t)ds); + strlcpy(ds->dq_label, "source", sizeof(ds->dq_label)); + + // Dispatch Object + ds->do_vtable = &_dispatch_source_kevent_vtable; + ds->do_ref_cnt++; // the reference the manger queue holds + ds->do_suspend_cnt = DISPATCH_OBJECT_SUSPEND_INTERVAL; + // do_targetq will be retained below, past point of no-return + ds->do_targetq = q; + + // Dispatch Source + ds->ds_ident_hack = dk->dk_kevent.ident; + ds->ds_dkev = dk; + ds->ds_pending_data_mask = dk->dk_kevent.fflags; + if ((EV_DISPATCH|EV_ONESHOT) & proto_kev->flags) { + if (proto_kev->filter != EVFILT_MACHPORT) { + ds->ds_is_level = true; + } + ds->ds_needs_rearm = true; + } else if (!(EV_CLEAR & proto_kev->flags)) { + // we cheat and use EV_CLEAR to mean a "flag thingy" + ds->ds_is_adder = true; + } + + // If its a timer source, it needs to be re-armed + if (type->ke.filter == DISPATCH_EVFILT_TIMER) { + ds->ds_needs_rearm = true; + } + + dispatch_assert(!(ds->ds_is_level && ds->ds_is_adder)); +#if DISPATCH_DEBUG + dispatch_debug(ds, __FUNCTION__); +#endif + + // Some sources require special processing + if (type == DISPATCH_SOURCE_TYPE_MACH_SEND) { + static dispatch_once_t pred; + dispatch_once_f(&pred, NULL, _dispatch_mach_notify_source_init); + } else if (type == DISPATCH_SOURCE_TYPE_TIMER) { + ds->ds_timer.flags = mask; + } + + _dispatch_retain(ds->do_targetq); + return ds; + +out_bad: + free(ds); + free(dk); + return NULL; +} + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +static void +_dispatch_source_set_event_handler2(void *context) +{ + struct Block_layout *bl = context; + + dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); + dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); + + if (ds->ds_handler_is_block && ds->ds_handler_ctxt) { + Block_release(ds->ds_handler_ctxt); + } + ds->ds_handler_func = bl ? (void *)bl->invoke : NULL; + ds->ds_handler_ctxt = bl; + ds->ds_handler_is_block = true; +} + +void +dispatch_source_set_event_handler(dispatch_source_t ds, dispatch_block_t handler) +{ + dispatch_assert(!ds->ds_is_legacy); + handler = _dispatch_Block_copy(handler); + dispatch_barrier_async_f((dispatch_queue_t)ds, + handler, _dispatch_source_set_event_handler2); +} + +static void +_dispatch_source_set_event_handler_f(void *context) +{ + dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); + dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); + + if (ds->ds_handler_is_block && ds->ds_handler_ctxt) { + Block_release(ds->ds_handler_ctxt); + } + ds->ds_handler_func = context; + ds->ds_handler_ctxt = ds->do_ctxt; + ds->ds_handler_is_block = false; +} + +void +dispatch_source_set_event_handler_f(dispatch_source_t ds, + dispatch_function_t handler) +{ + dispatch_assert(!ds->ds_is_legacy); + dispatch_barrier_async_f((dispatch_queue_t)ds, + handler, _dispatch_source_set_event_handler_f); +} + +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +static void +_dispatch_source_set_cancel_handler2(void *context) +{ + dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); + dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); + + if (ds->ds_cancel_is_block && ds->ds_cancel_handler) { + Block_release(ds->ds_cancel_handler); + } + ds->ds_cancel_handler = context; + ds->ds_cancel_is_block = true; +} + +void +dispatch_source_set_cancel_handler(dispatch_source_t ds, + dispatch_block_t handler) +{ + dispatch_assert(!ds->ds_is_legacy); + handler = _dispatch_Block_copy(handler); + dispatch_barrier_async_f((dispatch_queue_t)ds, + handler, _dispatch_source_set_cancel_handler2); +} + +static void +_dispatch_source_set_cancel_handler_f(void *context) +{ + dispatch_source_t ds = (dispatch_source_t)_dispatch_queue_get_current(); + dispatch_assert(ds->do_vtable == &_dispatch_source_kevent_vtable); + + if (ds->ds_cancel_is_block && ds->ds_cancel_handler) { + Block_release(ds->ds_cancel_handler); + } + ds->ds_cancel_handler = context; + ds->ds_cancel_is_block = false; +} + +void +dispatch_source_set_cancel_handler_f(dispatch_source_t ds, + dispatch_function_t handler) +{ + dispatch_assert(!ds->ds_is_legacy); + dispatch_barrier_async_f((dispatch_queue_t)ds, + handler, _dispatch_source_set_cancel_handler_f); +} + +#ifndef DISPATCH_NO_LEGACY +// 6618342 Contact the team that owns the Instrument DTrace probe before renaming this symbol +dispatch_source_t +_dispatch_source_create2(dispatch_source_t ds, + dispatch_source_attr_t attr, + void *context, + dispatch_source_handler_function_t handler) +{ + if (ds == NULL || handler == NULL) { + return NULL; + } + + ds->ds_is_legacy = true; + + ds->ds_handler_func = handler; + ds->ds_handler_ctxt = context; + + if (attr && attr != DISPATCH_SOURCE_CREATE_SUSPENDED) { + ds->dq_finalizer_ctxt = attr->finalizer_ctxt; + ds->dq_finalizer_func = (typeof(ds->dq_finalizer_func))attr->finalizer_func; + ds->do_ctxt = attr->context; + } +#ifdef __BLOCKS__ + if (ds->dq_finalizer_func == (void*)_dispatch_call_block_and_release2) { + ds->dq_finalizer_ctxt = Block_copy(ds->dq_finalizer_ctxt); + if (!ds->dq_finalizer_ctxt) { + goto out_bad; + } + } + if (handler == _dispatch_source_call_block) { + struct Block_layout *bl = ds->ds_handler_ctxt = Block_copy(context); + if (!ds->ds_handler_ctxt) { + if (ds->dq_finalizer_func == (void*)_dispatch_call_block_and_release2) { + Block_release(ds->dq_finalizer_ctxt); + } + goto out_bad; + } + ds->ds_handler_func = (void *)bl->invoke; + ds->ds_handler_is_block = true; + } + + // all legacy sources get a cancellation event on the normal event handler. + dispatch_source_handler_function_t func = ds->ds_handler_func; + dispatch_source_handler_t block = ds->ds_handler_ctxt; + void *ctxt = ds->ds_handler_ctxt; + bool handler_is_block = ds->ds_handler_is_block; + + ds->ds_cancel_is_block = true; + if (handler_is_block) { + ds->ds_cancel_handler = _dispatch_Block_copy(^{ + block(ds); + }); + } else { + ds->ds_cancel_handler = _dispatch_Block_copy(^{ + func(ctxt, ds); + }); + } +#endif + if (attr != DISPATCH_SOURCE_CREATE_SUSPENDED) { + dispatch_resume(ds); + } + + return ds; + +out_bad: + free(ds); + return NULL; +} + +long +dispatch_source_get_error(dispatch_source_t ds, long *err_out) +{ + // 6863892 don't report ECANCELED until kevent is unregistered + if ((ds->ds_atomic_flags & DSF_CANCELED) && !ds->ds_dkev) { + if (err_out) { + *err_out = ECANCELED; + } + return DISPATCH_ERROR_DOMAIN_POSIX; + } else { + return DISPATCH_ERROR_DOMAIN_NO_ERROR; + } +} +#endif /* DISPATCH_NO_LEGACY */ + +// Updates the ordered list of timers based on next fire date for changes to ds. +// Should only be called from the context of _dispatch_mgr_q. +void +_dispatch_timer_list_update(dispatch_source_t ds) +{ + dispatch_source_t dsi = NULL; + int idx; + + dispatch_assert(_dispatch_queue_get_current() == &_dispatch_mgr_q); + + // do not reschedule timers unregistered with _dispatch_kevent_release() + if (!ds->ds_dkev) { + return; + } + + // Ensure the source is on the global kevent lists before it is removed and + // readded below. + _dispatch_kevent_merge(ds); + + TAILQ_REMOVE(&ds->ds_dkev->dk_sources, ds, ds_list); + + // change the list if the clock type has changed + if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) { + idx = DISPATCH_TIMER_INDEX_WALL; + } else { + idx = DISPATCH_TIMER_INDEX_MACH; + } + ds->ds_dkev = &_dispatch_kevent_timer[idx]; + + if (ds->ds_timer.target) { + TAILQ_FOREACH(dsi, &ds->ds_dkev->dk_sources, ds_list) { + if (dsi->ds_timer.target == 0 || ds->ds_timer.target < dsi->ds_timer.target) { + break; + } + } + } + + if (dsi) { + TAILQ_INSERT_BEFORE(dsi, ds, ds_list); + } else { + TAILQ_INSERT_TAIL(&ds->ds_dkev->dk_sources, ds, ds_list); + } +} + +static void +_dispatch_run_timers2(unsigned int timer) +{ + dispatch_source_t ds; + uint64_t now, missed; + + if (timer == DISPATCH_TIMER_INDEX_MACH) { + now = mach_absolute_time(); + } else { + now = _dispatch_get_nanoseconds(); + } + + while ((ds = TAILQ_FIRST(&_dispatch_kevent_timer[timer].dk_sources))) { + // We may find timers on the wrong list due to a pending update from + // dispatch_source_set_timer. Force an update of the list in that case. + if (timer != ds->ds_ident_hack) { + _dispatch_timer_list_update(ds); + continue; + } + if (!ds->ds_timer.target) { + // no configured timers on the list + break; + } + if (ds->ds_timer.target > now) { + // Done running timers for now. + break; + } + + if (ds->ds_timer.flags & (DISPATCH_TIMER_ONESHOT|DISPATCH_TIMER_ABSOLUTE)) { + dispatch_atomic_inc(&ds->ds_pending_data); + ds->ds_timer.target = 0; + } else { + // Calculate number of missed intervals. + missed = (now - ds->ds_timer.target) / ds->ds_timer.interval; + dispatch_atomic_add(&ds->ds_pending_data, missed + 1); + ds->ds_timer.target += (missed + 1) * ds->ds_timer.interval; + } + + _dispatch_timer_list_update(ds); + _dispatch_wakeup(ds); + } +} + +void +_dispatch_run_timers(void) +{ + unsigned int i; + for (i = 0; i < DISPATCH_TIMER_COUNT; i++) { + _dispatch_run_timers2(i); + } +} + +#if defined(__i386__) || defined(__x86_64__) +// these architectures always return mach_absolute_time() in nanoseconds +#define _dispatch_convert_mach2nano(x) (x) +#define _dispatch_convert_nano2mach(x) (x) +#else +static mach_timebase_info_data_t tbi; +static dispatch_once_t tbi_pred; + +static void +_dispatch_convert_init(void *context __attribute__((unused))) +{ + dispatch_assume_zero(mach_timebase_info(&tbi)); +} + +static uint64_t +_dispatch_convert_mach2nano(uint64_t val) +{ +#ifdef __LP64__ + __uint128_t tmp; +#else + long double tmp; +#endif + + dispatch_once_f(&tbi_pred, NULL, _dispatch_convert_init); + + tmp = val; + tmp *= tbi.numer; + tmp /= tbi.denom; + + return tmp; +} + +static uint64_t +_dispatch_convert_nano2mach(uint64_t val) +{ +#ifdef __LP64__ + __uint128_t tmp; +#else + long double tmp; +#endif + + dispatch_once_f(&tbi_pred, NULL, _dispatch_convert_init); + + tmp = val; + tmp *= tbi.denom; + tmp /= tbi.numer; + + return tmp; +} +#endif + +// approx 1 year (60s * 60m * 24h * 365d) +#define FOREVER_SEC 3153600l +#define FOREVER_NSEC 31536000000000000ull + +struct timespec * +_dispatch_get_next_timer_fire(struct timespec *howsoon) +{ + // + // kevent(2) does not allow large timeouts, so we use a long timeout + // instead (approximately 1 year). + dispatch_source_t ds = NULL; + unsigned int timer; + uint64_t now, delta_tmp, delta = UINT64_MAX; + + // We are looking for the first unsuspended timer which has its target + // time set. Given timers are kept in order, if we hit an timer that's + // unset there's no point in continuing down the list. + for (timer = 0; timer < DISPATCH_TIMER_COUNT; timer++) { + TAILQ_FOREACH(ds, &_dispatch_kevent_timer[timer].dk_sources, ds_list) { + if (!ds->ds_timer.target) { + break; + } + if (DISPATCH_OBJECT_SUSPENDED(ds)) { + ds->ds_is_armed = false; + } else { + break; + } + } + + if (!ds || !ds->ds_timer.target) { + continue; + } + + if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) { + now = _dispatch_get_nanoseconds(); + } else { + now = mach_absolute_time(); + } + if (ds->ds_timer.target <= now) { + howsoon->tv_sec = 0; + howsoon->tv_nsec = 0; + return howsoon; + } + + // the subtraction cannot go negative because the previous "if" + // verified that the target is greater than now. + delta_tmp = ds->ds_timer.target - now; + if (!(ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK)) { + delta_tmp = _dispatch_convert_mach2nano(delta_tmp); + } + if (delta_tmp < delta) { + delta = delta_tmp; + } + } + if (slowpath(delta > FOREVER_NSEC)) { + return NULL; + } else { + howsoon->tv_sec = (time_t)(delta / NSEC_PER_SEC); + howsoon->tv_nsec = (long)(delta % NSEC_PER_SEC); + } + return howsoon; +} + +struct dispatch_set_timer_params { + dispatch_source_t ds; + uintptr_t ident; + struct dispatch_timer_source_s values; +}; + +// To be called from the context of the _dispatch_mgr_q +static void +_dispatch_source_set_timer2(void *context) +{ + struct dispatch_set_timer_params *params = context; + dispatch_source_t ds = params->ds; + ds->ds_ident_hack = params->ident; + ds->ds_timer = params->values; + _dispatch_timer_list_update(ds); + dispatch_resume(ds); + dispatch_release(ds); + free(params); +} + +void +dispatch_source_set_timer(dispatch_source_t ds, + dispatch_time_t start, + uint64_t interval, + uint64_t leeway) +{ + struct dispatch_set_timer_params *params; + + // we use zero internally to mean disabled + if (interval == 0) { + interval = 1; + } else if ((int64_t)interval < 0) { + // 6866347 - make sure nanoseconds won't overflow + interval = INT64_MAX; + } + + // Suspend the source so that it doesn't fire with pending changes + // The use of suspend/resume requires the external retain/release + dispatch_retain(ds); + dispatch_suspend(ds); + + if (start == DISPATCH_TIME_NOW) { + start = mach_absolute_time(); + } else if (start == DISPATCH_TIME_FOREVER) { + start = INT64_MAX; + } + + while (!(params = malloc(sizeof(struct dispatch_set_timer_params)))) { + sleep(1); + } + + params->ds = ds; + params->values.flags = ds->ds_timer.flags; + + if ((int64_t)start < 0) { + // wall clock + params->ident = DISPATCH_TIMER_INDEX_WALL; + params->values.start = -((int64_t)start); + params->values.target = -((int64_t)start); + params->values.interval = interval; + params->values.leeway = leeway; + params->values.flags |= DISPATCH_TIMER_WALL_CLOCK; + } else { + // mach clock + params->ident = DISPATCH_TIMER_INDEX_MACH; + params->values.start = start; + params->values.target = start; + params->values.interval = _dispatch_convert_nano2mach(interval); + params->values.leeway = _dispatch_convert_nano2mach(leeway); + params->values.flags &= ~DISPATCH_TIMER_WALL_CLOCK; + } + + dispatch_barrier_async_f(&_dispatch_mgr_q, params, _dispatch_source_set_timer2); +} + +#ifndef DISPATCH_NO_LEGACY +// LEGACY +long +dispatch_source_timer_set_time(dispatch_source_t ds, uint64_t nanoseconds, uint64_t leeway) +{ + dispatch_time_t start; + if (nanoseconds == 0) { + nanoseconds = 1; + } + if (ds->ds_timer.flags == (DISPATCH_TIMER_ABSOLUTE|DISPATCH_TIMER_WALL_CLOCK)) { + static const struct timespec t0; + start = dispatch_walltime(&t0, nanoseconds); + } else if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) { + start = dispatch_walltime(DISPATCH_TIME_NOW, nanoseconds); + } else { + start = dispatch_time(DISPATCH_TIME_NOW, nanoseconds); + } + if (ds->ds_timer.flags & (DISPATCH_TIMER_ABSOLUTE|DISPATCH_TIMER_ONESHOT)) { + // 6866347 - make sure nanoseconds won't overflow + nanoseconds = INT64_MAX; // non-repeating (~292 years) + } + dispatch_source_set_timer(ds, start, nanoseconds, leeway); + return 0; +} + +// LEGACY +uint64_t +dispatch_event_get_nanoseconds(dispatch_source_t ds) +{ + if (ds->ds_timer.flags & DISPATCH_TIMER_WALL_CLOCK) { + return ds->ds_timer.interval; + } else { + return _dispatch_convert_mach2nano(ds->ds_timer.interval); + } +} +#endif /* DISPATCH_NO_LEGACY */ + +static dispatch_source_t _dispatch_mach_notify_source; +static mach_port_t _dispatch_port_set; +static mach_port_t _dispatch_event_port; + +#define _DISPATCH_IS_POWER_OF_TWO(v) (!(v & (v - 1)) && v) +#define _DISPATCH_HASH(x, y) (_DISPATCH_IS_POWER_OF_TWO(y) ? (MACH_PORT_INDEX(x) & ((y) - 1)) : (MACH_PORT_INDEX(x) % (y))) + +#define _DISPATCH_MACHPORT_HASH_SIZE 32 +#define _DISPATCH_MACHPORT_HASH(x) _DISPATCH_HASH((x), _DISPATCH_MACHPORT_HASH_SIZE) + +static void _dispatch_port_set_init(void *); +static mach_port_t _dispatch_get_port_set(void); + +void +_dispatch_drain_mach_messages(struct kevent *ke) +{ + dispatch_source_t dsi; + dispatch_kevent_t dk; + struct kevent ke2; + + if (!dispatch_assume(ke->data)) { + return; + } + dk = _dispatch_kevent_find(ke->data, EVFILT_MACHPORT); + if (!dispatch_assume(dk)) { + return; + } + _dispatch_kevent_machport_disable(dk); // emulate EV_DISPATCH + + EV_SET(&ke2, ke->data, EVFILT_MACHPORT, EV_ADD|EV_ENABLE|EV_DISPATCH, DISPATCH_MACHPORT_RECV, 0, dk); + + TAILQ_FOREACH(dsi, &dk->dk_sources, ds_list) { + _dispatch_source_merge_kevent(dsi, &ke2); + } +} + +void +_dispatch_port_set_init(void *context __attribute__((unused))) +{ + struct kevent kev = { + .filter = EVFILT_MACHPORT, + .flags = EV_ADD, + }; + kern_return_t kr; + + kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &_dispatch_port_set); + DISPATCH_VERIFY_MIG(kr); + dispatch_assume_zero(kr); + kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &_dispatch_event_port); + DISPATCH_VERIFY_MIG(kr); + dispatch_assume_zero(kr); + kr = mach_port_move_member(mach_task_self(), _dispatch_event_port, _dispatch_port_set); + DISPATCH_VERIFY_MIG(kr); + dispatch_assume_zero(kr); + + kev.ident = _dispatch_port_set; + + _dispatch_update_kq(&kev); +} + +mach_port_t +_dispatch_get_port_set(void) +{ + static dispatch_once_t pred; + + dispatch_once_f(&pred, NULL, _dispatch_port_set_init); + + return _dispatch_port_set; +} + +void +_dispatch_kevent_machport_resume(dispatch_kevent_t dk, uint32_t new_flags, uint32_t del_flags) +{ + mach_port_t previous, port = (mach_port_t)dk->dk_kevent.ident; + kern_return_t kr; + + if ((new_flags & DISPATCH_MACHPORT_RECV) || (!new_flags && !del_flags && dk->dk_kevent.fflags & DISPATCH_MACHPORT_RECV)) { + _dispatch_kevent_machport_enable(dk); + } + if (new_flags & DISPATCH_MACHPORT_DEAD) { + kr = mach_port_request_notification(mach_task_self(), port, MACH_NOTIFY_DEAD_NAME, 1, + _dispatch_event_port, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); + DISPATCH_VERIFY_MIG(kr); + + + switch(kr) { + case KERN_INVALID_NAME: + case KERN_INVALID_RIGHT: + // Supress errors + break; + default: + // Else, we dont expect any errors from mach. Log any errors if we do + if (dispatch_assume_zero(kr)) { + // log the error + } else if (dispatch_assume_zero(previous)) { + // Another subsystem has beat libdispatch to requesting the Mach + // dead-name notification on this port. We should technically cache the + // previous port and message it when the kernel messages our port. Or + // we can just say screw those subsystems and drop the previous port. + // They should adopt libdispatch :-P + kr = mach_port_deallocate(mach_task_self(), previous); + DISPATCH_VERIFY_MIG(kr); + dispatch_assume_zero(kr); + } + } + } + + if (del_flags & DISPATCH_MACHPORT_RECV) { + _dispatch_kevent_machport_disable(dk); + } + if (del_flags & DISPATCH_MACHPORT_DEAD) { + kr = mach_port_request_notification(mach_task_self(), (mach_port_t)dk->dk_kevent.ident, + MACH_NOTIFY_DEAD_NAME, 1, MACH_PORT_NULL, MACH_MSG_TYPE_MAKE_SEND_ONCE, &previous); + DISPATCH_VERIFY_MIG(kr); + + switch (kr) { + case KERN_INVALID_NAME: + case KERN_INVALID_RIGHT: + case KERN_INVALID_ARGUMENT: + break; + default: + if (dispatch_assume_zero(kr)) { + // log the error + } else if (previous) { + // the kernel has not consumed the right yet + dispatch_assume_zero(_dispatch_send_consume_send_once_right(previous)); + } + } + } +} + +void +_dispatch_kevent_machport_enable(dispatch_kevent_t dk) +{ + mach_port_t mp = (mach_port_t)dk->dk_kevent.ident; + kern_return_t kr; + + kr = mach_port_move_member(mach_task_self(), mp, _dispatch_get_port_set()); + DISPATCH_VERIFY_MIG(kr); + switch (kr) { + case KERN_INVALID_NAME: +#if DISPATCH_DEBUG + _dispatch_log("Corruption: Mach receive right 0x%x destroyed prematurely", mp); +#endif + break; + default: + dispatch_assume_zero(kr); + } +} + +void +_dispatch_kevent_machport_disable(dispatch_kevent_t dk) +{ + mach_port_t mp = (mach_port_t)dk->dk_kevent.ident; + kern_return_t kr; + + kr = mach_port_move_member(mach_task_self(), mp, 0); + DISPATCH_VERIFY_MIG(kr); + switch (kr) { + case KERN_INVALID_RIGHT: + case KERN_INVALID_NAME: +#if DISPATCH_DEBUG + _dispatch_log("Corruption: Mach receive right 0x%x destroyed prematurely", mp); +#endif + break; + case 0: + break; + default: + dispatch_assume_zero(kr); + break; + } +} + +#define _DISPATCH_MIN_MSG_SZ (8ul * 1024ul - MAX_TRAILER_SIZE) +#ifndef DISPATCH_NO_LEGACY +dispatch_source_t +dispatch_source_mig_create(mach_port_t mport, size_t max_msg_size, dispatch_source_attr_t attr, + dispatch_queue_t dq, dispatch_mig_callback_t mig_callback) +{ + if (max_msg_size < _DISPATCH_MIN_MSG_SZ) { + max_msg_size = _DISPATCH_MIN_MSG_SZ; + } + return dispatch_source_machport_create(mport, DISPATCH_MACHPORT_RECV, attr, dq, + ^(dispatch_source_t ds) { + if (!dispatch_source_get_error(ds, NULL)) { + if (dq->dq_width != 1) { + dispatch_retain(ds); // this is a shim -- use the external retain + dispatch_async(dq, ^{ + dispatch_mig_server(ds, max_msg_size, mig_callback); + dispatch_release(ds); // this is a shim -- use the external release + }); + } else { + dispatch_mig_server(ds, max_msg_size, mig_callback); + } + } + }); +} +#endif /* DISPATCH_NO_LEGACY */ + +static void +_dispatch_mach_notify_source_init(void *context __attribute__((unused))) +{ + size_t maxsz = sizeof(union __RequestUnion___dispatch_send_libdispatch_internal_protocol_subsystem); + + if (sizeof(union __ReplyUnion___dispatch_libdispatch_internal_protocol_subsystem) > maxsz) { + maxsz = sizeof(union __ReplyUnion___dispatch_libdispatch_internal_protocol_subsystem); + } + + _dispatch_get_port_set(); + + _dispatch_mach_notify_source = dispatch_source_mig_create(_dispatch_event_port, + maxsz, NULL, &_dispatch_mgr_q, libdispatch_internal_protocol_server); + + dispatch_assert(_dispatch_mach_notify_source); +} + +kern_return_t +_dispatch_mach_notify_port_deleted(mach_port_t notify __attribute__((unused)), mach_port_name_t name) +{ + dispatch_source_t dsi; + dispatch_kevent_t dk; + struct kevent kev; + +#if DISPATCH_DEBUG + _dispatch_log("Corruption: Mach send/send-once/dead-name right 0x%x deleted prematurely", name); +#endif + + dk = _dispatch_kevent_find(name, EVFILT_MACHPORT); + if (!dk) { + goto out; + } + + EV_SET(&kev, name, EVFILT_MACHPORT, EV_ADD|EV_ENABLE|EV_DISPATCH|EV_EOF, DISPATCH_MACHPORT_DELETED, 0, dk); + + TAILQ_FOREACH(dsi, &dk->dk_sources, ds_list) { + _dispatch_source_merge_kevent(dsi, &kev); + // this can never happen again + // this must happen after the merge + // this may be racy in the future, but we don't provide a 'setter' API for the mask yet + dsi->ds_pending_data_mask &= ~DISPATCH_MACHPORT_DELETED; + } + + // no more sources have this flag + dk->dk_kevent.fflags &= ~DISPATCH_MACHPORT_DELETED; + +out: + return KERN_SUCCESS; +} + +kern_return_t +_dispatch_mach_notify_port_destroyed(mach_port_t notify __attribute__((unused)), mach_port_t name) +{ + kern_return_t kr; + // this function should never be called + dispatch_assume_zero(name); + kr = mach_port_mod_refs(mach_task_self(), name, MACH_PORT_RIGHT_RECEIVE, -1); + DISPATCH_VERIFY_MIG(kr); + dispatch_assume_zero(kr); + return KERN_SUCCESS; +} + +kern_return_t +_dispatch_mach_notify_no_senders(mach_port_t notify, mach_port_mscount_t mscnt __attribute__((unused))) +{ + // this function should never be called + dispatch_assume_zero(notify); + return KERN_SUCCESS; +} + +kern_return_t +_dispatch_mach_notify_send_once(mach_port_t notify __attribute__((unused))) +{ + // we only register for dead-name notifications + // some code deallocated our send-once right without consuming it +#if DISPATCH_DEBUG + _dispatch_log("Corruption: An app/library deleted a libdispatch dead-name notification"); +#endif + return KERN_SUCCESS; +} + +kern_return_t +_dispatch_mach_notify_dead_name(mach_port_t notify __attribute__((unused)), mach_port_name_t name) +{ + dispatch_source_t dsi; + dispatch_kevent_t dk; + struct kevent kev; + kern_return_t kr; + + dk = _dispatch_kevent_find(name, EVFILT_MACHPORT); + if (!dk) { + goto out; + } + + EV_SET(&kev, name, EVFILT_MACHPORT, EV_ADD|EV_ENABLE|EV_DISPATCH|EV_EOF, DISPATCH_MACHPORT_DEAD, 0, dk); + + TAILQ_FOREACH(dsi, &dk->dk_sources, ds_list) { + _dispatch_source_merge_kevent(dsi, &kev); + // this can never happen again + // this must happen after the merge + // this may be racy in the future, but we don't provide a 'setter' API for the mask yet + dsi->ds_pending_data_mask &= ~DISPATCH_MACHPORT_DEAD; + } + + // no more sources have this flag + dk->dk_kevent.fflags &= ~DISPATCH_MACHPORT_DEAD; + +out: + // the act of receiving a dead name notification allocates a dead-name right that must be deallocated + kr = mach_port_deallocate(mach_task_self(), name); + DISPATCH_VERIFY_MIG(kr); + //dispatch_assume_zero(kr); + + return KERN_SUCCESS; +} + +kern_return_t +_dispatch_wakeup_main_thread(mach_port_t mp __attribute__((unused))) +{ + // dummy function just to pop out the main thread out of mach_msg() + return 0; +} + +kern_return_t +_dispatch_consume_send_once_right(mach_port_t mp __attribute__((unused))) +{ + // dummy function to consume a send-once right + return 0; +} + +mach_msg_return_t +dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, dispatch_mig_callback_t callback) +{ + mach_msg_options_t options = MACH_RCV_MSG | MACH_RCV_TIMEOUT + | MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_CTX) + | MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0); + mach_msg_options_t tmp_options = options; + mig_reply_error_t *bufTemp, *bufRequest, *bufReply; + mach_msg_return_t kr = 0; + unsigned int cnt = 1000; // do not stall out serial queues + int demux_success; + + maxmsgsz += MAX_TRAILER_SIZE; + + // XXX FIXME -- allocate these elsewhere + bufRequest = alloca(maxmsgsz); + bufReply = alloca(maxmsgsz); + bufReply->Head.msgh_size = 0; // make CLANG happy + + // XXX FIXME -- change this to not starve out the target queue + for (;;) { + if (DISPATCH_OBJECT_SUSPENDED(ds) || (--cnt == 0)) { + options &= ~MACH_RCV_MSG; + tmp_options &= ~MACH_RCV_MSG; + + if (!(tmp_options & MACH_SEND_MSG)) { + break; + } + } + + kr = mach_msg(&bufReply->Head, tmp_options, bufReply->Head.msgh_size, + (mach_msg_size_t)maxmsgsz, (mach_port_t)ds->ds_ident_hack, 0, 0); + + tmp_options = options; + + if (slowpath(kr)) { + switch (kr) { + case MACH_SEND_INVALID_DEST: + case MACH_SEND_TIMED_OUT: + if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) { + mach_msg_destroy(&bufReply->Head); + } + break; + case MACH_RCV_TIMED_OUT: + case MACH_RCV_INVALID_NAME: + break; + default: + dispatch_assume_zero(kr); + break; + } + break; + } + + if (!(tmp_options & MACH_RCV_MSG)) { + break; + } + + bufTemp = bufRequest; + bufRequest = bufReply; + bufReply = bufTemp; + + demux_success = callback(&bufRequest->Head, &bufReply->Head); + + if (!demux_success) { + // destroy the request - but not the reply port + bufRequest->Head.msgh_remote_port = 0; + mach_msg_destroy(&bufRequest->Head); + } else if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX)) { + // if MACH_MSGH_BITS_COMPLEX is _not_ set, then bufReply->RetCode is present + if (slowpath(bufReply->RetCode)) { + if (bufReply->RetCode == MIG_NO_REPLY) { + continue; + } + + // destroy the request - but not the reply port + bufRequest->Head.msgh_remote_port = 0; + mach_msg_destroy(&bufRequest->Head); + } + } + + if (bufReply->Head.msgh_remote_port) { + tmp_options |= MACH_SEND_MSG; + if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE) { + tmp_options |= MACH_SEND_TIMEOUT; + } + } + } + + return kr; +} diff --git a/src/source.h b/src/source.h new file mode 100644 index 0000000..867ba86 --- /dev/null +++ b/src/source.h @@ -0,0 +1,583 @@ +/* + * 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@ + */ + +#ifndef __DISPATCH_SOURCE__ +#define __DISPATCH_SOURCE__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +#include +#include +#include + +/*! + * @header + * The dispatch framework provides a suite of interfaces for monitoring low- + * level system objects (file descriptors, Mach ports, signals, VFS nodes, etc.) + * for activity and automatically submitting event handler blocks to dispatch + * queues when such activity occurs. + * + * This suite of interfaces is known as the Dispatch Source API. + */ + +/*! + * @typedef dispatch_source_t + * + * @abstract + * Dispatch sources are used to automatically submit event handler blocks to + * dispatch queues in response to external events. + */ +DISPATCH_DECL(dispatch_source); + +/*! + * @typedef dispatch_source_type_t + * + * @abstract + * Constants of this type represent the class of low-level system object that + * is being monitored by the dispatch source. Constants of this type are + * passed as a parameter to dispatch_source_create() and determine how the + * handle argument is interpreted (i.e. as a file descriptor, mach port, + * signal number, process identifer, etc.), and how the mask arugment is + * interpreted. + */ +typedef const struct dispatch_source_type_s *dispatch_source_type_t; + +/*! + * @const DISPATCH_SOURCE_TYPE_DATA_ADD + * @discussion A dispatch source that coalesces data obtained via calls to + * dispatch_source_merge_data(). An ADD is used to coalesce the data. + * The handle is unused (pass zero for now). + * The mask is unused (pass zero for now). + */ +#define DISPATCH_SOURCE_TYPE_DATA_ADD (&_dispatch_source_type_data_add) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_data_add; + +/*! + * @const DISPATCH_SOURCE_TYPE_DATA_OR + * @discussion A dispatch source that coalesces data obtained via calls to + * dispatch_source_merge_data(). A logical OR is used to coalesce the data. + * The handle is unused (pass zero for now). + * The mask is used to perform a logical AND with the value passed to + * dispatch_source_merge_data(). + */ +#define DISPATCH_SOURCE_TYPE_DATA_OR (&_dispatch_source_type_data_or) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_data_or; + +/*! + * @const DISPATCH_SOURCE_TYPE_MACH_SEND + * @discussion A dispatch source that monitors a Mach port for dead name + * notifications (send right no longer has any corresponding receive right). + * The handle is a Mach port with a send or send-once right (mach_port_t). + * The mask is a mask of desired events from dispatch_source_mach_send_flags_t. + */ +#define DISPATCH_SOURCE_TYPE_MACH_SEND (&_dispatch_source_type_mach_send) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_mach_send; + +/*! + * @const DISPATCH_SOURCE_TYPE_MACH_RECV + * @discussion A dispatch source that monitors a Mach port for pending messages. + * The handle is a Mach port with a receive right (mach_port_t). + * The mask is unused (pass zero for now). + */ +#define DISPATCH_SOURCE_TYPE_MACH_RECV (&_dispatch_source_type_mach_recv) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_mach_recv; + +/*! + * @const DISPATCH_SOURCE_TYPE_PROC + * @discussion A dispatch source that monitors an external process for events + * defined by dispatch_source_proc_flags_t. + * The handle is a process identifier (pid_t). + * The mask is a mask of desired events from dispatch_source_proc_flags_t. + */ +#define DISPATCH_SOURCE_TYPE_PROC (&_dispatch_source_type_proc) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_proc; + +/*! + * @const DISPATCH_SOURCE_TYPE_READ + * @discussion A dispatch source that monitors a file descriptor for pending + * bytes available to be read. + * The handle is a file descriptor (int). + * The mask is unused (pass zero for now). + */ +#define DISPATCH_SOURCE_TYPE_READ (&_dispatch_source_type_read) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_read; + +/*! + * @const DISPATCH_SOURCE_TYPE_SIGNAL + * @discussion A dispatch source that monitors the current process for signals. + * The handle is a signal number (int). + * The mask is unused (pass zero for now). + */ +#define DISPATCH_SOURCE_TYPE_SIGNAL (&_dispatch_source_type_signal) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_signal; + +/*! + * @const DISPATCH_SOURCE_TYPE_TIMER + * @discussion A dispatch source that submits the event handler block based + * on a timer. + * The handle is unused (pass zero for now). + * The mask is unused (pass zero for now). + */ +#define DISPATCH_SOURCE_TYPE_TIMER (&_dispatch_source_type_timer) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_timer; + +/*! + * @const DISPATCH_SOURCE_TYPE_VNODE + * @discussion A dispatch source that monitors a file descriptor for events + * defined by dispatch_source_vnode_flags_t. + * The handle is a file descriptor (int). + * The mask is a mask of desired events from dispatch_source_vnode_flags_t. + */ +#define DISPATCH_SOURCE_TYPE_VNODE (&_dispatch_source_type_vnode) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_vnode; + +/*! + * @const DISPATCH_SOURCE_TYPE_WRITE + * @discussion A dispatch source that monitors a file descriptor for available + * buffer space to write bytes. + * The handle is a file descriptor (int). + * The mask is unused (pass zero for now). + */ +#define DISPATCH_SOURCE_TYPE_WRITE (&_dispatch_source_type_write) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_write; + +/*! + * @enum dispatch_source_mach_send_flags_t + * + * @constant DISPATCH_MACH_SEND_DEAD + * The receive right corresponding to the given send right was destroyed. + */ +enum { + DISPATCH_MACH_SEND_DEAD = 0x1, +}; + +/*! + * @enum dispatch_source_proc_flags_t + * + * @constant DISPATCH_PROC_EXIT + * The process has exited (perhaps cleanly, perhaps not). + * + * @constant DISPATCH_PROC_FORK + * The process has created one or more child processes. + * + * @constant DISPATCH_PROC_EXEC + * The process has become another executable image via + * exec*() or posix_spawn*(). + * + * @constant DISPATCH_PROC_SIGNAL + * A Unix signal was delivered to the process. + */ +enum { + DISPATCH_PROC_EXIT = 0x80000000, + DISPATCH_PROC_FORK = 0x40000000, + DISPATCH_PROC_EXEC = 0x20000000, + DISPATCH_PROC_SIGNAL = 0x08000000, +}; + +/*! + * @enum dispatch_source_vnode_flags_t + * + * @constant DISPATCH_VNODE_DELETE + * The filesystem object was deleted from the namespace. + * + * @constant DISPATCH_VNODE_WRITE + * The filesystem object data changed. + * + * @constant DISPATCH_VNODE_EXTEND + * The filesystem object changed in size. + * + * @constant DISPATCH_VNODE_ATTRIB + * The filesystem object metadata changed. + * + * @constant DISPATCH_VNODE_LINK + * The filesystem object link count changed. + * + * @constant DISPATCH_VNODE_RENAME + * The filesystem object was renamed in the namespace. + * + * @constant DISPATCH_VNODE_REVOKE + * The filesystem object was revoked. + */ +enum { + DISPATCH_VNODE_DELETE = 0x1, + DISPATCH_VNODE_WRITE = 0x2, + DISPATCH_VNODE_EXTEND = 0x4, + DISPATCH_VNODE_ATTRIB = 0x8, + DISPATCH_VNODE_LINK = 0x10, + DISPATCH_VNODE_RENAME = 0x20, + DISPATCH_VNODE_REVOKE = 0x40, +}; + +__BEGIN_DECLS + +/*! + * @function dispatch_source_create + * + * @abstract + * Creates a new dispatch source to monitor low-level system objects and auto- + * matically submit a handler block to a dispatch queue in response to events. + * + * @discussion + * Dispatch sources are not reentrant. Any events received while the dispatch + * source is suspended or while the event handler block is currently executing + * will be coalesced and delivered after the dispatch source is resumed or the + * event handler block has returned. + * + * Dispatch sources are created in a suspended state. After creating the + * source and setting any desired attributes (i.e. the handler, context, etc.), + * a call must be made to dispatch_resume() in order to begin event delivery. + * + * @param type + * Declares the type of the dispatch source. Must be one of the defined + * dispatch_source_type_t constants. + * @param handle + * The underlying system handle to monitor. The interpretation of this argument + * is determined by the constant provided in the type parameter. + * @param mask + * A mask of flags specifying which events are desired. The interpretation of + * this argument is determined by the constant provided in the type parameter. + * @param queue + * The dispatch queue to which the event handler block will be submited. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_MALLOC DISPATCH_NOTHROW +dispatch_source_t +dispatch_source_create(dispatch_source_type_t type, + uintptr_t handle, + unsigned long mask, + dispatch_queue_t queue); + +/*! + * @function dispatch_source_set_event_handler + * + * @abstract + * Sets the event handler block for the given dispatch source. + * + * @param source + * The dispatch source to modify. + * The result of passing NULL in this parameter is undefined. + * + * @param handler + * The event handler block to submit to the source's target queue. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_source_set_event_handler(dispatch_source_t source, + dispatch_block_t handler); +#endif /* __BLOCKS__ */ + +/*! + * @function dispatch_source_set_event_handler_f + * + * @abstract + * Sets the event handler function for the given dispatch source. + * + * @param source + * The dispatch source to modify. + * The result of passing NULL in this parameter is undefined. + * + * @param handler + * The event handler function to submit to the source's target queue. + * The context parameter passed to the event handler function is the current + * context of the dispatch source at the time the handler call is made. + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_source_set_event_handler_f(dispatch_source_t source, + dispatch_function_t handler); + +/*! + * @function dispatch_source_set_cancel_handler + * + * @abstract + * Sets the cancellation handler block for the given dispatch source. + * + * @discussion + * The cancellation handler (if specified) will be submitted to the source's + * target queue in response to a call to dispatch_source_cancel() once the + * system has released all references to the source's underlying handle and + * the source's event handler block has returned. + * + * IMPORTANT: + * A cancellation handler is required for file descriptor and mach port based + * sources in order to safely close the descriptor or destroy the port. Closing + * the descriptor or port before the cancellation handler may result in a race + * condition. If a new descriptor is allocated with the same value as the + * recently closed descriptor while the source's event handler is still running, + * the event handler may read/write data to the wrong descriptor. + * + * @param source + * The dispatch source to modify. + * The result of passing NULL in this parameter is undefined. + * + * @param handler + * The cancellation handler block to submit to the source's target queue. + */ +#ifdef __BLOCKS__ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_source_set_cancel_handler(dispatch_source_t source, + dispatch_block_t cancel_handler); +#endif /* __BLOCKS__ */ + +/*! + * @function dispatch_source_set_cancel_handler_f + * + * @abstract + * Sets the cancellation handler function for the given dispatch source. + * + * @discussion + * See dispatch_source_set_cancel_handler() for more details. + * + * @param source + * The dispatch source to modify. + * The result of passing NULL in this parameter is undefined. + * + * @param handler + * The cancellation handler function to submit to the source's target queue. + * The context parameter passed to the event handler function is the current + * context of the dispatch source at the time the handler call is made. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL1 DISPATCH_NOTHROW +void +dispatch_source_set_cancel_handler_f(dispatch_source_t source, + dispatch_function_t cancel_handler); + +/*! + * @function dispatch_source_cancel + * + * @abstract + * Asynchronously cancel the dispatch source, preventing any further invocation + * of its event handler block. + * + * @discussion + * Cancellation prevents any further invocation of the event handler block for + * the specified dispatch source, but does not interrupt an event handler + * block that is already in progress. + * + * The cancellation handler is submitted to the source's target queue once the + * the source's event handler has finished, indicating it is now safe to close + * the source's handle (i.e. file descriptor or mach port). + * + * See dispatch_source_set_cancel_handler() for more information. + * + * @param source + * The dispatch source to be canceled. + * The result of passing NULL in this parameter is undefined. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_source_cancel(dispatch_source_t source); + +/*! + * @function dispatch_source_testcancel + * + * @abstract + * Tests whether the given dispatch source has been canceled. + * + * @param source + * The dispatch source to be tested. + * The result of passing NULL in this parameter is undefined. + * + * @result + * Non-zero if canceled and zero if not canceled. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +long +dispatch_source_testcancel(dispatch_source_t source); + +/*! + * @function dispatch_source_get_handle + * + * @abstract + * Returns the underlying system handle associated with this dispatch source. + * + * @param source + * The result of passing NULL in this parameter is undefined. + * + * @result + * The return value should be interpreted according to the type of the dispatch + * source, and may be one of the following handles: + * + * DISPATCH_SOURCE_TYPE_DATA_ADD: n/a + * DISPATCH_SOURCE_TYPE_DATA_OR: n/a + * DISPATCH_SOURCE_TYPE_MACH_SEND: mach port (mach_port_t) + * DISPATCH_SOURCE_TYPE_MACH_RECV: mach port (mach_port_t) + * DISPATCH_SOURCE_TYPE_PROC: process identifier (pid_t) + * DISPATCH_SOURCE_TYPE_READ: file descriptor (int) + * DISPATCH_SOURCE_TYPE_SIGNAL: signal number (int) + * DISPATCH_SOURCE_TYPE_TIMER: n/a + * DISPATCH_SOURCE_TYPE_VNODE: file descriptor (int) + * DISPATCH_SOURCE_TYPE_WRITE: file descriptor (int) + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_PURE DISPATCH_NOTHROW +uintptr_t +dispatch_source_get_handle(dispatch_source_t source); + +/*! + * @function dispatch_source_get_mask + * + * @abstract + * Returns the mask of events monitored by the dispatch source. + * + * @param source + * The result of passing NULL in this parameter is undefined. + * + * @result + * The return value should be interpreted according to the type of the dispatch + * source, and may be one of the following flag sets: + * + * DISPATCH_SOURCE_TYPE_DATA_ADD: n/a + * DISPATCH_SOURCE_TYPE_DATA_OR: n/a + * DISPATCH_SOURCE_TYPE_MACH_SEND: dispatch_source_mach_send_flags_t + * DISPATCH_SOURCE_TYPE_MACH_RECV: n/a + * DISPATCH_SOURCE_TYPE_PROC: dispatch_source_proc_flags_t + * DISPATCH_SOURCE_TYPE_READ: n/a + * DISPATCH_SOURCE_TYPE_SIGNAL: n/a + * DISPATCH_SOURCE_TYPE_TIMER: n/a + * DISPATCH_SOURCE_TYPE_VNODE: dispatch_source_vnode_flags_t + * DISPATCH_SOURCE_TYPE_WRITE: n/a + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_PURE DISPATCH_NOTHROW +unsigned long +dispatch_source_get_mask(dispatch_source_t source); + +/*! + * @function dispatch_source_get_data + * + * @abstract + * Returns pending data for the dispatch source. + * + * @discussion + * This function is intended to be called from within the event handler block. + * The result of calling this function outside of the event handler callback is + * undefined. + * + * @param source + * The result of passing NULL in this parameter is undefined. + * + * @result + * The return value should be interpreted according to the type of the dispatch + * source, and may be one of the following: + * + * DISPATCH_SOURCE_TYPE_DATA_ADD: application defined data + * DISPATCH_SOURCE_TYPE_DATA_OR: application defined data + * DISPATCH_SOURCE_TYPE_MACH_SEND: dispatch_source_mach_send_flags_t + * DISPATCH_SOURCE_TYPE_MACH_RECV: n/a + * DISPATCH_SOURCE_TYPE_PROC: dispatch_source_proc_flags_t + * DISPATCH_SOURCE_TYPE_READ: estimated bytes available to read + * DISPATCH_SOURCE_TYPE_SIGNAL: number of signals delivered since + * the last handler invocation + * DISPATCH_SOURCE_TYPE_TIMER: number of times the timer has fired + * since the last handler invocation + * DISPATCH_SOURCE_TYPE_VNODE: dispatch_source_vnode_flags_t + * DISPATCH_SOURCE_TYPE_WRITE: estimated buffer space available + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_WARN_RESULT DISPATCH_PURE DISPATCH_NOTHROW +unsigned long +dispatch_source_get_data(dispatch_source_t source); + +/*! + * @function dispatch_source_merge_data + * + * @abstract + * Merges data into a dispatch source of type DISPATCH_SOURCE_TYPE_DATA_ADD or + * DISPATCH_SOURCE_TYPE_DATA_OR and submits its event handler block to its + * target queue. + * + * @param source + * The result of passing NULL in this parameter is undefined. + * + * @param value + * The value to coalesce with the pending data using a logical OR or an ADD + * as specified by the dispatch source type. A value of zero has no effect + * and will not result in the submission of the event handler block. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_source_merge_data(dispatch_source_t source, unsigned long value); + +/*! + * @function dispatch_source_set_timer + * + * @abstract + * Sets a start time, interval, and leeway value for a timer source. + * + * @discussion + * Calling this function has no effect if the timer source has already been + * canceled. + * + * The start time argument also determines which clock will be used for the + * timer. If the start time is DISPATCH_TIME_NOW or created with + * dispatch_time() then the timer is based on mach_absolute_time(). Otherwise, + * if the start time of the timer is created with dispatch_walltime() then the + * timer is based on gettimeofday(3). + * + * @param start + * The start time of the timer. See dispatch_time() and dispatch_walltime() + * for more information. + * + * @param interval + * The nanosecond interval for the timer. + * + * @param leeway + * A hint given to the system by the application for the amount of leeway, in + * nanoseconds, that the system may defer the timer in order to align with other + * system activity for improved system performance or power consumption. (For + * example, an application might perform a periodic task every 5 minutes, with + * a leeway of up to 30 seconds.) Note that some latency is to be expected for + * all timers even when a leeway value of zero is specified. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +void +dispatch_source_set_timer(dispatch_source_t source, + dispatch_time_t start, + uint64_t interval, + uint64_t leeway); + +__END_DECLS + +#endif diff --git a/src/source_internal.h b/src/source_internal.h new file mode 100644 index 0000000..e7126db --- /dev/null +++ b/src/source_internal.h @@ -0,0 +1,102 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_SOURCE_INTERNAL__ +#define __DISPATCH_SOURCE_INTERNAL__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +struct dispatch_source_vtable_s { + DISPATCH_VTABLE_HEADER(dispatch_source_s); +}; + +extern const struct dispatch_source_vtable_s _dispatch_source_kevent_vtable; + +struct dispatch_kevent_s { + TAILQ_ENTRY(dispatch_kevent_s) dk_list; + TAILQ_HEAD(, dispatch_source_s) dk_sources; + struct kevent dk_kevent; +}; + +typedef struct dispatch_kevent_s *dispatch_kevent_t; + +struct dispatch_timer_source_s { + uint64_t target; + uint64_t start; + uint64_t interval; + uint64_t leeway; + uint64_t flags; // dispatch_timer_flags_t +}; + +#define DSF_CANCELED 1u // cancellation has been requested + +struct dispatch_source_s { + DISPATCH_STRUCT_HEADER(dispatch_source_s, dispatch_source_vtable_s); + DISPATCH_QUEUE_HEADER; + // Instruments always copies DISPATCH_QUEUE_MIN_LABEL_SIZE, which is 64, + // so the remainder of the structure must be big enough + union { + char _ds_pad[DISPATCH_QUEUE_MIN_LABEL_SIZE]; + struct { + char dq_label[8]; + dispatch_kevent_t ds_dkev; + + dispatch_source_handler_function_t ds_handler_func; + void *ds_handler_ctxt; + + void *ds_cancel_handler; + + unsigned int ds_is_level:1, + ds_is_adder:1, + ds_is_installed:1, + ds_needs_rearm:1, + ds_is_armed:1, + ds_is_legacy:1, + ds_cancel_is_block:1, + ds_handler_is_block:1; + + unsigned int ds_atomic_flags; + + unsigned long ds_data; + unsigned long ds_pending_data; + unsigned long ds_pending_data_mask; + + TAILQ_ENTRY(dispatch_source_s) ds_list; + + unsigned long ds_ident_hack; + + struct dispatch_timer_source_s ds_timer; + }; + }; +}; + + +void _dispatch_source_legacy_xref_release(dispatch_source_t ds); + +#endif /* __DISPATCH_SOURCE_INTERNAL__ */ diff --git a/src/source_private.h b/src/source_private.h new file mode 100644 index 0000000..9e45cc1 --- /dev/null +++ b/src/source_private.h @@ -0,0 +1,129 @@ +/* + * 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@ + */ + +/* + * IMPORTANT: This header file describes INTERNAL interfaces to libdispatch + * which are subject to change in future releases of Mac OS X. Any applications + * relying on these interfaces WILL break. + */ + +#ifndef __DISPATCH_SOURCE_PRIVATE__ +#define __DISPATCH_SOURCE_PRIVATE__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +/*! + * @const DISPATCH_SOURCE_TYPE_VFS + * @discussion Apple-internal dispatch source that monitors for vfs events + * defined by dispatch_vfs_flags_t. + * The handle is a process identifier (pid_t). + */ +#define DISPATCH_SOURCE_TYPE_VFS (&_dispatch_source_type_vfs) +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +extern const struct dispatch_source_type_s _dispatch_source_type_vfs; + +/*! + * @enum dispatch_source_vfs_flags_t + * + * @constant DISPATCH_VFS_NOTRESP + * Server down. + * + * @constant DISPATCH_VFS_NEEDAUTH + * Server bad auth. + * + * @constant DISPATCH_VFS_LOWDISK + * We're low on space. + * + * @constant DISPATCH_VFS_MOUNT + * New filesystem arrived. + * + * @constant DISPATCH_VFS_UNMOUNT + * Filesystem has left. + * + * @constant DISPATCH_VFS_DEAD + * Filesystem is dead, needs force unmount. + * + * @constant DISPATCH_VFS_ASSIST + * Filesystem needs assistance from external program. + * + * @constant DISPATCH_VFS_NOTRESPLOCK + * Server lockd down. + * + * @constant DISPATCH_VFS_UPDATE + * Filesystem information has changed. + * + * @constant DISPATCH_VFS_VERYLOWDISK + * File system has *very* little disk space left. + */ +enum { + DISPATCH_VFS_NOTRESP = 0x0001, + DISPATCH_VFS_NEEDAUTH = 0x0002, + DISPATCH_VFS_LOWDISK = 0x0004, + DISPATCH_VFS_MOUNT = 0x0008, + DISPATCH_VFS_UNMOUNT = 0x0010, + DISPATCH_VFS_DEAD = 0x0020, + DISPATCH_VFS_ASSIST = 0x0040, + DISPATCH_VFS_NOTRESPLOCK = 0x0080, + DISPATCH_VFS_UPDATE = 0x0100, + DISPATCH_VFS_VERYLOWDISK = 0x0200, +}; + +/*! + * @enum dispatch_source_mach_send_flags_t + * + * @constant DISPATCH_MACH_SEND_DELETED + * The receive right corresponding to the given send right was destroyed. + */ +enum { + DISPATCH_MACH_SEND_DELETED = 0x2, +}; + +/*! + * @enum dispatch_source_proc_flags_t + * + * @constant DISPATCH_PROC_REAP + * The process has been reaped by the parent process via + * wait*(). + */ +enum { + DISPATCH_PROC_REAP = 0x10000000, +}; + +__BEGIN_DECLS + +/*! + * @typedef dispatch_mig_callback_t + * + * @abstract + * The signature of a function that handles Mach message delivery and response. + */ +typedef boolean_t (*dispatch_mig_callback_t)(mach_msg_header_t *message, mach_msg_header_t *reply); + +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NONNULL_ALL DISPATCH_NOTHROW +mach_msg_return_t +dispatch_mig_server(dispatch_source_t ds, size_t maxmsgsz, dispatch_mig_callback_t callback); + +__END_DECLS + +#endif diff --git a/src/time.c b/src/time.c new file mode 100644 index 0000000..07506f2 --- /dev/null +++ b/src/time.c @@ -0,0 +1,183 @@ +/* + * 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@ + */ + +#include "internal.h" + +uint64_t +_dispatch_get_nanoseconds(void) +{ + struct timeval now; + int r = gettimeofday(&now, NULL); + dispatch_assert_zero(r); + dispatch_assert(sizeof(NSEC_PER_SEC) == 8); + dispatch_assert(sizeof(NSEC_PER_USEC) == 8); + return now.tv_sec * NSEC_PER_SEC + now.tv_usec * NSEC_PER_USEC; +} + +#if defined(__i386__) || defined(__x86_64__) +// x86 currently implements mach time in nanoseconds; this is NOT likely to change +#define _dispatch_time_mach2nano(x) (x) +#define _dispatch_time_nano2mach(x) (x) +#else +static struct _dispatch_host_time_data_s { + mach_timebase_info_data_t tbi; + uint64_t safe_numer_math; + dispatch_once_t pred; +} _dispatch_host_time_data; + +static void +_dispatch_get_host_time_init(void *context __attribute__((unused))) +{ + dispatch_assume_zero(mach_timebase_info(&_dispatch_host_time_data.tbi)); + _dispatch_host_time_data.safe_numer_math = DISPATCH_TIME_FOREVER / _dispatch_host_time_data.tbi.numer; +} + +static uint64_t +_dispatch_time_mach2nano(uint64_t nsec) +{ + struct _dispatch_host_time_data_s *const data = &_dispatch_host_time_data; + uint64_t small_tmp = nsec; +#ifdef __LP64__ + __uint128_t big_tmp = nsec; +#else + long double big_tmp = nsec; +#endif + + dispatch_once_f(&data->pred, NULL, _dispatch_get_host_time_init); + + if (slowpath(data->tbi.numer != data->tbi.denom)) { + if (nsec < data->safe_numer_math) { + small_tmp *= data->tbi.numer; + small_tmp /= data->tbi.denom; + } else { + big_tmp *= data->tbi.numer; + big_tmp /= data->tbi.denom; + small_tmp = big_tmp; + } + } + return small_tmp; +} + +static int64_t +_dispatch_time_nano2mach(int64_t nsec) +{ + struct _dispatch_host_time_data_s *const data = &_dispatch_host_time_data; +#ifdef __LP64__ + __int128_t big_tmp = nsec; +#else + long double big_tmp = nsec; +#endif + + dispatch_once_f(&data->pred, NULL, _dispatch_get_host_time_init); + + if (fastpath(data->tbi.numer == data->tbi.denom)) { + return nsec; + } + + // Multiply by the inverse to convert nsec to Mach absolute time + big_tmp *= data->tbi.denom; + big_tmp /= data->tbi.numer; + + if (big_tmp > INT64_MAX) { + return INT64_MAX; + } + if (big_tmp < INT64_MIN) { + return INT64_MIN; + } + return big_tmp; +} +#endif + +dispatch_time_t +dispatch_time(dispatch_time_t inval, int64_t delta) +{ + if (inval == DISPATCH_TIME_FOREVER) { + return DISPATCH_TIME_FOREVER; + } + if ((int64_t)inval < 0) { + // wall clock + if (delta >= 0) { + if ((int64_t)(inval -= delta) >= 0) { + return DISPATCH_TIME_FOREVER; // overflow + } + return inval; + } + if ((int64_t)(inval -= delta) >= -1) { + // -1 is special == DISPATCH_TIME_FOREVER == forever + return -2; // underflow + } + return inval; + } + // mach clock + delta = _dispatch_time_nano2mach(delta); + if (inval == 0) { + inval = mach_absolute_time(); + } + if (delta >= 0) { + if ((int64_t)(inval += delta) <= 0) { + return DISPATCH_TIME_FOREVER; // overflow + } + return inval; + } + if ((int64_t)(inval += delta) < 1) { + return 1; // underflow + } + return inval; +} + +dispatch_time_t +dispatch_walltime(const struct timespec *inval, int64_t delta) +{ + int64_t nsec; + + if (inval) { + nsec = inval->tv_sec * 1000000000ull + inval->tv_nsec; + } else { + nsec = _dispatch_get_nanoseconds(); + } + + nsec += delta; + if (nsec <= 1) { + // -1 is special == DISPATCH_TIME_FOREVER == forever + return delta >= 0 ? DISPATCH_TIME_FOREVER : (uint64_t)-2ll; + } + + return -nsec; +} + +uint64_t +_dispatch_timeout(dispatch_time_t when) +{ + uint64_t now; + + if (when == DISPATCH_TIME_FOREVER) { + return DISPATCH_TIME_FOREVER; + } + if (when == 0) { + return 0; + } + if ((int64_t)when < 0) { + when = -(int64_t)when; + now = _dispatch_get_nanoseconds(); + return now >= when ? 0 : when - now; + } + now = mach_absolute_time(); + return now >= when ? 0 : _dispatch_time_mach2nano(when - now); +} diff --git a/src/time.h b/src/time.h new file mode 100644 index 0000000..510d6d7 --- /dev/null +++ b/src/time.h @@ -0,0 +1,113 @@ +/* + * 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@ + */ + +#ifndef __DISPATCH_TIME__ +#define __DISPATCH_TIME__ + +#ifndef __DISPATCH_INDIRECT__ +#error "Please #include instead of this file directly." +#include // for HeaderDoc +#endif + +#include + +__BEGIN_DECLS + +struct timespec; + +// 6368156 +#ifdef NSEC_PER_SEC +#undef NSEC_PER_SEC +#endif +#ifdef USEC_PER_SEC +#undef USEC_PER_SEC +#endif +#ifdef NSEC_PER_USEC +#undef NSEC_PER_USEC +#endif +#define NSEC_PER_SEC 1000000000ull +#define USEC_PER_SEC 1000000ull +#define NSEC_PER_USEC 1000ull + +/*! + * @typedef dispatch_time_t + * + * @abstract + * An somewhat abstract representation of time; where zero means "now" and + * DISPATCH_TIME_FOREVER means "infinity" and every value in between is an + * opaque encoding. + */ +typedef uint64_t dispatch_time_t; + +#define DISPATCH_TIME_NOW 0 +#define DISPATCH_TIME_FOREVER (~0ull) + +/*! + * @function dispatch_time + * + * @abstract + * Create dispatch_time_t relative to the default clock or modify an existing + * dispatch_time_t. + * + * @discussion + * On Mac OS X the default clock is based on mach_absolute_time(). + * + * @param when + * An optional dispatch_time_t to add nanoseconds to. If zero is passed, then + * dispatch_time() will use the result of mach_absolute_time(). + * + * @param delta + * Nanoseconds to add. + * + * @result + * A new dispatch_time_t. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NOTHROW +dispatch_time_t +dispatch_time(dispatch_time_t when, int64_t delta); + +/*! + * @function dispatch_walltime + * + * @abstract + * Create a dispatch_time_t using the wall clock. + * + * @discussion + * On Mac OS X the wall clock is based on gettimeofday(3). + * + * @param when + * A struct timespect to add time to. If NULL is passed, then + * dispatch_walltime() will use the result of gettimeofday(3). + * + * @param delta + * Nanoseconds to add. + * + * @result + * A new dispatch_time_t. + */ +__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_NA) +DISPATCH_NOTHROW +dispatch_time_t +dispatch_walltime(const struct timespec *when, int64_t delta); + +__END_DECLS + +#endif -- 2.45.2