├── LICENSE ├── README.md ├── examples ├── e_client.nim ├── e_client_iocp.nim ├── e_server.nim ├── test.c └── test.nim ├── src ├── xio.nim └── xio │ ├── beast_ref.nim │ ├── client.c │ ├── dispatch_ref.nim │ ├── fun.nim │ ├── iokit.nim │ ├── linux │ ├── inotify.nim │ ├── io_uring.nim │ └── test.h │ ├── macos │ └── coreservices │ │ └── coreservices.nim │ ├── notes.md │ ├── play.nim │ ├── server.c │ └── windows │ ├── base │ ├── basetsd.nim │ ├── bsdtypes.nim │ ├── commdlg.nim │ ├── crt │ │ └── rt.nim │ ├── errhandleapi.nim │ ├── fileapi.nim │ ├── handleapi.nim │ ├── inaddr.nim │ ├── ioapiset.nim │ ├── minwinbase.nim │ ├── minwindef.nim │ ├── ntdef.nim │ ├── qos.nim │ ├── sockettypes.nim │ ├── test.c │ ├── test.nim │ ├── widestr2.nim │ ├── winbase.nim │ ├── windef.nim │ ├── winnt.nim │ ├── winuser.nim │ ├── ws2def.nim │ ├── ws2tcpip.nim │ └── ws2types.nim │ ├── guiddef.nim │ ├── iocp.nim │ ├── sysinfoapi.nim │ ├── winsock2.nim │ └── wsadata.nim ├── tests ├── config.nims ├── t_socket.nim └── xio.nim └── xio.nimble /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Xio 2 | Cross platform system API for os and net. 3 | -------------------------------------------------------------------------------- /examples/e_client.nim: -------------------------------------------------------------------------------- 1 | import ../src/xio/windows/[wsadata, winsock2] 2 | import ../src/xio/windows/base/[ws2tcpip, ws2def, sockettypes] 3 | 4 | # proc getAddrInfo*( 5 | # pNodeName: PCSTR, 6 | # pServiceName: PCSTR, 7 | # pHints: ptr AddrInfoA, 8 | # ppResult: var PAddrInfoA 9 | # ): cint {.libWs2_32, importc: "getaddrinfo"} 10 | 11 | block: 12 | # echo inet_addr("127.0.0.1") 13 | # echo inet_addr("1.0.0.127") 14 | # echo htons(0x1) 15 | # echo htons(256) 16 | # echo htonl(1) 17 | 18 | proc main = 19 | var wsa: WSAData 20 | 21 | if WSAStartup(0x0101, addr wsa) != 0: 22 | doAssert false 23 | 24 | let 25 | address = "127.0.0.1" 26 | port = "5000" 27 | 28 | var hints: AddrInfoA 29 | var result: PAddrInfoA 30 | 31 | zeroMem(addr hints, sizeof(hints)) 32 | 33 | hints.aiFamily = AF_INET 34 | hints.aiSocktype = 1 35 | hints.aiProtocol = 6 36 | 37 | echo getAddrInfo(address, port, addr hints, addr result) 38 | echo result.repr 39 | 40 | var connectSocket = socket(result.aiFamily, result.aiSocktype, 41 | result.aiProtocol) 42 | echo connect(connectSocket, result.aiAddr, cast[cint](result.aiAddrlen)) 43 | 44 | var s = "This is a test!\n" 45 | echo send(connectSocket, s, s.len.cint, 0) 46 | let ires = shutdown(connectSocket, SD_SEND) 47 | if ires == SOCKET_ERROR: 48 | echo "shutdown failed: ", WSAGetLastError() 49 | discard closeSocket(connectSocket) 50 | discard WSACleanup() 51 | return 52 | 53 | var recvBuf = newString(16) 54 | 55 | let res = recv(connectSocket, recvBuf, recvBuf.len.cint, 0) 56 | echo res 57 | echo recvBuf 58 | echo "done" 59 | 60 | 61 | freeAddrInfo(result) 62 | discard closeSocket(connectSocket) 63 | discard WSACleanup() 64 | 65 | main() 66 | -------------------------------------------------------------------------------- /examples/e_client_iocp.nim: -------------------------------------------------------------------------------- 1 | import ../src/xio/windows/[iocp, wsadata, winsock2] 2 | import ../src/xio/windows/base/[ws2tcpip, ws2def, basetsd] 3 | 4 | import os 5 | 6 | # proc getAddrInfo*( 7 | # pNodeName: PCSTR, 8 | # pServiceName: PCSTR, 9 | # pHints: ptr AddrInfoA, 10 | # ppResult: var PAddrInfoA 11 | # ): cint {.libWs2_32, importc: "getaddrinfo"} 12 | 13 | block: 14 | # echo inet_addr("127.0.0.1") 15 | # echo inet_addr("1.0.0.127") 16 | # echo htons(0x1) 17 | # echo htons(256) 18 | # echo htonl(1) 19 | 20 | proc main = 21 | var queue = createIoCompletionPort(INVALID_HANDLE_VALUE, cast[Handle](0), 0, 1) 22 | 23 | if queue == nil: 24 | raiseOSError(osLastError()) 25 | 26 | var wsa: WSAData 27 | 28 | if WSAStartup(0x0101, addr wsa) != 0: 29 | doAssert false 30 | 31 | let 32 | address = "127.0.0.1" 33 | port = "5000" 34 | 35 | var hints: AddrInfoA 36 | var result: PAddrInfoA 37 | 38 | zeroMem(addr hints, sizeof(hints)) 39 | 40 | hints.aiFamily = AF_INET 41 | hints.aiSocktype = 1 42 | hints.aiProtocol = 6 43 | 44 | echo getAddrInfo(cstring(address), cstring(port), addr hints, addr result) 45 | echo result.repr 46 | 47 | var connectSocket = socket(result.aiFamily, result.aiSocktype, 48 | result.aiProtocol) 49 | echo connect(connectSocket, result.aiAddr, cast[cint](result.aiAddrlen)) 50 | 51 | # var s = "This is a test!\n" 52 | # echo send(connectSocket, s, s.len.cint, 0) 53 | # let ires = shutdown(connectSocket, SD_SEND) 54 | # if ires == SOCKET_ERROR: 55 | # echo "shutdown failed: ", WSAGetLastError() 56 | # discard closeSocket(connectSocket) 57 | # discard WSACleanup() 58 | # return 59 | 60 | var recvBuf = newString(16) 61 | 62 | var buffer: array[200, char] 63 | var b = WSABUF(len: 200, buf: cast[cstring](buffer.addr)) 64 | 65 | var nums: culong 66 | var over: WSAOVERLAPPED 67 | 68 | 69 | # LPWSABUF* = ptr WSABUF 70 | 71 | 72 | # lpFlags: LPDWORD, 73 | # lpOverlapped: LPWSAOVERLAPPED, 74 | # lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 75 | 76 | var flag = cast[DWORD](0) 77 | 78 | let res = WSARecv(connectSocket, addr b, 1, nums, flag, addr over, nil) 79 | 80 | discard createIoCompletionPort(over.hevent, queue, 0, 1) 81 | 82 | # var data: DWORD 83 | # var key: ULONG_PTR 84 | 85 | # while true: 86 | # completionPort: Handle, 87 | # lpNumberOfBytesTransferred: var DWORD, 88 | # lpCompletionKey: var ULONG_PTR, 89 | # lpOverlapped: var OVERLAPPED, 90 | # dwMilliseconds: DWORD 91 | 92 | # getQueuedCompletionStatus(queue, data, key, over, 10) 93 | 94 | # let res = recv(connectSocket, recvBuf, recvBuf.len.cint, 0) 95 | 96 | # echo WSAGetLastError() 97 | 98 | echo res 99 | echo recvBuf 100 | echo "done" 101 | 102 | 103 | freeAddrInfo(result) 104 | discard closeSocket(connectSocket) 105 | # discard WSACleanup() 106 | 107 | main() 108 | -------------------------------------------------------------------------------- /examples/e_server.nim: -------------------------------------------------------------------------------- 1 | import ../src/xio/windows/[wsadata, winsock2] 2 | import ../src/xio/windows/base/[ws2types, ws2tcpip, ws2def] 3 | 4 | block: 5 | proc main = 6 | var wsa: WSAData 7 | 8 | if WSAStartup(0x0101, addr wsa) != 0: 9 | doAssert false 10 | 11 | var 12 | address = "127.0.0.1" 13 | port = "5000" 14 | 15 | var hints: AddrInfoA 16 | var result: PAddrInfoA 17 | 18 | zeroMem(addr hints, sizeof(hints)) 19 | 20 | hints.aiFamily = AF_INET 21 | hints.aiSocktype = 1 22 | hints.aiProtocol = 6 23 | 24 | echo getAddrInfo(cstring(address), cstring(port), addr hints, addr result) 25 | 26 | var connectSocket = socket(result.aiFamily, result.aiSocktype, 27 | result.aiProtocol) 28 | discard bindAddr(connectSocket, result.aiAddr, cast[cint](result.aiAddrLen)) 29 | discard listen(connectSocket, SOMAXCONN) 30 | 31 | 32 | while true: 33 | var address: SockAddr 34 | var addressLen = sizeof(address).cint 35 | var client = accept(connectSocket, addr address, addressLen) 36 | var text = "This is a test!\n" 37 | doAssert client.send(cstring(text), text.len.cint, 0) == text.len 38 | 39 | freeAddrInfo(result) 40 | 41 | 42 | main() 43 | 44 | # import net 45 | 46 | 47 | # var socket = newSocket() 48 | # socket.bindAddr(Port(5000)) 49 | # socket.listen() 50 | 51 | # var client: Socket 52 | # var address = "" 53 | # while true: 54 | # socket.acceptAddr(client, address) 55 | # echo("Client connected from: ", address) 56 | # echo(client.recvLine) 57 | # client.send("from socket") 58 | -------------------------------------------------------------------------------- /examples/test.c: -------------------------------------------------------------------------------- 1 | #define IPPROTO_IP 0 2 | #define IPPROTO_HOPOPTS 0 3 | #define IPPROTO_ICMP 1 4 | #define IPPROTO_IGMP 2 5 | #define IPPROTO_GGP 3 6 | #define IPPROTO_IPV4 4 7 | #define IPPROTO_TCP 6 8 | #define IPPROTO_PUP 12 9 | #define IPPROTO_UDP 17 10 | #define IPPROTO_IDP 22 11 | #define IPPROTO_IPV6 41 12 | #define IPPROTO_ROUTING 43 13 | #define IPPROTO_FRAGMENT 44 14 | #define IPPROTO_ESP 50 15 | #define IPPROTO_AH 51 16 | #define IPPROTO_ICMPV6 58 17 | #define IPPROTO_NONE 59 18 | #define IPPROTO_DSTOPTS 60 19 | #define IPPROTO_ND 77 20 | #define IPPROTO_ICLFXBM 78 21 | 22 | #define IPPROTO_RAW 255 23 | #define IPPROTO_MAX 256 24 | 25 | #define IPPORT_ECHO 7 26 | #define IPPORT_DISCARD 9 27 | #define IPPORT_SYSTAT 11 28 | #define IPPORT_DAYTIME 13 29 | #define IPPORT_NETSTAT 15 30 | #define IPPORT_FTP 21 31 | #define IPPORT_TELNET 23 32 | #define IPPORT_SMTP 25 33 | #define IPPORT_TIMESERVER 37 34 | #define IPPORT_NAMESERVER 42 35 | #define IPPORT_WHOIS 43 36 | #define IPPORT_MTP 57 37 | 38 | #define IPPORT_TFTP 69 39 | #define IPPORT_RJE 77 40 | #define IPPORT_FINGER 79 41 | #define IPPORT_TTYLINK 87 42 | #define IPPORT_SUPDUP 95 43 | 44 | #define IPPORT_EXECSERVER 512 45 | #define IPPORT_LOGINSERVER 513 46 | #define IPPORT_CMDSERVER 514 47 | #define IPPORT_EFSSERVER 520 48 | 49 | #define IPPORT_BIFFUDP 512 50 | #define IPPORT_WHOSERVER 513 51 | #define IPPORT_ROUTESERVER 520 52 | 53 | #define IPPORT_RESERVED 1024 54 | 55 | #define IMPLINK_IP 155 56 | #define IMPLINK_LOWEXPER 156 57 | #define IMPLINK_HIGHEXPER 158 -------------------------------------------------------------------------------- /examples/test.nim: -------------------------------------------------------------------------------- 1 | const 2 | IPPROTO_IP* = 0 3 | IPPROTO_HOPOPTS* = 0 4 | IPPROTO_ICMP* = 1 5 | IPPROTO_IGMP* = 2 6 | IPPROTO_GGP* = 3 7 | IPPROTO_IPV4* = 4 8 | IPPROTO_TCP* = 6 9 | IPPROTO_PUP* = 12 10 | IPPROTO_UDP* = 17 11 | IPPROTO_IDP* = 22 12 | IPPROTO_IPV6* = 41 13 | IPPROTO_ROUTING* = 43 14 | IPPROTO_FRAGMENT* = 44 15 | IPPROTO_ESP* = 50 16 | IPPROTO_AH* = 51 17 | IPPROTO_ICMPV6* = 58 18 | IPPROTO_NONE* = 59 19 | IPPROTO_DSTOPTS* = 60 20 | IPPROTO_ND* = 77 21 | IPPROTO_ICLFXBM* = 78 22 | IPPROTO_RAW* = 255 23 | IPPROTO_MAX* = 256 24 | IPPORT_ECHO* = 7 25 | IPPORT_DISCARD* = 9 26 | IPPORT_SYSTAT* = 11 27 | IPPORT_DAYTIME* = 13 28 | IPPORT_NETSTAT* = 15 29 | IPPORT_FTP* = 21 30 | IPPORT_TELNET* = 23 31 | IPPORT_SMTP* = 25 32 | IPPORT_TIMESERVER* = 37 33 | IPPORT_NAMESERVER* = 42 34 | IPPORT_WHOIS* = 43 35 | IPPORT_MTP* = 57 36 | IPPORT_TFTP* = 69 37 | IPPORT_RJE* = 77 38 | IPPORT_FINGER* = 79 39 | IPPORT_TTYLINK* = 87 40 | IPPORT_SUPDUP* = 95 41 | IPPORT_EXECSERVER* = 512 42 | IPPORT_LOGINSERVER* = 513 43 | IPPORT_CMDSERVER* = 514 44 | IPPORT_EFSSERVER* = 520 45 | IPPORT_BIFFUDP* = 512 46 | IPPORT_WHOSERVER* = 513 47 | IPPORT_ROUTESERVER* = 520 48 | IPPORT_RESERVED* = 1024 49 | IMPLINK_IP* = 155 50 | IMPLINK_LOWEXPER* = 156 51 | IMPLINK_HIGHEXPER* = 158 52 | -------------------------------------------------------------------------------- /src/xio.nim: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ringabout/xio/73321c2c163e7eebbee1b5774ccde8a74ef6fdbf/src/xio.nim -------------------------------------------------------------------------------- /src/xio/beast_ref.nim: -------------------------------------------------------------------------------- 1 | import selectors, net, nativesockets, os, httpcore, asyncdispatch, strutils, posix 2 | import parseutils 3 | import options, future, logging 4 | 5 | from deques import len 6 | 7 | from osproc import countProcessors 8 | 9 | import times # TODO this shouldn't be required. Nim bug? 10 | 11 | export httpcore 12 | 13 | import httpbeast/parser 14 | 15 | type 16 | FdKind = enum 17 | Server, Client, Dispatcher 18 | 19 | Data = object 20 | fdKind: FdKind ## Determines the fd kind (server, client, dispatcher) 21 | ## - Client specific data. 22 | ## A queue of data that needs to be sent when the FD becomes writeable. 23 | sendQueue: string 24 | ## The number of characters in `sendQueue` that have been sent already. 25 | bytesSent: int 26 | ## Big chunk of data read from client during request. 27 | data: string 28 | ## Determines whether `data` contains "\c\l\c\l". 29 | headersFinished: bool 30 | ## Determines position of the end of "\c\l\c\l". 31 | headersFinishPos: int 32 | ## The address that a `client` connects from. 33 | ip: string 34 | 35 | type 36 | Request* = object 37 | selector: Selector[Data] 38 | client*: SocketHandle 39 | # Determines where in the data buffer this request starts. 40 | # Only used for HTTP pipelining. 41 | start: int 42 | 43 | OnRequest* = proc (req: Request): Future[void] {.gcsafe.} 44 | 45 | Settings* = object 46 | port*: Port 47 | bindAddr*: string 48 | numThreads: int 49 | 50 | const 51 | serverInfo = "HttpBeast" 52 | 53 | proc initSettings*(port: Port = Port(8080), 54 | bindAddr: string = "", 55 | numThreads: int = 0): Settings = 56 | Settings( 57 | port: port, 58 | bindAddr: bindAddr, 59 | numThreads: numThreads, 60 | ) 61 | 62 | proc initData(fdKind: FdKind, ip = ""): Data = 63 | Data(fdKind: fdKind, 64 | sendQueue: "", 65 | bytesSent: 0, 66 | data: "", 67 | headersFinished: false, 68 | headersFinishPos: -1, ## By default we assume the fast case: end of data. 69 | ip: ip 70 | ) 71 | 72 | template handleAccept() = 73 | let (client, address) = fd.SocketHandle.accept() 74 | if client == osInvalidSocket: 75 | let lastError = osLastError() 76 | 77 | if lastError.int32 == EMFILE: 78 | warn("Ignoring EMFILE error: ", osErrorMsg(lastError)) 79 | return 80 | 81 | raiseOSError(lastError) 82 | setBlocking(client, false) 83 | selector.registerHandle(client, {Event.Read}, 84 | initData(Client, ip=address)) 85 | 86 | template handleClientClosure(selector: Selector[Data], 87 | fd: SocketHandle|int, 88 | inLoop=true) = 89 | # TODO: Logging that the socket was closed. 90 | 91 | # TODO: Can POST body be sent with Connection: Close? 92 | 93 | selector.unregister(fd) 94 | fd.SocketHandle.close() 95 | when inLoop: 96 | break 97 | else: 98 | return 99 | 100 | proc onRequestFutureComplete(theFut: Future[void], 101 | selector: Selector[Data], fd: int) = 102 | if theFut.failed: 103 | raise theFut.error 104 | 105 | template fastHeadersCheck(data: ptr Data): untyped = 106 | (let res = data.data[^1] == '\l' and data.data[^2] == '\c' and 107 | data.data[^3] == '\l' and data.data[^4] == '\c'; 108 | if res: data.headersFinishPos = data.data.len; 109 | res) 110 | 111 | template methodNeedsBody(data: ptr Data): untyped = 112 | ( 113 | # Only idempotent methods can be pipelined (GET/HEAD/PUT/DELETE), they 114 | # never need a body, so we just assume `start` at 0. 115 | let m = parseHttpMethod(data.data, start=0); 116 | m.isSome() and m.get() in {HttpPost, HttpPut, HttpConnect, HttpPatch} 117 | ) 118 | 119 | proc slowHeadersCheck(data: ptr Data): bool = 120 | # TODO: See how this `unlikely` affects ASM. 121 | if unlikely(methodNeedsBody(data)): 122 | # Look for \c\l\c\l inside data. 123 | data.headersFinishPos = 0 124 | template ch(i): untyped = 125 | ( 126 | let pos = data.headersFinishPos+i; 127 | if pos >= data.data.len: '\0' else: data.data[pos] 128 | ) 129 | while data.headersFinishPos < data.data.len: 130 | case ch(0) 131 | of '\c': 132 | if ch(1) == '\l' and ch(2) == '\c' and ch(3) == '\l': 133 | data.headersFinishPos.inc(4) 134 | return true 135 | else: discard 136 | data.headersFinishPos.inc() 137 | 138 | data.headersFinishPos = -1 139 | 140 | proc bodyInTransit(data: ptr Data): bool = 141 | assert methodNeedsBody(data), "Calling bodyInTransit now is inefficient." 142 | assert data.headersFinished 143 | 144 | if data.headersFinishPos == -1: return false 145 | 146 | var trueLen = parseContentLength(data.data, start=0) 147 | 148 | let bodyLen = data.data.len - data.headersFinishPos 149 | assert(not (bodyLen > trueLen)) 150 | return bodyLen != trueLen 151 | 152 | proc validateRequest(req: Request): bool {.gcsafe.} 153 | proc processEvents(selector: Selector[Data], 154 | events: array[64, ReadyKey], count: int, 155 | onRequest: OnRequest) = 156 | for i in 0 ..< count: 157 | let fd = events[i].fd 158 | var data: ptr Data = addr(selector.getData(fd)) 159 | # Handle error events first. 160 | if Event.Error in events[i].events: 161 | if isDisconnectionError({SocketFlag.SafeDisconn}, 162 | events[i].errorCode): 163 | handleClientClosure(selector, fd) 164 | raiseOSError(events[i].errorCode) 165 | 166 | case data.fdKind 167 | of Server: 168 | if Event.Read in events[i].events: 169 | handleAccept() 170 | else: 171 | assert false, "Only Read events are expected for the server" 172 | of Dispatcher: 173 | # Run the dispatcher loop. 174 | assert events[i].events == {Event.Read} 175 | asyncdispatch.poll(0) 176 | of Client: 177 | if Event.Read in events[i].events: 178 | const size = 256 179 | var buf: array[size, char] 180 | # Read until EAGAIN. We take advantage of the fact that the client 181 | # will wait for a response after they send a request. So we can 182 | # comfortably continue reading until the message ends with \c\l 183 | # \c\l. 184 | while true: 185 | let ret = recv(fd.SocketHandle, addr buf[0], size, 0.cint) 186 | if ret == 0: 187 | handleClientClosure(selector, fd) 188 | 189 | if ret == -1: 190 | # Error! 191 | let lastError = osLastError() 192 | if lastError.int32 in {EWOULDBLOCK, EAGAIN}: 193 | break 194 | if isDisconnectionError({SocketFlag.SafeDisconn}, lastError): 195 | handleClientClosure(selector, fd) 196 | raiseOSError(lastError) 197 | 198 | # Write buffer to our data. 199 | let origLen = data.data.len 200 | data.data.setLen(origLen + ret) 201 | for i in 0 ..< ret: data.data[origLen+i] = buf[i] 202 | 203 | if fastHeadersCheck(data) or slowHeadersCheck(data): 204 | # First line and headers for request received. 205 | data.headersFinished = true 206 | when not defined(release): 207 | if data.sendQueue.len != 0: 208 | logging.warn("sendQueue isn't empty.") 209 | if data.bytesSent != 0: 210 | logging.warn("bytesSent isn't empty.") 211 | 212 | let waitingForBody = methodNeedsBody(data) and bodyInTransit(data) 213 | if likely(not waitingForBody): 214 | for start in parseRequests(data.data): 215 | # For pipelined requests, we need to reset this flag. 216 | data.headersFinished = true 217 | 218 | let request = Request( 219 | selector: selector, 220 | client: fd.SocketHandle, 221 | start: start 222 | ) 223 | 224 | template validateResponse(): untyped = 225 | data.headersFinished = false 226 | 227 | if validateRequest(request): 228 | let fut = onRequest(request) 229 | if not fut.isNil: 230 | fut.callback = 231 | proc (theFut: Future[void]) = 232 | onRequestFutureComplete(theFut, selector, fd) 233 | validateResponse() 234 | else: 235 | validateResponse() 236 | 237 | if ret != size: 238 | # Assume there is nothing else for us right now and break. 239 | break 240 | elif Event.Write in events[i].events: 241 | assert data.sendQueue.len > 0 242 | assert data.bytesSent < data.sendQueue.len 243 | # Write the sendQueue. 244 | let leftover = data.sendQueue.len-data.bytesSent 245 | let ret = send(fd.SocketHandle, addr data.sendQueue[data.bytesSent], 246 | leftover, 0) 247 | if ret == -1: 248 | # Error! 249 | let lastError = osLastError() 250 | if lastError.int32 in {EWOULDBLOCK, EAGAIN}: 251 | break 252 | if isDisconnectionError({SocketFlag.SafeDisconn}, lastError): 253 | handleClientClosure(selector, fd) 254 | raiseOSError(lastError) 255 | 256 | data.bytesSent.inc(ret) 257 | 258 | if data.sendQueue.len == data.bytesSent: 259 | data.bytesSent = 0 260 | data.sendQueue.setLen(0) 261 | data.data.setLen(0) 262 | selector.updateHandle(fd.SocketHandle, 263 | {Event.Read}) 264 | else: 265 | assert false 266 | 267 | var serverDate {.threadvar.}: string 268 | proc updateDate(fd: AsyncFD): bool = 269 | result = false # Returning true signifies we want timer to stop. 270 | serverDate = now().utc().format("ddd, dd MMM yyyy HH:mm:ss 'GMT'") 271 | 272 | proc eventLoop(params: (OnRequest, Settings)) = 273 | let (onRequest, settings) = params 274 | 275 | let selector = newSelector[Data]() 276 | 277 | let server = newSocket() 278 | server.setSockOpt(OptReuseAddr, true) 279 | server.setSockOpt(OptReusePort, true) 280 | server.bindAddr(settings.port, settings.bindAddr) 281 | server.listen() 282 | server.getFd().setBlocking(false) 283 | selector.registerHandle(server.getFd(), {Event.Read}, initData(Server)) 284 | 285 | let disp = getGlobalDispatcher() 286 | selector.registerHandle(getIoHandler(disp).getFd(), {Event.Read}, 287 | initData(Dispatcher)) 288 | 289 | # Set up timer to get current date/time. 290 | discard updateDate(0.AsyncFD) 291 | asyncdispatch.addTimer(1000, false, updateDate) 292 | 293 | var events: array[64, ReadyKey] 294 | while true: 295 | let ret = selector.selectInto(-1, events) 296 | processEvents(selector, events, ret, onRequest) 297 | 298 | # Ensure callbacks list doesn't grow forever in asyncdispatch. 299 | # See https://github.com/nim-lang/Nim/issues/7532. 300 | # Not processing callbacks can also lead to exceptions being silently 301 | # lost! 302 | if unlikely(asyncdispatch.getGlobalDispatcher().callbacks.len > 0): 303 | asyncdispatch.poll(0) 304 | 305 | #[ API start ]# 306 | 307 | proc unsafeSend*(req: Request, data: string) {.inline.} = 308 | ## Sends the specified data on the request socket. 309 | ## 310 | ## This function can be called as many times as necessary. 311 | ## 312 | ## It does not 313 | ## check whether the socket is in a state that can be written so be 314 | ## careful when using it. 315 | if req.client notin req.selector: 316 | return 317 | req.selector.getData(req.client).sendQueue.add(data) 318 | req.selector.updateHandle(req.client, {Event.Read, Event.Write}) 319 | 320 | proc send*(req: Request, code: HttpCode, body: string, headers="") = 321 | ## Responds with the specified HttpCode and body. 322 | ## 323 | ## **Warning:** This can only be called once in the OnRequest callback. 324 | 325 | if req.client notin req.selector: 326 | return 327 | 328 | # TODO: Reduce the amount of `getData` accesses. 329 | template getData: var Data = req.selector.getData(req.client) 330 | assert getData.headersFinished, "Selector not ready to send." 331 | 332 | let otherHeaders = if likely(headers.len == 0): "" else: "\c\L" & headers 333 | var 334 | text = ( 335 | "HTTP/1.1 $#\c\L" & 336 | "Content-Length: $#\c\LServer: $#\c\LDate: $#$#\c\L\c\L$#" 337 | ) % [$code, $body.len, serverInfo, serverDate, otherHeaders, body] 338 | 339 | getData.sendQueue.add(text) 340 | req.selector.updateHandle(req.client, {Event.Read, Event.Write}) 341 | 342 | proc send*(req: Request, code: HttpCode) = 343 | ## Responds with the specified HttpCode. The body of the response 344 | ## is the same as the HttpCode description. 345 | req.send(code, $code) 346 | 347 | proc send*(req: Request, body: string, code = Http200) {.inline.} = 348 | ## Sends a HTTP 200 OK response with the specified body. 349 | ## 350 | ## **Warning:** This can only be called once in the OnRequest callback. 351 | req.send(code, body) 352 | 353 | proc httpMethod*(req: Request): Option[HttpMethod] {.inline.} = 354 | ## Parses the request's data to find the request HttpMethod. 355 | parseHttpMethod(req.selector.getData(req.client).data, req.start) 356 | 357 | proc path*(req: Request): Option[string] {.inline.} = 358 | ## Parses the request's data to find the request target. 359 | if unlikely(req.client notin req.selector): return 360 | parsePath(req.selector.getData(req.client).data, req.start) 361 | 362 | proc headers*(req: Request): Option[HttpHeaders] = 363 | ## Parses the request's data to get the headers. 364 | if unlikely(req.client notin req.selector): return 365 | parseHeaders(req.selector.getData(req.client).data, req.start) 366 | 367 | proc body*(req: Request): Option[string] = 368 | ## Retrieves the body of the request. 369 | let pos = req.selector.getData(req.client).headersFinishPos 370 | if pos == -1: return none(string) 371 | result = req.selector.getData(req.client).data[ 372 | pos .. ^1 373 | ].some() 374 | 375 | when not defined(release): 376 | let length = 377 | if req.headers.get().hasKey("Content-Length"): 378 | req.headers.get()["Content-Length"].parseInt() 379 | else: 380 | 0 381 | assert result.get().len == length 382 | 383 | proc ip*(req: Request): string = 384 | ## Retrieves the IP address that the request was made from. 385 | req.selector.getData(req.client).ip 386 | 387 | proc forget*(req: Request) = 388 | ## Unregisters the underlying request's client socket from httpbeast's 389 | ## event loop. 390 | ## 391 | ## This is useful when you want to register ``req.client`` in your own 392 | ## event loop, for example when wanting to integrate httpbeast into a 393 | ## websocket library. 394 | req.selector.unregister(req.client) 395 | 396 | proc validateRequest(req: Request): bool = 397 | ## Handles protocol-mandated responses. 398 | ## 399 | ## Returns ``false`` when the request has been handled. 400 | result = true 401 | 402 | # From RFC7231: "When a request method is received 403 | # that is unrecognized or not implemented by an origin server, the 404 | # origin server SHOULD respond with the 501 (Not Implemented) status 405 | # code." 406 | if req.httpMethod().isNone(): 407 | req.send(Http501) 408 | return false 409 | 410 | proc run*(onRequest: OnRequest, settings: Settings) = 411 | ## Starts the HTTP server and calls `onRequest` for each request. 412 | ## 413 | ## The ``onRequest`` procedure returns a ``Future[void]`` type. But 414 | ## unlike most asynchronous procedures in Nim, it can return ``nil`` 415 | ## for better performance, when no async operations are needed. 416 | when compileOption("threads"): 417 | let numThreads = 418 | if settings.numThreads == 0: countProcessors() 419 | else: settings.numThreads 420 | else: 421 | let numThreads = 1 422 | 423 | echo("Starting ", numThreads, " threads") 424 | if numThreads > 1: 425 | when compileOption("threads"): 426 | var threads = newSeq[Thread[(OnRequest, Settings)]](numThreads) 427 | for i in 0 ..< numThreads: 428 | createThread[(OnRequest, Settings)]( 429 | threads[i], eventLoop, (onRequest, settings) 430 | ) 431 | echo("Listening on port ", settings.port) # This line is used in the tester to signal readiness. 432 | joinThreads(threads) 433 | else: 434 | assert false 435 | else: 436 | eventLoop((onRequest, settings)) 437 | 438 | proc run*(onRequest: OnRequest) {.inline.} = 439 | ## Starts the HTTP server with default settings. Calls `onRequest` for each 440 | ## request. 441 | ## 442 | ## See the other ``run`` proc for more info. 443 | run(onRequest, Settings(port: Port(8080), bindAddr: "")) 444 | 445 | when false: 446 | proc close*(port: Port) = 447 | ## Closes an httpbeast server that is running on the specified port. 448 | ## 449 | ## **NOTE:** This is not yet implemented. 450 | 451 | assert false 452 | # TODO: Figure out the best way to implement this. One way is to use async 453 | # events to signal our `eventLoop`. Maybe it would be better not to support 454 | # multiple servers running at the same time? -------------------------------------------------------------------------------- /src/xio/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #pragma comment(lib, "ws2_32.lib") 5 | 6 | typedef struct 7 | { 8 | WSAOVERLAPPED Overlapped; 9 | SOCKET Socket; 10 | WSABUF wsaBuf; 11 | char Buffer[1024]; 12 | DWORD Flags; 13 | } PER_IO_DATA, *LPPER_IO_DATA; 14 | 15 | static DWORD WINAPI ClientWorkerThread(LPVOID lpParameter) 16 | { 17 | HANDLE hCompletionPort = (HANDLE)lpParameter; 18 | DWORD NumBytesRecv = 0; 19 | ULONG CompletionKey; 20 | LPPER_IO_DATA PerIoData; 21 | 22 | while (GetQueuedCompletionStatus(hCompletionPort, &NumBytesRecv, &CompletionKey, (LPOVERLAPPED *)&PerIoData, INFINITE)) 23 | { 24 | if (!PerIoData) 25 | continue; 26 | 27 | if (NumBytesRecv == 0) 28 | { 29 | std::cout << "Server disconnected!\r\n\r\n"; 30 | } 31 | else 32 | { 33 | // use PerIoData->Buffer as needed... 34 | std::cout << std::string(PerIoData->Buffer, NumBytesRecv); 35 | 36 | PerIoData->wsaBuf.len = sizeof(PerIoData->Buffer); 37 | PerIoData->Flags = 0; 38 | 39 | if (WSARecv(PerIoData->Socket, &(PerIoData->wsaBuf), 1, &NumBytesRecv, &(PerIoData->Flags), &(PerIoData->Overlapped), NULL) == 0) 40 | continue; 41 | 42 | if (WSAGetLastError() == WSA_IO_PENDING) 43 | continue; 44 | } 45 | 46 | closesocket(PerIoData->Socket); 47 | delete PerIoData; 48 | } 49 | 50 | return 0; 51 | } 52 | 53 | int main(void) 54 | { 55 | WSADATA WsaDat; 56 | if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != 0) 57 | return 0; 58 | 59 | HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); 60 | if (!hCompletionPort) 61 | return 0; 62 | 63 | SYSTEM_INFO systemInfo; 64 | GetSystemInfo(&systemInfo); 65 | 66 | for (DWORD i = 0; i < systemInfo.dwNumberOfProcessors; ++i) 67 | { 68 | HANDLE hThread = CreateThread(NULL, 0, ClientWorkerThread, hCompletionPort, 0, NULL); 69 | CloseHandle(hThread); 70 | } 71 | 72 | SOCKET Socket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); 73 | if (Socket == INVALID_SOCKET) 74 | return 0; 75 | 76 | SOCKADDR_IN SockAddr; 77 | SockAddr.sin_family = AF_INET; 78 | SockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 79 | SockAddr.sin_port = htons(8888); 80 | 81 | CreateIoCompletionPort((HANDLE)Socket, hCompletionPort, 0, 0); 82 | 83 | if (WSAConnect(Socket, (SOCKADDR *)(&SockAddr), sizeof(SockAddr), NULL, NULL, NULL, NULL) == SOCKET_ERROR) 84 | return 0; 85 | 86 | PER_IO_DATA *pPerIoData = new PER_IO_DATA; 87 | ZeroMemory(pPerIoData, sizeof(PER_IO_DATA)); 88 | 89 | pPerIoData->Socket = Socket; 90 | pPerIoData->Overlapped.hEvent = WSACreateEvent(); 91 | pPerIoData->wsaBuf.buf = pPerIoData->Buffer; 92 | pPerIoData->wsaBuf.len = sizeof(pPerIoData->Buffer); 93 | 94 | DWORD dwNumRecv; 95 | if (WSARecv(Socket, &(pPerIoData->wsaBuf), 1, &dwNumRecv, &(pPerIoData->Flags), &(pPerIoData->Overlapped), NULL) == SOCKET_ERROR) 96 | { 97 | if (WSAGetLastError() != WSA_IO_PENDING) 98 | { 99 | delete pPerIoData; 100 | return 0; 101 | } 102 | } 103 | 104 | while (TRUE) 105 | Sleep(1000); 106 | 107 | shutdown(Socket, SD_BOTH); 108 | closesocket(Socket); 109 | 110 | WSACleanup(); 111 | return 0; 112 | } -------------------------------------------------------------------------------- /src/xio/dispatch_ref.nim: -------------------------------------------------------------------------------- 1 | import os, tables, strutils, times, heapqueue, options, asyncstreams 2 | import options, math, std/monotimes 3 | import asyncfutures except callSoon 4 | 5 | import nativesockets, net, deques 6 | 7 | export Port, SocketFlag 8 | export asyncfutures except callSoon 9 | export asyncstreams 10 | 11 | #{.injectStmt: newGcInvariant().} 12 | 13 | # TODO: Check if yielded future is nil and throw a more meaningful exception 14 | 15 | type 16 | PDispatcherBase = ref object of RootRef 17 | timers*: HeapQueue[tuple[finishAt: MonoTime, fut: Future[void]]] 18 | callbacks*: Deque[proc () {.gcsafe.}] 19 | 20 | proc processTimers( 21 | p: PDispatcherBase, didSomeWork: var bool 22 | ): Option[int] {.inline.} = 23 | # Pop the timers in the order in which they will expire (smaller `finishAt`). 24 | var count = p.timers.len 25 | let t = getMonoTime() 26 | while count > 0 and t >= p.timers[0].finishAt: 27 | p.timers.pop().fut.complete() 28 | dec count 29 | didSomeWork = true 30 | 31 | # Return the number of milliseconds in which the next timer will expire. 32 | if p.timers.len == 0: return 33 | 34 | let millisecs = (p.timers[0].finishAt - getMonoTime()).inMilliseconds 35 | return some(millisecs.int + 1) 36 | 37 | proc processPendingCallbacks(p: PDispatcherBase; didSomeWork: var bool) = 38 | while p.callbacks.len > 0: 39 | var cb = p.callbacks.popFirst() 40 | cb() 41 | didSomeWork = true 42 | 43 | proc adjustTimeout( 44 | p: PDispatcherBase, pollTimeout: int, nextTimer: Option[int] 45 | ): int {.inline.} = 46 | if p.callbacks.len != 0: 47 | return 0 48 | 49 | if nextTimer.isNone() or pollTimeout == -1: 50 | return pollTimeout 51 | 52 | result = max(nextTimer.get(), 0) 53 | result = min(pollTimeout, result) 54 | 55 | proc callSoon*(cbproc: proc () {.gcsafe.}) {.gcsafe.} 56 | ## Schedule `cbproc` to be called as soon as possible. 57 | ## The callback is called when control returns to the event loop. 58 | 59 | proc initCallSoonProc = 60 | if asyncfutures.getCallSoonProc().isNil: 61 | asyncfutures.setCallSoonProc(callSoon) 62 | 63 | template implementSetInheritable() {.dirty.} = 64 | when declared(setInheritable): 65 | proc setInheritable*(fd: AsyncFD, inheritable: bool): bool = 66 | ## Control whether a file handle can be inherited by child processes. 67 | ## Returns ``true`` on success. 68 | ## 69 | ## This procedure is not guaranteed to be available for all platforms. 70 | ## Test for availability with `declared()`_. 71 | fd.FileHandle.setInheritable(inheritable) 72 | 73 | when defined(windows) or defined(nimdoc): 74 | import winlean, sets, hashes 75 | type 76 | CompletionKey = ULONG_PTR 77 | 78 | CompletionData* = object 79 | fd*: AsyncFD # TODO: Rename this. 80 | cb*: owned(proc (fd: AsyncFD, bytesTransferred: DWORD, 81 | errcode: OSErrorCode) {.closure, gcsafe.}) 82 | cell*: ForeignCell # we need this `cell` to protect our `cb` environment, 83 | # when using RegisterWaitForSingleObject, because 84 | # waiting is done in different thread. 85 | 86 | PDispatcher* = ref object of PDispatcherBase 87 | ioPort: Handle 88 | handles: HashSet[AsyncFD] 89 | 90 | CustomObj = object of OVERLAPPED 91 | data*: CompletionData 92 | 93 | CustomRef* = ref CustomObj 94 | 95 | AsyncFD* = distinct int 96 | 97 | PostCallbackData = object 98 | ioPort: Handle 99 | handleFd: AsyncFD 100 | waitFd: Handle 101 | ovl: owned CustomRef 102 | PostCallbackDataPtr = ptr PostCallbackData 103 | 104 | AsyncEventImpl = object 105 | hEvent: Handle 106 | hWaiter: Handle 107 | pcd: PostCallbackDataPtr 108 | AsyncEvent* = ptr AsyncEventImpl 109 | 110 | Callback* = proc (fd: AsyncFD): bool {.closure, gcsafe.} 111 | 112 | proc hash(x: AsyncFD): Hash {.borrow.} 113 | proc `==`*(x: AsyncFD, y: AsyncFD): bool {.borrow.} 114 | 115 | proc newDispatcher*(): owned PDispatcher = 116 | ## Creates a new Dispatcher instance. 117 | new result 118 | result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) 119 | result.handles = initHashSet[AsyncFD]() 120 | result.timers.clear() 121 | result.callbacks = initDeque[proc () {.closure, gcsafe.}](64) 122 | 123 | var gDisp{.threadvar.}: owned PDispatcher ## Global dispatcher 124 | 125 | proc setGlobalDispatcher*(disp: owned PDispatcher) = 126 | if not gDisp.isNil: 127 | assert gDisp.callbacks.len == 0 128 | gDisp = disp 129 | initCallSoonProc() 130 | 131 | proc getGlobalDispatcher*(): PDispatcher = 132 | if gDisp.isNil: 133 | setGlobalDispatcher(newDispatcher()) 134 | result = gDisp 135 | 136 | proc getIoHandler*(disp: PDispatcher): Handle = 137 | ## Returns the underlying IO Completion Port handle (Windows) or selector 138 | ## (Unix) for the specified dispatcher. 139 | return disp.ioPort 140 | 141 | proc register*(fd: AsyncFD) = 142 | ## Registers ``fd`` with the dispatcher. 143 | let p = getGlobalDispatcher() 144 | 145 | if createIoCompletionPort(fd.Handle, p.ioPort, 146 | cast[CompletionKey](fd), 1) == 0: 147 | raiseOSError(osLastError()) 148 | p.handles.incl(fd) 149 | 150 | proc verifyPresence(fd: AsyncFD) = 151 | ## Ensures that file descriptor has been registered with the dispatcher. 152 | ## Raises ValueError if `fd` has not been registered. 153 | let p = getGlobalDispatcher() 154 | if fd notin p.handles: 155 | raise newException(ValueError, 156 | "Operation performed on a socket which has not been registered with" & 157 | " the dispatcher yet.") 158 | 159 | proc hasPendingOperations*(): bool = 160 | ## Returns `true` if the global dispatcher has pending operations. 161 | let p = getGlobalDispatcher() 162 | p.handles.len != 0 or p.timers.len != 0 or p.callbacks.len != 0 163 | 164 | proc runOnce(timeout = 500): bool = 165 | let p = getGlobalDispatcher() 166 | if p.handles.len == 0 and p.timers.len == 0 and p.callbacks.len == 0: 167 | raise newException(ValueError, 168 | "No handles or timers registered in dispatcher.") 169 | 170 | result = false 171 | let nextTimer = processTimers(p, result) 172 | let at = adjustTimeout(p, timeout, nextTimer) 173 | var llTimeout = 174 | if at == -1: winlean.INFINITE 175 | else: at.int32 176 | 177 | var lpNumberOfBytesTransferred: DWORD 178 | var lpCompletionKey: ULONG_PTR 179 | var customOverlapped: CustomRef 180 | let res = getQueuedCompletionStatus(p.ioPort, 181 | addr lpNumberOfBytesTransferred, addr lpCompletionKey, 182 | cast[ptr POVERLAPPED](addr customOverlapped), llTimeout).bool 183 | result = true 184 | # For 'gcDestructors' the destructor of 'customOverlapped' will 185 | # be called at the end and we are the only owner here. This means 186 | # We do not have to 'GC_unref(customOverlapped)' because the destructor 187 | # does that for us. 188 | 189 | # http://stackoverflow.com/a/12277264/492186 190 | # TODO: http://www.serverframework.com/handling-multiple-pending-socket-read-and-write-operations.html 191 | if res: 192 | # This is useful for ensuring the reliability of the overlapped struct. 193 | assert customOverlapped.data.fd == lpCompletionKey.AsyncFD 194 | 195 | customOverlapped.data.cb(customOverlapped.data.fd, 196 | lpNumberOfBytesTransferred, OSErrorCode(-1)) 197 | 198 | # If cell.data != nil, then system.protect(rawEnv(cb)) was called, 199 | # so we need to dispose our `cb` environment, because it is not needed 200 | # anymore. 201 | if customOverlapped.data.cell.data != nil: 202 | system.dispose(customOverlapped.data.cell) 203 | 204 | when not defined(gcDestructors): 205 | GC_unref(customOverlapped) 206 | else: 207 | let errCode = osLastError() 208 | if customOverlapped != nil: 209 | assert customOverlapped.data.fd == lpCompletionKey.AsyncFD 210 | customOverlapped.data.cb(customOverlapped.data.fd, 211 | lpNumberOfBytesTransferred, errCode) 212 | if customOverlapped.data.cell.data != nil: 213 | system.dispose(customOverlapped.data.cell) 214 | when not defined(gcDestructors): 215 | GC_unref(customOverlapped) 216 | else: 217 | if errCode.int32 == WAIT_TIMEOUT: 218 | # Timed out 219 | result = false 220 | else: raiseOSError(errCode) 221 | 222 | # Timer processing. 223 | discard processTimers(p, result) 224 | # Callback queue processing 225 | processPendingCallbacks(p, result) 226 | 227 | 228 | var acceptEx: WSAPROC_ACCEPTEX 229 | var connectEx: WSAPROC_CONNECTEX 230 | var getAcceptExSockAddrs: WSAPROC_GETACCEPTEXSOCKADDRS 231 | 232 | proc initPointer(s: SocketHandle, fun: var pointer, guid: var GUID): bool = 233 | # Ref: https://github.com/powdahound/twisted/blob/master/twisted/internet/iocpreactor/iocpsupport/winsock_pointers.c 234 | var bytesRet: DWORD 235 | fun = nil 236 | result = WSAIoctl(s, SIO_GET_EXTENSION_FUNCTION_POINTER, addr guid, 237 | sizeof(GUID).DWORD, addr fun, sizeof(pointer).DWORD, 238 | addr bytesRet, nil, nil) == 0 239 | 240 | proc initAll() = 241 | let dummySock = createNativeSocket() 242 | if dummySock == INVALID_SOCKET: 243 | raiseOSError(osLastError()) 244 | var fun: pointer = nil 245 | if not initPointer(dummySock, fun, WSAID_CONNECTEX): 246 | raiseOSError(osLastError()) 247 | connectEx = cast[WSAPROC_CONNECTEX](fun) 248 | if not initPointer(dummySock, fun, WSAID_ACCEPTEX): 249 | raiseOSError(osLastError()) 250 | acceptEx = cast[WSAPROC_ACCEPTEX](fun) 251 | if not initPointer(dummySock, fun, WSAID_GETACCEPTEXSOCKADDRS): 252 | raiseOSError(osLastError()) 253 | getAcceptExSockAddrs = cast[WSAPROC_GETACCEPTEXSOCKADDRS](fun) 254 | close(dummySock) 255 | 256 | proc newCustom*(): CustomRef = 257 | result = CustomRef() # 0 258 | GC_ref(result) # 1 prevent destructor from doing a premature free. 259 | # destructor of newCustom's caller --> 0. This means 260 | # Windows holds a ref for us with RC == 0 (single owner). 261 | # This is passed back to us in the IO completion port. 262 | 263 | proc recv*(socket: AsyncFD, size: int, 264 | flags = {SocketFlag.SafeDisconn}): owned(Future[string]) = 265 | ## Reads **up to** ``size`` bytes from ``socket``. Returned future will 266 | ## complete once all the data requested is read, a part of the data has been 267 | ## read, or the socket has disconnected in which case the future will 268 | ## complete with a value of ``""``. 269 | ## 270 | ## **Warning**: The ``Peek`` socket flag is not supported on Windows. 271 | 272 | 273 | # Things to note: 274 | # * When WSARecv completes immediately then ``bytesReceived`` is very 275 | # unreliable. 276 | # * Still need to implement message-oriented socket disconnection, 277 | # '\0' in the message currently signifies a socket disconnect. Who 278 | # knows what will happen when someone sends that to our socket. 279 | verifyPresence(socket) 280 | assert SocketFlag.Peek notin flags, "Peek not supported on Windows." 281 | 282 | var retFuture = newFuture[string]("recv") 283 | var dataBuf: TWSABuf 284 | dataBuf.buf = cast[cstring](alloc0(size)) 285 | dataBuf.len = size.ULONG 286 | 287 | var bytesReceived: DWORD 288 | var flagsio = flags.toOSFlags().DWORD 289 | var ol = newCustom() 290 | ol.data = CompletionData(fd: socket, cb: 291 | proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = 292 | if not retFuture.finished: 293 | if errcode == OSErrorCode(-1): 294 | if bytesCount == 0 and dataBuf.buf[0] == '\0': 295 | retFuture.complete("") 296 | else: 297 | var data = newString(bytesCount) 298 | assert bytesCount <= size 299 | copyMem(addr data[0], addr dataBuf.buf[0], bytesCount) 300 | retFuture.complete($data) 301 | else: 302 | if flags.isDisconnectionError(errcode): 303 | retFuture.complete("") 304 | else: 305 | retFuture.fail(newException(OSError, osErrorMsg(errcode))) 306 | if dataBuf.buf != nil: 307 | dealloc dataBuf.buf 308 | dataBuf.buf = nil 309 | ) 310 | 311 | let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived, 312 | addr flagsio, cast[POVERLAPPED](ol), nil) 313 | if ret == -1: 314 | let err = osLastError() 315 | if err.int32 != ERROR_IO_PENDING: 316 | if dataBuf.buf != nil: 317 | dealloc dataBuf.buf 318 | dataBuf.buf = nil 319 | GC_unref(ol) 320 | if flags.isDisconnectionError(err): 321 | retFuture.complete("") 322 | else: 323 | retFuture.fail(newException(OSError, osErrorMsg(err))) 324 | elif ret == 0: 325 | # Request completed immediately. 326 | if bytesReceived != 0: 327 | var data = newString(bytesReceived) 328 | assert bytesReceived <= size 329 | copyMem(addr data[0], addr dataBuf.buf[0], bytesReceived) 330 | retFuture.complete($data) 331 | else: 332 | if hasOverlappedIoCompleted(cast[POVERLAPPED](ol)): 333 | retFuture.complete("") 334 | return retFuture 335 | 336 | proc recvInto*(socket: AsyncFD, buf: pointer, size: int, 337 | flags = {SocketFlag.SafeDisconn}): owned(Future[int]) = 338 | ## Reads **up to** ``size`` bytes from ``socket`` into ``buf``, which must 339 | ## at least be of that size. Returned future will complete once all the 340 | ## data requested is read, a part of the data has been read, or the socket 341 | ## has disconnected in which case the future will complete with a value of 342 | ## ``0``. 343 | ## 344 | ## **Warning**: The ``Peek`` socket flag is not supported on Windows. 345 | 346 | 347 | # Things to note: 348 | # * When WSARecv completes immediately then ``bytesReceived`` is very 349 | # unreliable. 350 | # * Still need to implement message-oriented socket disconnection, 351 | # '\0' in the message currently signifies a socket disconnect. Who 352 | # knows what will happen when someone sends that to our socket. 353 | verifyPresence(socket) 354 | assert SocketFlag.Peek notin flags, "Peek not supported on Windows." 355 | 356 | var retFuture = newFuture[int]("recvInto") 357 | 358 | #buf[] = '\0' 359 | var dataBuf: TWSABuf 360 | dataBuf.buf = cast[cstring](buf) 361 | dataBuf.len = size.ULONG 362 | 363 | var bytesReceived: DWORD 364 | var flagsio = flags.toOSFlags().DWORD 365 | var ol = newCustom() 366 | ol.data = CompletionData(fd: socket, cb: 367 | proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = 368 | if not retFuture.finished: 369 | if errcode == OSErrorCode(-1): 370 | retFuture.complete(bytesCount) 371 | else: 372 | if flags.isDisconnectionError(errcode): 373 | retFuture.complete(0) 374 | else: 375 | retFuture.fail(newException(OSError, osErrorMsg(errcode))) 376 | if dataBuf.buf != nil: 377 | dataBuf.buf = nil 378 | ) 379 | 380 | let ret = WSARecv(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived, 381 | addr flagsio, cast[POVERLAPPED](ol), nil) 382 | if ret == -1: 383 | let err = osLastError() 384 | if err.int32 != ERROR_IO_PENDING: 385 | if dataBuf.buf != nil: 386 | dataBuf.buf = nil 387 | GC_unref(ol) 388 | if flags.isDisconnectionError(err): 389 | retFuture.complete(0) 390 | else: 391 | retFuture.fail(newException(OSError, osErrorMsg(err))) 392 | elif ret == 0: 393 | # Request completed immediately. 394 | if bytesReceived != 0: 395 | assert bytesReceived <= size 396 | retFuture.complete(bytesReceived) 397 | else: 398 | if hasOverlappedIoCompleted(cast[POVERLAPPED](ol)): 399 | retFuture.complete(bytesReceived) 400 | return retFuture 401 | 402 | proc send*(socket: AsyncFD, buf: pointer, size: int, 403 | flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = 404 | ## Sends ``size`` bytes from ``buf`` to ``socket``. The returned future 405 | ## will complete once all data has been sent. 406 | ## 407 | ## **WARNING**: Use it with caution. If ``buf`` refers to GC'ed object, 408 | ## you must use GC_ref/GC_unref calls to avoid early freeing of the buffer. 409 | verifyPresence(socket) 410 | var retFuture = newFuture[void]("send") 411 | 412 | var dataBuf: TWSABuf 413 | dataBuf.buf = cast[cstring](buf) 414 | dataBuf.len = size.ULONG 415 | 416 | var bytesReceived, lowFlags: DWORD 417 | var ol = newCustom() 418 | ol.data = CompletionData(fd: socket, cb: 419 | proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = 420 | if not retFuture.finished: 421 | if errcode == OSErrorCode(-1): 422 | retFuture.complete() 423 | else: 424 | if flags.isDisconnectionError(errcode): 425 | retFuture.complete() 426 | else: 427 | retFuture.fail(newOSError(errcode)) 428 | ) 429 | 430 | let ret = WSASend(socket.SocketHandle, addr dataBuf, 1, addr bytesReceived, 431 | lowFlags, cast[POVERLAPPED](ol), nil) 432 | if ret == -1: 433 | let err = osLastError() 434 | if err.int32 != ERROR_IO_PENDING: 435 | GC_unref(ol) 436 | if flags.isDisconnectionError(err): 437 | retFuture.complete() 438 | else: 439 | retFuture.fail(newException(OSError, osErrorMsg(err))) 440 | else: 441 | retFuture.complete() 442 | # We don't deallocate ``ol`` here because even though this completed 443 | # immediately poll will still be notified about its completion and it will 444 | # free ``ol``. 445 | return retFuture 446 | 447 | proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr, 448 | saddrLen: SockLen, 449 | flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = 450 | ## Sends ``data`` to specified destination ``saddr``, using 451 | ## socket ``socket``. The returned future will complete once all data 452 | ## has been sent. 453 | verifyPresence(socket) 454 | var retFuture = newFuture[void]("sendTo") 455 | var dataBuf: TWSABuf 456 | dataBuf.buf = cast[cstring](data) 457 | dataBuf.len = size.ULONG 458 | var bytesSent = 0.DWORD 459 | var lowFlags = 0.DWORD 460 | 461 | # we will preserve address in our stack 462 | var staddr: array[128, char] # SOCKADDR_STORAGE size is 128 bytes 463 | var stalen: cint = cint(saddrLen) 464 | zeroMem(addr(staddr[0]), 128) 465 | copyMem(addr(staddr[0]), saddr, saddrLen) 466 | 467 | var ol = newCustom() 468 | ol.data = CompletionData(fd: socket, cb: 469 | proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = 470 | if not retFuture.finished: 471 | if errcode == OSErrorCode(-1): 472 | retFuture.complete() 473 | else: 474 | retFuture.fail(newException(OSError, osErrorMsg(errcode))) 475 | ) 476 | 477 | let ret = WSASendTo(socket.SocketHandle, addr dataBuf, 1, addr bytesSent, 478 | lowFlags, cast[ptr SockAddr](addr(staddr[0])), 479 | stalen, cast[POVERLAPPED](ol), nil) 480 | if ret == -1: 481 | let err = osLastError() 482 | if err.int32 != ERROR_IO_PENDING: 483 | GC_unref(ol) 484 | retFuture.fail(newException(OSError, osErrorMsg(err))) 485 | else: 486 | retFuture.complete() 487 | # We don't deallocate ``ol`` here because even though this completed 488 | # immediately poll will still be notified about its completion and it will 489 | # free ``ol``. 490 | return retFuture 491 | 492 | proc recvFromInto*(socket: AsyncFD, data: pointer, size: int, 493 | saddr: ptr SockAddr, saddrLen: ptr SockLen, 494 | flags = {SocketFlag.SafeDisconn}): owned(Future[int]) = 495 | ## Receives a datagram data from ``socket`` into ``buf``, which must 496 | ## be at least of size ``size``, address of datagram's sender will be 497 | ## stored into ``saddr`` and ``saddrLen``. Returned future will complete 498 | ## once one datagram has been received, and will return size of packet 499 | ## received. 500 | verifyPresence(socket) 501 | var retFuture = newFuture[int]("recvFromInto") 502 | 503 | var dataBuf = TWSABuf(buf: cast[cstring](data), len: size.ULONG) 504 | 505 | var bytesReceived = 0.DWORD 506 | var lowFlags = 0.DWORD 507 | 508 | var ol = newCustom() 509 | ol.data = CompletionData(fd: socket, cb: 510 | proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = 511 | if not retFuture.finished: 512 | if errcode == OSErrorCode(-1): 513 | assert bytesCount <= size 514 | retFuture.complete(bytesCount) 515 | else: 516 | # datagram sockets don't have disconnection, 517 | # so we can just raise an exception 518 | retFuture.fail(newException(OSError, osErrorMsg(errcode))) 519 | ) 520 | 521 | let res = WSARecvFrom(socket.SocketHandle, addr dataBuf, 1, 522 | addr bytesReceived, addr lowFlags, 523 | saddr, cast[ptr cint](saddrLen), 524 | cast[POVERLAPPED](ol), nil) 525 | if res == -1: 526 | let err = osLastError() 527 | if err.int32 != ERROR_IO_PENDING: 528 | GC_unref(ol) 529 | retFuture.fail(newException(OSError, osErrorMsg(err))) 530 | else: 531 | # Request completed immediately. 532 | if bytesReceived != 0: 533 | assert bytesReceived <= size 534 | retFuture.complete(bytesReceived) 535 | else: 536 | if hasOverlappedIoCompleted(cast[POVERLAPPED](ol)): 537 | retFuture.complete(bytesReceived) 538 | return retFuture 539 | 540 | proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}, 541 | inheritable = defined(nimInheritHandles)): 542 | owned(Future[tuple[address: string, client: AsyncFD]]) = 543 | ## Accepts a new connection. Returns a future containing the client socket 544 | ## corresponding to that connection and the remote address of the client. 545 | ## The future will complete when the connection is successfully accepted. 546 | ## 547 | ## The resulting client socket is automatically registered to the 548 | ## dispatcher. 549 | ## 550 | ## If ``inheritable`` is false (the default), the resulting client socket will 551 | ## not be inheritable by child processes. 552 | ## 553 | ## The ``accept`` call may result in an error if the connecting socket 554 | ## disconnects during the duration of the ``accept``. If the ``SafeDisconn`` 555 | ## flag is specified then this error will not be raised and instead 556 | ## accept will be called again. 557 | verifyPresence(socket) 558 | var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr") 559 | 560 | var clientSock = createNativeSocket(inheritable = inheritable) 561 | if clientSock == osInvalidSocket: raiseOSError(osLastError()) 562 | 563 | const lpOutputLen = 1024 564 | var lpOutputBuf = newString(lpOutputLen) 565 | var dwBytesReceived: DWORD 566 | let dwReceiveDataLength = 0.DWORD # We don't want any data to be read. 567 | let dwLocalAddressLength = DWORD(sizeof(Sockaddr_in6) + 16) 568 | let dwRemoteAddressLength = DWORD(sizeof(Sockaddr_in6) + 16) 569 | 570 | template failAccept(errcode) = 571 | if flags.isDisconnectionError(errcode): 572 | var newAcceptFut = acceptAddr(socket, flags) 573 | newAcceptFut.callback = 574 | proc () = 575 | if newAcceptFut.failed: 576 | retFuture.fail(newAcceptFut.readError) 577 | else: 578 | retFuture.complete(newAcceptFut.read) 579 | else: 580 | retFuture.fail(newException(OSError, osErrorMsg(errcode))) 581 | 582 | template completeAccept() {.dirty.} = 583 | var listenSock = socket 584 | let setoptRet = setsockopt(clientSock, SOL_SOCKET, 585 | SO_UPDATE_ACCEPT_CONTEXT, addr listenSock, 586 | sizeof(listenSock).SockLen) 587 | if setoptRet != 0: 588 | let errcode = osLastError() 589 | discard clientSock.closesocket() 590 | failAccept(errcode) 591 | else: 592 | var localSockaddr, remoteSockaddr: ptr SockAddr 593 | var localLen, remoteLen: int32 594 | getAcceptExSockAddrs(addr lpOutputBuf[0], dwReceiveDataLength, 595 | dwLocalAddressLength, dwRemoteAddressLength, 596 | addr localSockaddr, addr localLen, 597 | addr remoteSockaddr, addr remoteLen) 598 | try: 599 | let address = getAddrString(remoteSockaddr) 600 | register(clientSock.AsyncFD) 601 | retFuture.complete((address: address, client: clientSock.AsyncFD)) 602 | except: 603 | # getAddrString may raise 604 | clientSock.close() 605 | retFuture.fail(getCurrentException()) 606 | 607 | var ol = newCustom() 608 | ol.data = CompletionData(fd: socket, cb: 609 | proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = 610 | if not retFuture.finished: 611 | if errcode == OSErrorCode(-1): 612 | completeAccept() 613 | else: 614 | failAccept(errcode) 615 | ) 616 | 617 | # http://msdn.microsoft.com/en-us/library/windows/desktop/ms737524%28v=vs.85%29.aspx 618 | let ret = acceptEx(socket.SocketHandle, clientSock, addr lpOutputBuf[0], 619 | dwReceiveDataLength, 620 | dwLocalAddressLength, 621 | dwRemoteAddressLength, 622 | addr dwBytesReceived, cast[POVERLAPPED](ol)) 623 | 624 | if not ret: 625 | let err = osLastError() 626 | if err.int32 != ERROR_IO_PENDING: 627 | failAccept(err) 628 | GC_unref(ol) 629 | else: 630 | completeAccept() 631 | # We don't deallocate ``ol`` here because even though this completed 632 | # immediately poll will still be notified about its completion and it will 633 | # free ``ol``. 634 | 635 | return retFuture 636 | 637 | implementSetInheritable() 638 | 639 | proc closeSocket*(socket: AsyncFD) = 640 | ## Closes a socket and ensures that it is unregistered. 641 | socket.SocketHandle.close() 642 | getGlobalDispatcher().handles.excl(socket) 643 | 644 | proc unregister*(fd: AsyncFD) = 645 | ## Unregisters ``fd``. 646 | getGlobalDispatcher().handles.excl(fd) 647 | 648 | proc contains*(disp: PDispatcher, fd: AsyncFD): bool = 649 | return fd in disp.handles 650 | 651 | {.push stackTrace: off.} 652 | proc waitableCallback(param: pointer, 653 | timerOrWaitFired: WINBOOL) {.stdcall.} = 654 | var p = cast[PostCallbackDataPtr](param) 655 | discard postQueuedCompletionStatus(p.ioPort, timerOrWaitFired.DWORD, 656 | ULONG_PTR(p.handleFd), 657 | cast[pointer](p.ovl)) 658 | {.pop.} 659 | 660 | proc registerWaitableEvent(fd: AsyncFD, cb: Callback; mask: DWORD) = 661 | let p = getGlobalDispatcher() 662 | var flags = (WT_EXECUTEINWAITTHREAD or WT_EXECUTEONLYONCE).DWORD 663 | var hEvent = wsaCreateEvent() 664 | if hEvent == 0: 665 | raiseOSError(osLastError()) 666 | var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData))) 667 | pcd.ioPort = p.ioPort 668 | pcd.handleFd = fd 669 | var ol = newCustom() 670 | 671 | ol.data = CompletionData(fd: fd, cb: 672 | proc(fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) {.gcsafe.} = 673 | # we excluding our `fd` because cb(fd) can register own handler 674 | # for this `fd` 675 | p.handles.excl(fd) 676 | # unregisterWait() is called before callback, because appropriate 677 | # winsockets function can re-enable event. 678 | # https://msdn.microsoft.com/en-us/library/windows/desktop/ms741576(v=vs.85).aspx 679 | if unregisterWait(pcd.waitFd) == 0: 680 | let err = osLastError() 681 | if err.int32 != ERROR_IO_PENDING: 682 | deallocShared(cast[pointer](pcd)) 683 | discard wsaCloseEvent(hEvent) 684 | raiseOSError(err) 685 | if cb(fd): 686 | # callback returned `true`, so we free all allocated resources 687 | deallocShared(cast[pointer](pcd)) 688 | if not wsaCloseEvent(hEvent): 689 | raiseOSError(osLastError()) 690 | # pcd.ovl will be unrefed in poll(). 691 | else: 692 | # callback returned `false` we need to continue 693 | if p.handles.contains(fd): 694 | # new callback was already registered with `fd`, so we free all 695 | # allocated resources. This happens because in callback `cb` 696 | # addRead/addWrite was called with same `fd`. 697 | deallocShared(cast[pointer](pcd)) 698 | if not wsaCloseEvent(hEvent): 699 | raiseOSError(osLastError()) 700 | else: 701 | # we need to include `fd` again 702 | p.handles.incl(fd) 703 | # and register WaitForSingleObject again 704 | if not registerWaitForSingleObject(addr(pcd.waitFd), hEvent, 705 | cast[WAITORTIMERCALLBACK](waitableCallback), 706 | cast[pointer](pcd), INFINITE, flags): 707 | # pcd.ovl will be unrefed in poll() 708 | let err = osLastError() 709 | deallocShared(cast[pointer](pcd)) 710 | discard wsaCloseEvent(hEvent) 711 | raiseOSError(err) 712 | else: 713 | # we incref `pcd.ovl` and `protect` callback one more time, 714 | # because it will be unrefed and disposed in `poll()` after 715 | # callback finishes. 716 | GC_ref(pcd.ovl) 717 | pcd.ovl.data.cell = system.protect(rawEnv(pcd.ovl.data.cb)) 718 | ) 719 | # We need to protect our callback environment value, so GC will not free it 720 | # accidentally. 721 | ol.data.cell = system.protect(rawEnv(ol.data.cb)) 722 | 723 | # This is main part of `hacky way` is using WSAEventSelect, so `hEvent` 724 | # will be signaled when appropriate `mask` events will be triggered. 725 | if wsaEventSelect(fd.SocketHandle, hEvent, mask) != 0: 726 | let err = osLastError() 727 | GC_unref(ol) 728 | deallocShared(cast[pointer](pcd)) 729 | discard wsaCloseEvent(hEvent) 730 | raiseOSError(err) 731 | 732 | pcd.ovl = ol 733 | if not registerWaitForSingleObject(addr(pcd.waitFd), hEvent, 734 | cast[WAITORTIMERCALLBACK](waitableCallback), 735 | cast[pointer](pcd), INFINITE, flags): 736 | let err = osLastError() 737 | GC_unref(ol) 738 | deallocShared(cast[pointer](pcd)) 739 | discard wsaCloseEvent(hEvent) 740 | raiseOSError(err) 741 | p.handles.incl(fd) 742 | 743 | proc addRead*(fd: AsyncFD, cb: Callback) = 744 | ## Start watching the file descriptor for read availability and then call 745 | ## the callback ``cb``. 746 | ## 747 | ## This is not ``pure`` mechanism for Windows Completion Ports (IOCP), 748 | ## so if you can avoid it, please do it. Use `addRead` only if really 749 | ## need it (main usecase is adaptation of unix-like libraries to be 750 | ## asynchronous on Windows). 751 | ## 752 | ## If you use this function, you don't need to use asyncdispatch.recv() 753 | ## or asyncdispatch.accept(), because they are using IOCP, please use 754 | ## nativesockets.recv() and nativesockets.accept() instead. 755 | ## 756 | ## Be sure your callback ``cb`` returns ``true``, if you want to remove 757 | ## watch of `read` notifications, and ``false``, if you want to continue 758 | ## receiving notifications. 759 | registerWaitableEvent(fd, cb, FD_READ or FD_ACCEPT or FD_OOB or FD_CLOSE) 760 | 761 | proc addWrite*(fd: AsyncFD, cb: Callback) = 762 | ## Start watching the file descriptor for write availability and then call 763 | ## the callback ``cb``. 764 | ## 765 | ## This is not ``pure`` mechanism for Windows Completion Ports (IOCP), 766 | ## so if you can avoid it, please do it. Use `addWrite` only if really 767 | ## need it (main usecase is adaptation of unix-like libraries to be 768 | ## asynchronous on Windows). 769 | ## 770 | ## If you use this function, you don't need to use asyncdispatch.send() 771 | ## or asyncdispatch.connect(), because they are using IOCP, please use 772 | ## nativesockets.send() and nativesockets.connect() instead. 773 | ## 774 | ## Be sure your callback ``cb`` returns ``true``, if you want to remove 775 | ## watch of `write` notifications, and ``false``, if you want to continue 776 | ## receiving notifications. 777 | registerWaitableEvent(fd, cb, FD_WRITE or FD_CONNECT or FD_CLOSE) 778 | 779 | template registerWaitableHandle(p, hEvent, flags, pcd, timeout, 780 | handleCallback) = 781 | let handleFD = AsyncFD(hEvent) 782 | pcd.ioPort = p.ioPort 783 | pcd.handleFd = handleFD 784 | var ol = newCustom() 785 | ol.data.fd = handleFD 786 | ol.data.cb = handleCallback 787 | # We need to protect our callback environment value, so GC will not free it 788 | # accidentally. 789 | ol.data.cell = system.protect(rawEnv(ol.data.cb)) 790 | 791 | pcd.ovl = ol 792 | if not registerWaitForSingleObject(addr(pcd.waitFd), hEvent, 793 | cast[WAITORTIMERCALLBACK](waitableCallback), 794 | cast[pointer](pcd), timeout.DWORD, flags): 795 | let err = osLastError() 796 | GC_unref(ol) 797 | deallocShared(cast[pointer](pcd)) 798 | discard closeHandle(hEvent) 799 | raiseOSError(err) 800 | p.handles.incl(handleFD) 801 | 802 | template closeWaitable(handle: untyped) = 803 | let waitFd = pcd.waitFd 804 | deallocShared(cast[pointer](pcd)) 805 | p.handles.excl(fd) 806 | if unregisterWait(waitFd) == 0: 807 | let err = osLastError() 808 | if err.int32 != ERROR_IO_PENDING: 809 | discard closeHandle(handle) 810 | raiseOSError(err) 811 | if closeHandle(handle) == 0: 812 | raiseOSError(osLastError()) 813 | 814 | proc addTimer*(timeout: int, oneshot: bool, cb: Callback) = 815 | ## Registers callback ``cb`` to be called when timer expired. 816 | ## 817 | ## Parameters: 818 | ## 819 | ## * ``timeout`` - timeout value in milliseconds. 820 | ## * ``oneshot`` 821 | ## * `true` - generate only one timeout event 822 | ## * `false` - generate timeout events periodically 823 | 824 | doAssert(timeout > 0) 825 | let p = getGlobalDispatcher() 826 | 827 | var hEvent = createEvent(nil, 1, 0, nil) 828 | if hEvent == INVALID_HANDLE_VALUE: 829 | raiseOSError(osLastError()) 830 | 831 | var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData))) 832 | var flags = WT_EXECUTEINWAITTHREAD.DWORD 833 | if oneshot: flags = flags or WT_EXECUTEONLYONCE 834 | 835 | proc timercb(fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = 836 | let res = cb(fd) 837 | if res or oneshot: 838 | closeWaitable(hEvent) 839 | else: 840 | # if callback returned `false`, then it wants to be called again, so 841 | # we need to ref and protect `pcd.ovl` again, because it will be 842 | # unrefed and disposed in `poll()`. 843 | GC_ref(pcd.ovl) 844 | pcd.ovl.data.cell = system.protect(rawEnv(pcd.ovl.data.cb)) 845 | 846 | registerWaitableHandle(p, hEvent, flags, pcd, timeout, timercb) 847 | 848 | proc addProcess*(pid: int, cb: Callback) = 849 | ## Registers callback ``cb`` to be called when process with process ID 850 | ## ``pid`` exited. 851 | const NULL = Handle(0) 852 | let p = getGlobalDispatcher() 853 | let procFlags = SYNCHRONIZE 854 | var hProcess = openProcess(procFlags, 0, pid.DWORD) 855 | if hProcess == NULL: 856 | raiseOSError(osLastError()) 857 | 858 | var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData))) 859 | var flags = WT_EXECUTEINWAITTHREAD.DWORD 860 | 861 | proc proccb(fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = 862 | closeWaitable(hProcess) 863 | discard cb(fd) 864 | 865 | registerWaitableHandle(p, hProcess, flags, pcd, INFINITE, proccb) 866 | 867 | proc newAsyncEvent*(): AsyncEvent = 868 | ## Creates a new thread-safe ``AsyncEvent`` object. 869 | ## 870 | ## New ``AsyncEvent`` object is not automatically registered with 871 | ## dispatcher like ``AsyncSocket``. 872 | var sa = SECURITY_ATTRIBUTES( 873 | nLength: sizeof(SECURITY_ATTRIBUTES).cint, 874 | bInheritHandle: 1 875 | ) 876 | var event = createEvent(addr(sa), 0'i32, 0'i32, nil) 877 | if event == INVALID_HANDLE_VALUE: 878 | raiseOSError(osLastError()) 879 | result = cast[AsyncEvent](allocShared0(sizeof(AsyncEventImpl))) 880 | result.hEvent = event 881 | 882 | proc trigger*(ev: AsyncEvent) = 883 | ## Set event ``ev`` to signaled state. 884 | if setEvent(ev.hEvent) == 0: 885 | raiseOSError(osLastError()) 886 | 887 | proc unregister*(ev: AsyncEvent) = 888 | ## Unregisters event ``ev``. 889 | doAssert(ev.hWaiter != 0, "Event is not registered in the queue!") 890 | let p = getGlobalDispatcher() 891 | p.handles.excl(AsyncFD(ev.hEvent)) 892 | if unregisterWait(ev.hWaiter) == 0: 893 | let err = osLastError() 894 | if err.int32 != ERROR_IO_PENDING: 895 | raiseOSError(err) 896 | ev.hWaiter = 0 897 | 898 | proc close*(ev: AsyncEvent) = 899 | ## Closes event ``ev``. 900 | let res = closeHandle(ev.hEvent) 901 | deallocShared(cast[pointer](ev)) 902 | if res == 0: 903 | raiseOSError(osLastError()) 904 | 905 | proc addEvent*(ev: AsyncEvent, cb: Callback) = 906 | ## Registers callback ``cb`` to be called when ``ev`` will be signaled 907 | doAssert(ev.hWaiter == 0, "Event is already registered in the queue!") 908 | 909 | let p = getGlobalDispatcher() 910 | let hEvent = ev.hEvent 911 | 912 | var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData))) 913 | var flags = WT_EXECUTEINWAITTHREAD.DWORD 914 | 915 | proc eventcb(fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = 916 | if ev.hWaiter != 0: 917 | if cb(fd): 918 | # we need this check to avoid exception, if `unregister(event)` was 919 | # called in callback. 920 | deallocShared(cast[pointer](pcd)) 921 | if ev.hWaiter != 0: 922 | unregister(ev) 923 | else: 924 | # if callback returned `false`, then it wants to be called again, so 925 | # we need to ref and protect `pcd.ovl` again, because it will be 926 | # unrefed and disposed in `poll()`. 927 | GC_ref(pcd.ovl) 928 | pcd.ovl.data.cell = system.protect(rawEnv(pcd.ovl.data.cb)) 929 | else: 930 | # if ev.hWaiter == 0, then event was unregistered before `poll()` call. 931 | deallocShared(cast[pointer](pcd)) 932 | 933 | registerWaitableHandle(p, hEvent, flags, pcd, INFINITE, eventcb) 934 | ev.hWaiter = pcd.waitFd 935 | 936 | initAll() 937 | else: 938 | import selectors 939 | from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK, 940 | MSG_NOSIGNAL 941 | when declared(posix.accept4): 942 | from posix import accept4, SOCK_CLOEXEC 943 | 944 | const 945 | InitCallbackListSize = 4 # initial size of callbacks sequence, 946 | # associated with file/socket descriptor. 947 | InitDelayedCallbackListSize = 64 # initial size of delayed callbacks 948 | # queue. 949 | type 950 | AsyncFD* = distinct cint 951 | Callback* = proc (fd: AsyncFD): bool {.closure, gcsafe.} 952 | 953 | AsyncData = object 954 | readList: seq[Callback] 955 | writeList: seq[Callback] 956 | 957 | AsyncEvent* = distinct SelectEvent 958 | 959 | PDispatcher* = ref object of PDispatcherBase 960 | selector: Selector[AsyncData] 961 | 962 | proc `==`*(x, y: AsyncFD): bool {.borrow.} 963 | proc `==`*(x, y: AsyncEvent): bool {.borrow.} 964 | 965 | template newAsyncData(): AsyncData = 966 | AsyncData( 967 | readList: newSeqOfCap[Callback](InitCallbackListSize), 968 | writeList: newSeqOfCap[Callback](InitCallbackListSize) 969 | ) 970 | 971 | proc newDispatcher*(): owned(PDispatcher) = 972 | new result 973 | result.selector = newSelector[AsyncData]() 974 | result.timers.clear() 975 | result.callbacks = initDeque[proc () {.closure, gcsafe.}](InitDelayedCallbackListSize) 976 | 977 | var gDisp{.threadvar.}: owned PDispatcher ## Global dispatcher 978 | 979 | proc setGlobalDispatcher*(disp: owned PDispatcher) = 980 | if not gDisp.isNil: 981 | assert gDisp.callbacks.len == 0 982 | gDisp = disp 983 | initCallSoonProc() 984 | 985 | proc getGlobalDispatcher*(): PDispatcher = 986 | if gDisp.isNil: 987 | setGlobalDispatcher(newDispatcher()) 988 | result = gDisp 989 | 990 | proc getIoHandler*(disp: PDispatcher): Selector[AsyncData] = 991 | return disp.selector 992 | 993 | proc register*(fd: AsyncFD) = 994 | let p = getGlobalDispatcher() 995 | var data = newAsyncData() 996 | p.selector.registerHandle(fd.SocketHandle, {}, data) 997 | 998 | proc unregister*(fd: AsyncFD) = 999 | getGlobalDispatcher().selector.unregister(fd.SocketHandle) 1000 | 1001 | proc unregister*(ev: AsyncEvent) = 1002 | getGlobalDispatcher().selector.unregister(SelectEvent(ev)) 1003 | 1004 | proc contains*(disp: PDispatcher, fd: AsyncFD): bool = 1005 | return fd.SocketHandle in disp.selector 1006 | 1007 | proc addRead*(fd: AsyncFD, cb: Callback) = 1008 | let p = getGlobalDispatcher() 1009 | var newEvents = {Event.Read} 1010 | withData(p.selector, fd.SocketHandle, adata) do: 1011 | adata.readList.add(cb) 1012 | newEvents.incl(Event.Read) 1013 | if len(adata.writeList) != 0: newEvents.incl(Event.Write) 1014 | do: 1015 | raise newException(ValueError, "File descriptor not registered.") 1016 | p.selector.updateHandle(fd.SocketHandle, newEvents) 1017 | 1018 | proc addWrite*(fd: AsyncFD, cb: Callback) = 1019 | let p = getGlobalDispatcher() 1020 | var newEvents = {Event.Write} 1021 | withData(p.selector, fd.SocketHandle, adata) do: 1022 | adata.writeList.add(cb) 1023 | newEvents.incl(Event.Write) 1024 | if len(adata.readList) != 0: newEvents.incl(Event.Read) 1025 | do: 1026 | raise newException(ValueError, "File descriptor not registered.") 1027 | p.selector.updateHandle(fd.SocketHandle, newEvents) 1028 | 1029 | proc hasPendingOperations*(): bool = 1030 | let p = getGlobalDispatcher() 1031 | not p.selector.isEmpty() or p.timers.len != 0 or p.callbacks.len != 0 1032 | 1033 | proc processBasicCallbacks( 1034 | fd: AsyncFD, event: Event 1035 | ): tuple[readCbListCount, writeCbListCount: int] = 1036 | # Process pending descriptor and AsyncEvent callbacks. 1037 | # 1038 | # Invoke every callback stored in `rwlist`, until one 1039 | # returns `false` (which means callback wants to stay 1040 | # alive). In such case all remaining callbacks will be added 1041 | # to `rwlist` again, in the order they have been inserted. 1042 | # 1043 | # `rwlist` associated with file descriptor MUST BE emptied before 1044 | # dispatching callback (See https://github.com/nim-lang/Nim/issues/5128), 1045 | # or it can be possible to fall into endless cycle. 1046 | var curList: seq[Callback] 1047 | 1048 | let selector = getGlobalDispatcher().selector 1049 | withData(selector, fd.int, fdData): 1050 | case event 1051 | of Event.Read: 1052 | shallowCopy(curList, fdData.readList) 1053 | fdData.readList = newSeqOfCap[Callback](InitCallbackListSize) 1054 | of Event.Write: 1055 | shallowCopy(curList, fdData.writeList) 1056 | fdData.writeList = newSeqOfCap[Callback](InitCallbackListSize) 1057 | else: 1058 | assert false, "Cannot process callbacks for " & $event 1059 | 1060 | let newLength = max(len(curList), InitCallbackListSize) 1061 | var newList = newSeqOfCap[Callback](newLength) 1062 | 1063 | var eventsExtinguished = false 1064 | for cb in curList: 1065 | if eventsExtinguished: 1066 | newList.add(cb) 1067 | continue 1068 | if not cb(fd): 1069 | # Callback wants to be called again. 1070 | newList.add(cb) 1071 | # This callback has returned with EAGAIN, so we don't need to 1072 | # call any other callbacks as they are all waiting for the same event 1073 | # on the same fd. 1074 | # We do need to ensure they are called again though. 1075 | eventsExtinguished = true 1076 | 1077 | withData(selector, fd.int, fdData) do: 1078 | # Descriptor is still present in the queue. 1079 | case event 1080 | of Event.Read: 1081 | fdData.readList = newList & fdData.readList 1082 | of Event.Write: 1083 | fdData.writeList = newList & fdData.writeList 1084 | else: 1085 | assert false, "Cannot process callbacks for " & $event 1086 | 1087 | result.readCbListCount = len(fdData.readList) 1088 | result.writeCbListCount = len(fdData.writeList) 1089 | do: 1090 | # Descriptor was unregistered in callback via `unregister()`. 1091 | result.readCbListCount = -1 1092 | result.writeCbListCount = -1 1093 | 1094 | template processCustomCallbacks(ident: untyped) = 1095 | # Process pending custom event callbacks. Custom events are 1096 | # {Event.Timer, Event.Signal, Event.Process, Event.Vnode}. 1097 | # There can be only one callback registered with one descriptor, 1098 | # so there is no need to iterate over list. 1099 | var curList: seq[Callback] 1100 | 1101 | withData(p.selector, ident.int, adata) do: 1102 | shallowCopy(curList, adata.readList) 1103 | adata.readList = newSeqOfCap[Callback](InitCallbackListSize) 1104 | 1105 | let newLength = len(curList) 1106 | var newList = newSeqOfCap[Callback](newLength) 1107 | 1108 | var cb = curList[0] 1109 | if not cb(fd.AsyncFD): 1110 | newList.add(cb) 1111 | 1112 | withData(p.selector, ident.int, adata) do: 1113 | # descriptor still present in queue. 1114 | adata.readList = newList & adata.readList 1115 | if len(adata.readList) == 0: 1116 | # if no callbacks registered with descriptor, unregister it. 1117 | p.selector.unregister(fd.int) 1118 | do: 1119 | # descriptor was unregistered in callback via `unregister()`. 1120 | discard 1121 | 1122 | implementSetInheritable() 1123 | 1124 | proc closeSocket*(sock: AsyncFD) = 1125 | let selector = getGlobalDispatcher().selector 1126 | if sock.SocketHandle notin selector: 1127 | raise newException(ValueError, "File descriptor not registered.") 1128 | 1129 | let data = selector.getData(sock.SocketHandle) 1130 | sock.unregister() 1131 | sock.SocketHandle.close() 1132 | # We need to unblock the read and write callbacks which could still be 1133 | # waiting for the socket to become readable and/or writeable. 1134 | for cb in data.readList & data.writeList: 1135 | if not cb(sock): 1136 | raise newException( 1137 | ValueError, "Expecting async operations to stop when fd has closed." 1138 | ) 1139 | 1140 | 1141 | proc runOnce(timeout = 500): bool = 1142 | let p = getGlobalDispatcher() 1143 | when ioselSupportedPlatform: 1144 | let customSet = {Event.Timer, Event.Signal, Event.Process, 1145 | Event.Vnode} 1146 | 1147 | if p.selector.isEmpty() and p.timers.len == 0 and p.callbacks.len == 0: 1148 | raise newException(ValueError, 1149 | "No handles or timers registered in dispatcher.") 1150 | 1151 | result = false 1152 | var keys: array[64, ReadyKey] 1153 | let nextTimer = processTimers(p, result) 1154 | var count = 1155 | p.selector.selectInto(adjustTimeout(p, timeout, nextTimer), keys) 1156 | for i in 0.. 0: incl(newEvents, Event.Read) 1191 | if writeCbListCount > 0: incl(newEvents, Event.Write) 1192 | p.selector.updateHandle(SocketHandle(fd), newEvents) 1193 | 1194 | # Timer processing. 1195 | discard processTimers(p, result) 1196 | # Callback queue processing 1197 | processPendingCallbacks(p, result) 1198 | 1199 | proc recv*(socket: AsyncFD, size: int, 1200 | flags = {SocketFlag.SafeDisconn}): owned(Future[string]) = 1201 | var retFuture = newFuture[string]("recv") 1202 | 1203 | var readBuffer = newString(size) 1204 | 1205 | proc cb(sock: AsyncFD): bool = 1206 | result = true 1207 | let res = recv(sock.SocketHandle, addr readBuffer[0], size.cint, 1208 | flags.toOSFlags()) 1209 | if res < 0: 1210 | let lastError = osLastError() 1211 | if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and 1212 | lastError.int32 != EAGAIN: 1213 | if flags.isDisconnectionError(lastError): 1214 | retFuture.complete("") 1215 | else: 1216 | retFuture.fail(newException(OSError, osErrorMsg(lastError))) 1217 | else: 1218 | result = false # We still want this callback to be called. 1219 | elif res == 0: 1220 | # Disconnected 1221 | retFuture.complete("") 1222 | else: 1223 | readBuffer.setLen(res) 1224 | retFuture.complete(readBuffer) 1225 | # TODO: The following causes a massive slowdown. 1226 | #if not cb(socket): 1227 | addRead(socket, cb) 1228 | return retFuture 1229 | 1230 | proc recvInto*(socket: AsyncFD, buf: pointer, size: int, 1231 | flags = {SocketFlag.SafeDisconn}): owned(Future[int]) = 1232 | var retFuture = newFuture[int]("recvInto") 1233 | 1234 | proc cb(sock: AsyncFD): bool = 1235 | result = true 1236 | let res = recv(sock.SocketHandle, buf, size.cint, 1237 | flags.toOSFlags()) 1238 | if res < 0: 1239 | let lastError = osLastError() 1240 | if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and 1241 | lastError.int32 != EAGAIN: 1242 | if flags.isDisconnectionError(lastError): 1243 | retFuture.complete(0) 1244 | else: 1245 | retFuture.fail(newException(OSError, osErrorMsg(lastError))) 1246 | else: 1247 | result = false # We still want this callback to be called. 1248 | else: 1249 | retFuture.complete(res) 1250 | # TODO: The following causes a massive slowdown. 1251 | #if not cb(socket): 1252 | addRead(socket, cb) 1253 | return retFuture 1254 | 1255 | proc send*(socket: AsyncFD, buf: pointer, size: int, 1256 | flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = 1257 | var retFuture = newFuture[void]("send") 1258 | 1259 | var written = 0 1260 | 1261 | proc cb(sock: AsyncFD): bool = 1262 | result = true 1263 | let netSize = size-written 1264 | var d = cast[cstring](buf) 1265 | let res = send(sock.SocketHandle, addr d[written], netSize.cint, 1266 | MSG_NOSIGNAL) 1267 | if res < 0: 1268 | let lastError = osLastError() 1269 | if lastError.int32 != EINTR and 1270 | lastError.int32 != EWOULDBLOCK and 1271 | lastError.int32 != EAGAIN: 1272 | if flags.isDisconnectionError(lastError): 1273 | retFuture.complete() 1274 | else: 1275 | retFuture.fail(newOSError(lastError)) 1276 | else: 1277 | result = false # We still want this callback to be called. 1278 | else: 1279 | written.inc(res) 1280 | if res != netSize: 1281 | result = false # We still have data to send. 1282 | else: 1283 | retFuture.complete() 1284 | # TODO: The following causes crashes. 1285 | #if not cb(socket): 1286 | addWrite(socket, cb) 1287 | return retFuture 1288 | 1289 | proc sendTo*(socket: AsyncFD, data: pointer, size: int, saddr: ptr SockAddr, 1290 | saddrLen: SockLen, 1291 | flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = 1292 | ## Sends ``data`` of size ``size`` in bytes to specified destination 1293 | ## (``saddr`` of size ``saddrLen`` in bytes, using socket ``socket``. 1294 | ## The returned future will complete once all data has been sent. 1295 | var retFuture = newFuture[void]("sendTo") 1296 | 1297 | # we will preserve address in our stack 1298 | var staddr: array[128, char] # SOCKADDR_STORAGE size is 128 bytes 1299 | var stalen = saddrLen 1300 | zeroMem(addr(staddr[0]), 128) 1301 | copyMem(addr(staddr[0]), saddr, saddrLen) 1302 | 1303 | proc cb(sock: AsyncFD): bool = 1304 | result = true 1305 | let res = sendto(sock.SocketHandle, data, size, MSG_NOSIGNAL, 1306 | cast[ptr SockAddr](addr(staddr[0])), stalen) 1307 | if res < 0: 1308 | let lastError = osLastError() 1309 | if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and 1310 | lastError.int32 != EAGAIN: 1311 | retFuture.fail(newException(OSError, osErrorMsg(lastError))) 1312 | else: 1313 | result = false # We still want this callback to be called. 1314 | else: 1315 | retFuture.complete() 1316 | 1317 | addWrite(socket, cb) 1318 | return retFuture 1319 | 1320 | proc recvFromInto*(socket: AsyncFD, data: pointer, size: int, 1321 | saddr: ptr SockAddr, saddrLen: ptr SockLen, 1322 | flags = {SocketFlag.SafeDisconn}): owned(Future[int]) = 1323 | ## Receives a datagram data from ``socket`` into ``data``, which must 1324 | ## be at least of size ``size`` in bytes, address of datagram's sender 1325 | ## will be stored into ``saddr`` and ``saddrLen``. Returned future will 1326 | ## complete once one datagram has been received, and will return size 1327 | ## of packet received. 1328 | var retFuture = newFuture[int]("recvFromInto") 1329 | proc cb(sock: AsyncFD): bool = 1330 | result = true 1331 | let res = recvfrom(sock.SocketHandle, data, size.cint, flags.toOSFlags(), 1332 | saddr, saddrLen) 1333 | if res < 0: 1334 | let lastError = osLastError() 1335 | if lastError.int32 != EINTR and lastError.int32 != EWOULDBLOCK and 1336 | lastError.int32 != EAGAIN: 1337 | retFuture.fail(newException(OSError, osErrorMsg(lastError))) 1338 | else: 1339 | result = false 1340 | else: 1341 | retFuture.complete(res) 1342 | addRead(socket, cb) 1343 | return retFuture 1344 | 1345 | proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}, 1346 | inheritable = defined(nimInheritHandles)): 1347 | owned(Future[tuple[address: string, client: AsyncFD]]) = 1348 | var retFuture = newFuture[tuple[address: string, 1349 | client: AsyncFD]]("acceptAddr") 1350 | proc cb(sock: AsyncFD): bool = 1351 | result = true 1352 | var sockAddress: Sockaddr_storage 1353 | var addrLen = sizeof(sockAddress).SockLen 1354 | var client = 1355 | when declared(accept4): 1356 | accept4(sock.SocketHandle, cast[ptr SockAddr](addr(sockAddress)), 1357 | addr(addrLen), if inheritable: 0 else: SOCK_CLOEXEC) 1358 | else: 1359 | accept(sock.SocketHandle, cast[ptr SockAddr](addr(sockAddress)), 1360 | addr(addrLen)) 1361 | when declared(setInheritable) and not declared(accept4): 1362 | if client != osInvalidSocket and not setInheritable(client, inheritable): 1363 | # Set failure first because close() itself can fail, 1364 | # altering osLastError(). 1365 | retFuture.fail(newOSError(osLastError())) 1366 | close client 1367 | return false 1368 | 1369 | if client == osInvalidSocket: 1370 | let lastError = osLastError() 1371 | assert lastError.int32 != EWOULDBLOCK and lastError.int32 != EAGAIN 1372 | if lastError.int32 == EINTR: 1373 | return false 1374 | else: 1375 | if flags.isDisconnectionError(lastError): 1376 | return false 1377 | else: 1378 | retFuture.fail(newException(OSError, osErrorMsg(lastError))) 1379 | else: 1380 | try: 1381 | let address = getAddrString(cast[ptr SockAddr](addr sockAddress)) 1382 | register(client.AsyncFD) 1383 | retFuture.complete((address, client.AsyncFD)) 1384 | except: 1385 | # getAddrString may raise 1386 | client.close() 1387 | retFuture.fail(getCurrentException()) 1388 | addRead(socket, cb) 1389 | return retFuture 1390 | 1391 | when ioselSupportedPlatform: 1392 | 1393 | proc addTimer*(timeout: int, oneshot: bool, cb: Callback) = 1394 | ## Start watching for timeout expiration, and then call the 1395 | ## callback ``cb``. 1396 | ## ``timeout`` - time in milliseconds, 1397 | ## ``oneshot`` - if ``true`` only one event will be dispatched, 1398 | ## if ``false`` continuous events every ``timeout`` milliseconds. 1399 | let p = getGlobalDispatcher() 1400 | var data = newAsyncData() 1401 | data.readList.add(cb) 1402 | p.selector.registerTimer(timeout, oneshot, data) 1403 | 1404 | proc addSignal*(signal: int, cb: Callback) = 1405 | ## Start watching signal ``signal``, and when signal appears, call the 1406 | ## callback ``cb``. 1407 | let p = getGlobalDispatcher() 1408 | var data = newAsyncData() 1409 | data.readList.add(cb) 1410 | p.selector.registerSignal(signal, data) 1411 | 1412 | proc addProcess*(pid: int, cb: Callback) = 1413 | ## Start watching for process exit with pid ``pid``, and then call 1414 | ## the callback ``cb``. 1415 | let p = getGlobalDispatcher() 1416 | var data = newAsyncData() 1417 | data.readList.add(cb) 1418 | p.selector.registerProcess(pid, data) 1419 | 1420 | proc newAsyncEvent*(): AsyncEvent = 1421 | ## Creates new ``AsyncEvent``. 1422 | result = AsyncEvent(newSelectEvent()) 1423 | 1424 | proc trigger*(ev: AsyncEvent) = 1425 | ## Sets new ``AsyncEvent`` to signaled state. 1426 | trigger(SelectEvent(ev)) 1427 | 1428 | proc close*(ev: AsyncEvent) = 1429 | ## Closes ``AsyncEvent`` 1430 | close(SelectEvent(ev)) 1431 | 1432 | proc addEvent*(ev: AsyncEvent, cb: Callback) = 1433 | ## Start watching for event ``ev``, and call callback ``cb``, when 1434 | ## ev will be set to signaled state. 1435 | let p = getGlobalDispatcher() 1436 | var data = newAsyncData() 1437 | data.readList.add(cb) 1438 | p.selector.registerEvent(SelectEvent(ev), data) 1439 | 1440 | proc drain*(timeout = 500) = 1441 | ## Waits for completion of **all** events and processes them. Raises ``ValueError`` 1442 | ## if there are no pending operations. In contrast to ``poll`` this 1443 | ## processes as many events as are available until the timeout has elapsed. 1444 | var curTimeout = timeout 1445 | let start = now() 1446 | while hasPendingOperations(): 1447 | discard runOnce(curTimeout) 1448 | curTimeout -= (now() - start).inMilliseconds.int 1449 | if curTimeout < 0: 1450 | break 1451 | 1452 | proc poll*(timeout = 500) = 1453 | ## Waits for completion events and processes them. Raises ``ValueError`` 1454 | ## if there are no pending operations. This runs the underlying OS 1455 | ## `epoll`:idx: or `kqueue`:idx: primitive only once. 1456 | discard runOnce(timeout) 1457 | 1458 | template createAsyncNativeSocketImpl(domain, sockType, protocol: untyped, 1459 | inheritable = defined(nimInheritHandles)) = 1460 | let handle = createNativeSocket(domain, sockType, protocol, inheritable) 1461 | if handle == osInvalidSocket: 1462 | return osInvalidSocket.AsyncFD 1463 | handle.setBlocking(false) 1464 | when defined(macosx) and not defined(nimdoc): 1465 | handle.setSockOptInt(SOL_SOCKET, SO_NOSIGPIPE, 1) 1466 | result = handle.AsyncFD 1467 | register(result) 1468 | 1469 | proc createAsyncNativeSocket*(domain: cint, sockType: cint, 1470 | protocol: cint, 1471 | inheritable = defined(nimInheritHandles)): AsyncFD = 1472 | createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable) 1473 | 1474 | proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET, 1475 | sockType: SockType = SOCK_STREAM, 1476 | protocol: Protocol = IPPROTO_TCP, 1477 | inheritable = defined(nimInheritHandles)): AsyncFD = 1478 | createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable) 1479 | 1480 | when defined(windows) or defined(nimdoc): 1481 | proc bindToDomain(handle: SocketHandle, domain: Domain) = 1482 | # Extracted into a separate proc, because connect() on Windows requires 1483 | # the socket to be initially bound. 1484 | template doBind(saddr) = 1485 | if bindAddr(handle, cast[ptr SockAddr](addr(saddr)), 1486 | sizeof(saddr).SockLen) < 0'i32: 1487 | raiseOSError(osLastError()) 1488 | 1489 | if domain == Domain.AF_INET6: 1490 | var saddr: Sockaddr_in6 1491 | saddr.sin6_family = uint16(toInt(domain)) 1492 | doBind(saddr) 1493 | else: 1494 | var saddr: Sockaddr_in 1495 | saddr.sin_family = uint16(toInt(domain)) 1496 | doBind(saddr) 1497 | 1498 | proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): owned(Future[void]) = 1499 | let retFuture = newFuture[void]("doConnect") 1500 | result = retFuture 1501 | 1502 | var ol = newCustom() 1503 | ol.data = CompletionData(fd: socket, cb: 1504 | proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = 1505 | if not retFuture.finished: 1506 | if errcode == OSErrorCode(-1): 1507 | retFuture.complete() 1508 | else: 1509 | retFuture.fail(newException(OSError, osErrorMsg(errcode))) 1510 | ) 1511 | 1512 | let ret = connectEx(socket.SocketHandle, addrInfo.ai_addr, 1513 | cint(addrInfo.ai_addrlen), nil, 0, nil, 1514 | cast[POVERLAPPED](ol)) 1515 | if ret: 1516 | # Request to connect completed immediately. 1517 | retFuture.complete() 1518 | # We don't deallocate ``ol`` here because even though this completed 1519 | # immediately poll will still be notified about its completion and it 1520 | # will free ``ol``. 1521 | else: 1522 | let lastError = osLastError() 1523 | if lastError.int32 != ERROR_IO_PENDING: 1524 | # With ERROR_IO_PENDING ``ol`` will be deallocated in ``poll``, 1525 | # and the future will be completed/failed there, too. 1526 | GC_unref(ol) 1527 | retFuture.fail(newException(OSError, osErrorMsg(lastError))) 1528 | else: 1529 | proc doConnect(socket: AsyncFD, addrInfo: ptr AddrInfo): owned(Future[void]) = 1530 | let retFuture = newFuture[void]("doConnect") 1531 | result = retFuture 1532 | 1533 | proc cb(fd: AsyncFD): bool = 1534 | let ret = SocketHandle(fd).getSockOptInt( 1535 | cint(SOL_SOCKET), cint(SO_ERROR)) 1536 | if ret == 0: 1537 | # We have connected. 1538 | retFuture.complete() 1539 | return true 1540 | elif ret == EINTR: 1541 | # interrupted, keep waiting 1542 | return false 1543 | else: 1544 | retFuture.fail(newException(OSError, osErrorMsg(OSErrorCode(ret)))) 1545 | return true 1546 | 1547 | let ret = connect(socket.SocketHandle, 1548 | addrInfo.ai_addr, 1549 | addrInfo.ai_addrlen.SockLen) 1550 | if ret == 0: 1551 | # Request to connect completed immediately. 1552 | retFuture.complete() 1553 | else: 1554 | let lastError = osLastError() 1555 | if lastError.int32 == EINTR or lastError.int32 == EINPROGRESS: 1556 | addWrite(socket, cb) 1557 | else: 1558 | retFuture.fail(newException(OSError, osErrorMsg(lastError))) 1559 | 1560 | template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped, 1561 | protocol: Protocol = IPPROTO_RAW) = 1562 | ## Iterates through the AddrInfo linked list asynchronously 1563 | ## until the connection can be established. 1564 | const shouldCreateFd = not declared(fd) 1565 | 1566 | when shouldCreateFd: 1567 | let sockType = protocol.toSockType() 1568 | 1569 | var fdPerDomain: array[low(Domain).ord..high(Domain).ord, AsyncFD] 1570 | for i in low(fdPerDomain)..high(fdPerDomain): 1571 | fdPerDomain[i] = osInvalidSocket.AsyncFD 1572 | template closeUnusedFds(domainToKeep = -1) {.dirty.} = 1573 | for i, fd in fdPerDomain: 1574 | if fd != osInvalidSocket.AsyncFD and i != domainToKeep: 1575 | fd.closeSocket() 1576 | 1577 | var lastException: ref Exception 1578 | var curAddrInfo = addrInfo 1579 | var domain: Domain 1580 | when shouldCreateFd: 1581 | var curFd: AsyncFD 1582 | else: 1583 | var curFd = fd 1584 | proc tryNextAddrInfo(fut: Future[void]) {.gcsafe.} = 1585 | if fut == nil or fut.failed: 1586 | if fut != nil: 1587 | lastException = fut.readError() 1588 | 1589 | while curAddrInfo != nil: 1590 | let domainOpt = curAddrInfo.ai_family.toKnownDomain() 1591 | if domainOpt.isSome: 1592 | domain = domainOpt.unsafeGet() 1593 | break 1594 | curAddrInfo = curAddrInfo.ai_next 1595 | 1596 | if curAddrInfo == nil: 1597 | freeaddrinfo(addrInfo) 1598 | when shouldCreateFd: 1599 | closeUnusedFds() 1600 | if lastException != nil: 1601 | retFuture.fail(lastException) 1602 | else: 1603 | retFuture.fail(newException( 1604 | IOError, "Couldn't resolve address: " & address)) 1605 | return 1606 | 1607 | when shouldCreateFd: 1608 | curFd = fdPerDomain[ord(domain)] 1609 | if curFd == osInvalidSocket.AsyncFD: 1610 | try: 1611 | curFd = createAsyncNativeSocket(domain, sockType, protocol) 1612 | except: 1613 | freeaddrinfo(addrInfo) 1614 | closeUnusedFds() 1615 | raise getCurrentException() 1616 | when defined(windows): 1617 | curFd.SocketHandle.bindToDomain(domain) 1618 | fdPerDomain[ord(domain)] = curFd 1619 | 1620 | doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo 1621 | curAddrInfo = curAddrInfo.ai_next 1622 | else: 1623 | freeaddrinfo(addrInfo) 1624 | when shouldCreateFd: 1625 | closeUnusedFds(ord(domain)) 1626 | retFuture.complete(curFd) 1627 | else: 1628 | retFuture.complete() 1629 | 1630 | tryNextAddrInfo(nil) 1631 | 1632 | proc dial*(address: string, port: Port, 1633 | protocol: Protocol = IPPROTO_TCP): owned(Future[AsyncFD]) = 1634 | ## Establishes connection to the specified ``address``:``port`` pair via the 1635 | ## specified protocol. The procedure iterates through possible 1636 | ## resolutions of the ``address`` until it succeeds, meaning that it 1637 | ## seamlessly works with both IPv4 and IPv6. 1638 | ## Returns the async file descriptor, registered in the dispatcher of 1639 | ## the current thread, ready to send or receive data. 1640 | let retFuture = newFuture[AsyncFD]("dial") 1641 | result = retFuture 1642 | let sockType = protocol.toSockType() 1643 | 1644 | let aiList = getAddrInfo(address, port, Domain.AF_UNSPEC, sockType, protocol) 1645 | asyncAddrInfoLoop(aiList, noFD, protocol) 1646 | 1647 | proc connect*(socket: AsyncFD, address: string, port: Port, 1648 | domain = Domain.AF_INET): owned(Future[void]) = 1649 | let retFuture = newFuture[void]("connect") 1650 | result = retFuture 1651 | 1652 | when defined(windows): 1653 | verifyPresence(socket) 1654 | else: 1655 | assert getSockDomain(socket.SocketHandle) == domain 1656 | 1657 | let aiList = getAddrInfo(address, port, domain) 1658 | when defined(windows): 1659 | socket.SocketHandle.bindToDomain(domain) 1660 | asyncAddrInfoLoop(aiList, socket) 1661 | 1662 | proc sleepAsync*(ms: int | float): owned(Future[void]) = 1663 | ## Suspends the execution of the current async procedure for the next 1664 | ## ``ms`` milliseconds. 1665 | var retFuture = newFuture[void]("sleepAsync") 1666 | let p = getGlobalDispatcher() 1667 | when ms is int: 1668 | p.timers.push((getMonoTime() + initDuration(milliseconds = ms), retFuture)) 1669 | elif ms is float: 1670 | let ns = (ms * 1_000_000).int64 1671 | p.timers.push((getMonoTime() + initDuration(nanoseconds = ns), retFuture)) 1672 | return retFuture 1673 | 1674 | proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) = 1675 | ## Returns a future which will complete once ``fut`` completes or after 1676 | ## ``timeout`` milliseconds has elapsed. 1677 | ## 1678 | ## If ``fut`` completes first the returned future will hold true, 1679 | ## otherwise, if ``timeout`` milliseconds has elapsed first, the returned 1680 | ## future will hold false. 1681 | 1682 | var retFuture = newFuture[bool]("asyncdispatch.`withTimeout`") 1683 | var timeoutFuture = sleepAsync(timeout) 1684 | fut.callback = 1685 | proc () = 1686 | if not retFuture.finished: 1687 | if fut.failed: 1688 | retFuture.fail(fut.error) 1689 | else: 1690 | retFuture.complete(true) 1691 | timeoutFuture.callback = 1692 | proc () = 1693 | if not retFuture.finished: retFuture.complete(false) 1694 | return retFuture 1695 | 1696 | proc accept*(socket: AsyncFD, 1697 | flags = {SocketFlag.SafeDisconn}, 1698 | inheritable = defined(nimInheritHandles)): owned(Future[AsyncFD]) = 1699 | ## Accepts a new connection. Returns a future containing the client socket 1700 | ## corresponding to that connection. 1701 | ## 1702 | ## If ``inheritable`` is false (the default), the resulting client socket 1703 | ## will not be inheritable by child processes. 1704 | ## 1705 | ## The future will complete when the connection is successfully accepted. 1706 | var retFut = newFuture[AsyncFD]("accept") 1707 | var fut = acceptAddr(socket, flags, inheritable) 1708 | fut.callback = 1709 | proc (future: Future[tuple[address: string, client: AsyncFD]]) = 1710 | assert future.finished 1711 | if future.failed: 1712 | retFut.fail(future.error) 1713 | else: 1714 | retFut.complete(future.read.client) 1715 | return retFut 1716 | 1717 | proc keepAlive(x: string) = 1718 | discard "mark 'x' as escaping so that it is put into a closure for us to keep the data alive" 1719 | 1720 | proc send*(socket: AsyncFD, data: string, 1721 | flags = {SocketFlag.SafeDisconn}): owned(Future[void]) = 1722 | ## Sends ``data`` to ``socket``. The returned future will complete once all 1723 | ## data has been sent. 1724 | var retFuture = newFuture[void]("send") 1725 | if data.len > 0: 1726 | let sendFut = socket.send(unsafeAddr data[0], data.len, flags) 1727 | sendFut.callback = 1728 | proc () = 1729 | keepAlive(data) 1730 | if sendFut.failed: 1731 | retFuture.fail(sendFut.error) 1732 | else: 1733 | retFuture.complete() 1734 | else: 1735 | retFuture.complete() 1736 | 1737 | return retFuture 1738 | 1739 | # -- Await Macro 1740 | include asyncmacro 1741 | 1742 | proc readAll*(future: FutureStream[string]): owned(Future[string]) {.async.} = 1743 | ## Returns a future that will complete when all the string data from the 1744 | ## specified future stream is retrieved. 1745 | result = "" 1746 | while true: 1747 | let (hasValue, value) = await future.read() 1748 | if hasValue: 1749 | result.add(value) 1750 | else: 1751 | break 1752 | 1753 | proc callSoon(cbproc: proc () {.gcsafe.}) = 1754 | getGlobalDispatcher().callbacks.addLast(cbproc) 1755 | 1756 | proc runForever*() = 1757 | ## Begins a never ending global dispatcher poll loop. 1758 | while true: 1759 | poll() 1760 | 1761 | proc waitFor*[T](fut: Future[T]): T = 1762 | ## **Blocks** the current thread until the specified future completes. 1763 | while not fut.finished: 1764 | poll() 1765 | 1766 | fut.read 1767 | -------------------------------------------------------------------------------- /src/xio/fun.nim: -------------------------------------------------------------------------------- 1 | import windows/base/[fileapi, winbase, widestr2] 2 | 3 | 4 | var name = newWideCString("windows") 5 | # echo name 6 | 7 | 8 | let x = proc (dwErrorCode: DWORD, dwNumberOfBytesTransfered: DWORD, lpOverlapped: LPOVERLAPPED) = 9 | echo "Hello, World" 10 | 11 | let h = createFileW(name, FILE_LIST_DIRECTORY, FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, nil, 12 | OPEN_EXISTING, FILE_FLAG_OVERLAPPED or FILE_FLAG_BACKUP_SEMANTICS, x) 13 | 14 | 15 | var buffer = newString(1024) 16 | var reads: DWORD 17 | var over: OVERLAPPED 18 | 19 | 20 | import os 21 | 22 | while true: 23 | sleep(1000) 24 | echo readDirectoryChangesW(h, buffer.cstring, 25 | 1024, 0, FILE_NOTIFY_CHANGE_FILE_NAME or 26 | FILE_NOTIFY_CHANGE_DIR_NAME or 27 | FILE_NOTIFY_CHANGE_LAST_WRITE, reads, addr over, nil) 28 | 29 | echo osLastError() 30 | var buf = cast[pointer](buffer.cstring) 31 | 32 | while true: 33 | let info = cast[PFILE_NOTIFY_INFORMATION](buf) 34 | 35 | echo info == nil 36 | echo (info.NextEntryOffset, info.Action, info.FileNameLength, toString(info.FileName)) 37 | 38 | # var filename = newWideCString("".cstring, info.FileNameLength.int div 2) 39 | # for i in 0 ..< info.FileNameLength div 2: 40 | # filename[i] = info.FileName[i] 41 | 42 | echo toString(info.FileName) 43 | 44 | buf = cast[pointer](cast[ByteAddress](buf) + info.NextEntryOffset.int) 45 | 46 | if info.NextEntryOffset == 0: 47 | break 48 | 49 | if reads != 0: 50 | break -------------------------------------------------------------------------------- /src/xio/iokit.nim: -------------------------------------------------------------------------------- 1 | when defined(windows): 2 | import iokit/windows/iocp 3 | export iocp 4 | else: 5 | import iokit/linux/io_uring 6 | export io_uring 7 | -------------------------------------------------------------------------------- /src/xio/linux/inotify.nim: -------------------------------------------------------------------------------- 1 | # 2 | # 3 | # Nim's Runtime Library 4 | # (c) Copyright 2012 Dominik Picheta 5 | # 6 | # See the file "copying.txt", included in this 7 | # distribution, for details about the copyright. 8 | # 9 | 10 | # Get the platform-dependent flags. 11 | # Structure describing an inotify event. 12 | 13 | # FileHandle = cint [io] 14 | type 15 | InotifyEvent* {.pure, final, importc: "struct inotify_event", 16 | header: "".} = object ## An Inotify event. 17 | wd* {.importc: "wd".}: FileHandle ## Watch descriptor. 18 | mask* {.importc: "mask".}: uint32 ## Watch mask. 19 | cookie* {.importc: "cookie".}: uint32 ## Cookie to synchronize two events. 20 | len* {.importc: "len".}: uint32 ## Length (including NULs) of name. 21 | name* {.importc: "name".}: char ## Name. 22 | 23 | # Supported events suitable for MASK parameter of INOTIFY_ADD_WATCH. 24 | const 25 | IN_ACCESS* = 0x00000001 ## File was accessed. 26 | IN_MODIFY* = 0x00000002 ## File was modified. 27 | IN_ATTRIB* = 0x00000004 ## Metadata changed. 28 | IN_CLOSE_WRITE* = 0x00000008 ## Writtable file was closed. 29 | IN_CLOSE_NOWRITE* = 0x00000010 ## Unwrittable file closed. 30 | IN_CLOSE* = (IN_CLOSE_WRITE or IN_CLOSE_NOWRITE) ## Close. 31 | IN_OPEN* = 0x00000020 ## File was opened. 32 | IN_MOVED_FROM* = 0x00000040 ## File was moved from X. 33 | IN_MOVED_TO* = 0x00000080 ## File was moved to Y. 34 | IN_MOVE* = (IN_MOVED_FROM or IN_MOVED_TO) ## Moves. 35 | IN_CREATE* = 0x00000100 ## Subfile was created. 36 | IN_DELETE* = 0x00000200 ## Subfile was deleted. 37 | IN_DELETE_SELF* = 0x00000400 ## Self was deleted. 38 | IN_MOVE_SELF* = 0x00000800 ## Self was moved. 39 | 40 | # Events sent by the kernel. 41 | const 42 | IN_UNMOUNT* = 0x00002000 ## Backing fs was unmounted. 43 | IN_Q_OVERFLOW* = 0x00004000 ## Event queued overflowed. 44 | IN_IGNORED* = 0x00008000 ## File was ignored. 45 | 46 | # Special flags. 47 | const 48 | IN_ONLYDIR* = 0x01000000 ## Only watch the path if it is a directory. 49 | IN_DONT_FOLLOW* = 0x02000000 ## Do not follow a sym link. 50 | IN_EXCL_UNLINK* = 0x04000000 ## Exclude events on unlinked objects. 51 | IN_MASK_ADD* = 0x20000000 ## Add to the mask of an already existing watch. 52 | IN_ISDIR* = 0x40000000 ## Event occurred against dir. 53 | IN_ONESHOT* = 0x80000000 ## Only send event once. 54 | 55 | # All events which a program can wait on. 56 | const 57 | IN_ALL_EVENTS* = (IN_ACCESS or IN_MODIFY or IN_ATTRIB or IN_CLOSE_WRITE or 58 | IN_CLOSE_NOWRITE or IN_OPEN or IN_MOVED_FROM or IN_MOVED_TO or 59 | IN_CREATE or IN_DELETE or IN_DELETE_SELF or IN_MOVE_SELF) 60 | 61 | 62 | proc inotify_init*(): FileHandle {.cdecl, importc: "inotify_init", 63 | header: "".} 64 | ## Create and initialize inotify instance. 65 | 66 | proc inotify_init1*(flags: cint): FileHandle {.cdecl, importc: "inotify_init1", 67 | header: "".} 68 | ## Create and initialize inotify instance. 69 | 70 | proc inotify_add_watch*(fd: cint, name: cstring, mask: uint32): cint {.cdecl, 71 | importc: "inotify_add_watch", header: "".} 72 | ## Add watch of object NAME to inotify instance FD. Notify about events specified by MASK. 73 | 74 | proc inotify_rm_watch*(fd: cint, wd: cint): cint {.cdecl, 75 | importc: "inotify_rm_watch", header: "".} 76 | ## Remove the watch specified by WD from the inotify instance FD. 77 | -------------------------------------------------------------------------------- /src/xio/linux/io_uring.nim: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ringabout/xio/73321c2c163e7eebbee1b5774ccde8a74ef6fdbf/src/xio/linux/io_uring.nim -------------------------------------------------------------------------------- /src/xio/linux/test.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Inode based directory notification for Linux 3 | * 4 | * Copyright (C) 2005 John McCutchan 5 | */ 6 | 7 | #ifndef _LINUX_INOTIFY_H 8 | #define _LINUX_INOTIFY_H 9 | 10 | /* For O_CLOEXEC and O_NONBLOCK */ 11 | #include 12 | #include 13 | 14 | /* 15 | * struct inotify_event - structure read from the inotify device for each event 16 | * 17 | * When you are watching a directory, you will receive the filename for events 18 | * such as IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE, ..., relative to the wd. 19 | */ 20 | struct inotify_event { 21 | __s32 wd; /* watch descriptor */ 22 | __u32 mask; /* watch mask */ 23 | __u32 cookie; /* cookie to synchronize two events */ 24 | __u32 len; /* length (including nulls) of name */ 25 | char name[0]; /* stub for possible name */ 26 | }; 27 | 28 | /* the following are legal, implemented events that user-space can watch for */ 29 | #define IN_ACCESS 0x00000001 /* File was accessed */ 30 | #define IN_MODIFY 0x00000002 /* File was modified */ 31 | #define IN_ATTRIB 0x00000004 /* Metadata changed */ 32 | #define IN_CLOSE_WRITE 0x00000008 /* Writtable file was closed */ 33 | #define IN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */ 34 | #define IN_OPEN 0x00000020 /* File was opened */ 35 | #define IN_MOVED_FROM 0x00000040 /* File was moved from X */ 36 | #define IN_MOVED_TO 0x00000080 /* File was moved to Y */ 37 | #define IN_CREATE 0x00000100 /* Subfile was created */ 38 | #define IN_DELETE 0x00000200 /* Subfile was deleted */ 39 | #define IN_DELETE_SELF 0x00000400 /* Self was deleted */ 40 | #define IN_MOVE_SELF 0x00000800 /* Self was moved */ 41 | 42 | /* the following are legal events. they are sent as needed to any watch */ 43 | #define IN_UNMOUNT 0x00002000 /* Backing fs was unmounted */ 44 | #define IN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ 45 | #define IN_IGNORED 0x00008000 /* File was ignored */ 46 | 47 | /* helper events */ 48 | #define IN_CLOSE (IN_CLOSE_WRITE | IN_CLOSE_NOWRITE) /* close */ 49 | #define IN_MOVE (IN_MOVED_FROM | IN_MOVED_TO) /* moves */ 50 | 51 | /* special flags */ 52 | #define IN_ONLYDIR 0x01000000 /* only watch the path if it is a directory */ 53 | #define IN_DONT_FOLLOW 0x02000000 /* don't follow a sym link */ 54 | #define IN_MASK_ADD 0x20000000 /* add to the mask of an already existing watch */ 55 | #define IN_ISDIR 0x40000000 /* event occurred against dir */ 56 | #define IN_ONESHOT 0x80000000 /* only send event once */ 57 | 58 | /* 59 | * All of the events - we build the list by hand so that we can add flags in 60 | * the future and not break backward compatibility. Apps will get only the 61 | * events that they originally wanted. Be sure to add new events here! 62 | */ 63 | #define IN_ALL_EVENTS (IN_ACCESS | IN_MODIFY | IN_ATTRIB | IN_CLOSE_WRITE | \ 64 | IN_CLOSE_NOWRITE | IN_OPEN | IN_MOVED_FROM | \ 65 | IN_MOVED_TO | IN_DELETE | IN_CREATE | IN_DELETE_SELF | \ 66 | IN_MOVE_SELF) 67 | 68 | /* Flags for sys_inotify_init1. */ 69 | #define IN_CLOEXEC O_CLOEXEC 70 | #define IN_NONBLOCK O_NONBLOCK 71 | 72 | #ifdef __KERNEL__ 73 | 74 | #include 75 | #include 76 | 77 | /* 78 | * struct inotify_watch - represents a watch request on a specific inode 79 | * 80 | * h_list is protected by ih->mutex of the associated inotify_handle. 81 | * i_list, mask are protected by inode->inotify_mutex of the associated inode. 82 | * ih, inode, and wd are never written to once the watch is created. 83 | * 84 | * Callers must use the established inotify interfaces to access inotify_watch 85 | * contents. The content of this structure is private to the inotify 86 | * implementation. 87 | */ 88 | struct inotify_watch { 89 | struct list_head h_list; /* entry in inotify_handle's list */ 90 | struct list_head i_list; /* entry in inode's list */ 91 | atomic_t count; /* reference count */ 92 | struct inotify_handle *ih; /* associated inotify handle */ 93 | struct inode *inode; /* associated inode */ 94 | __s32 wd; /* watch descriptor */ 95 | __u32 mask; /* event mask for this watch */ 96 | }; 97 | 98 | struct inotify_operations { 99 | void (*handle_event)(struct inotify_watch *, u32, u32, u32, 100 | const char *, struct inode *); 101 | void (*destroy_watch)(struct inotify_watch *); 102 | }; 103 | 104 | #ifdef CONFIG_INOTIFY 105 | 106 | /* Kernel API for producing events */ 107 | 108 | extern void inotify_d_instantiate(struct dentry *, struct inode *); 109 | extern void inotify_d_move(struct dentry *); 110 | extern void inotify_inode_queue_event(struct inode *, __u32, __u32, 111 | const char *, struct inode *); 112 | extern void inotify_dentry_parent_queue_event(struct dentry *, __u32, __u32, 113 | const char *); 114 | extern void inotify_unmount_inodes(struct list_head *); 115 | extern void inotify_inode_is_dead(struct inode *); 116 | extern u32 inotify_get_cookie(void); 117 | 118 | /* Kernel Consumer API */ 119 | 120 | extern struct inotify_handle *inotify_init(const struct inotify_operations *); 121 | extern void inotify_init_watch(struct inotify_watch *); 122 | extern void inotify_destroy(struct inotify_handle *); 123 | extern __s32 inotify_find_watch(struct inotify_handle *, struct inode *, 124 | struct inotify_watch **); 125 | extern __s32 inotify_find_update_watch(struct inotify_handle *, struct inode *, 126 | u32); 127 | extern __s32 inotify_add_watch(struct inotify_handle *, struct inotify_watch *, 128 | struct inode *, __u32); 129 | extern __s32 inotify_clone_watch(struct inotify_watch *, struct inotify_watch *); 130 | extern void inotify_evict_watch(struct inotify_watch *); 131 | extern int inotify_rm_watch(struct inotify_handle *, struct inotify_watch *); 132 | extern int inotify_rm_wd(struct inotify_handle *, __u32); 133 | extern void inotify_remove_watch_locked(struct inotify_handle *, 134 | struct inotify_watch *); 135 | extern void get_inotify_watch(struct inotify_watch *); 136 | extern void put_inotify_watch(struct inotify_watch *); 137 | extern int pin_inotify_watch(struct inotify_watch *); 138 | extern void unpin_inotify_watch(struct inotify_watch *); 139 | 140 | #else 141 | 142 | static inline void inotify_d_instantiate(struct dentry *dentry, 143 | struct inode *inode) 144 | { 145 | } 146 | 147 | static inline void inotify_d_move(struct dentry *dentry) 148 | { 149 | } 150 | 151 | static inline void inotify_inode_queue_event(struct inode *inode, 152 | __u32 mask, __u32 cookie, 153 | const char *filename, 154 | struct inode *n_inode) 155 | { 156 | } 157 | 158 | static inline void inotify_dentry_parent_queue_event(struct dentry *dentry, 159 | __u32 mask, __u32 cookie, 160 | const char *filename) 161 | { 162 | } 163 | 164 | static inline void inotify_unmount_inodes(struct list_head *list) 165 | { 166 | } 167 | 168 | static inline void inotify_inode_is_dead(struct inode *inode) 169 | { 170 | } 171 | 172 | static inline u32 inotify_get_cookie(void) 173 | { 174 | return 0; 175 | } 176 | 177 | static inline struct inotify_handle *inotify_init(const struct inotify_operations *ops) 178 | { 179 | return ERR_PTR(-EOPNOTSUPP); 180 | } 181 | 182 | static inline void inotify_init_watch(struct inotify_watch *watch) 183 | { 184 | } 185 | 186 | static inline void inotify_destroy(struct inotify_handle *ih) 187 | { 188 | } 189 | 190 | static inline __s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode, 191 | struct inotify_watch **watchp) 192 | { 193 | return -EOPNOTSUPP; 194 | } 195 | 196 | static inline __s32 inotify_find_update_watch(struct inotify_handle *ih, 197 | struct inode *inode, u32 mask) 198 | { 199 | return -EOPNOTSUPP; 200 | } 201 | 202 | static inline __s32 inotify_add_watch(struct inotify_handle *ih, 203 | struct inotify_watch *watch, 204 | struct inode *inode, __u32 mask) 205 | { 206 | return -EOPNOTSUPP; 207 | } 208 | 209 | static inline int inotify_rm_watch(struct inotify_handle *ih, 210 | struct inotify_watch *watch) 211 | { 212 | return -EOPNOTSUPP; 213 | } 214 | 215 | static inline int inotify_rm_wd(struct inotify_handle *ih, __u32 wd) 216 | { 217 | return -EOPNOTSUPP; 218 | } 219 | 220 | static inline void inotify_remove_watch_locked(struct inotify_handle *ih, 221 | struct inotify_watch *watch) 222 | { 223 | } 224 | 225 | static inline void get_inotify_watch(struct inotify_watch *watch) 226 | { 227 | } 228 | 229 | static inline void put_inotify_watch(struct inotify_watch *watch) 230 | { 231 | } 232 | 233 | extern inline int pin_inotify_watch(struct inotify_watch *watch) 234 | { 235 | return 0; 236 | } 237 | 238 | extern inline void unpin_inotify_watch(struct inotify_watch *watch) 239 | { 240 | } 241 | 242 | #endif /* CONFIG_INOTIFY */ 243 | 244 | #endif /* __KERNEL __ */ 245 | 246 | #endif /* _LINUX_INOTIFY_H */ -------------------------------------------------------------------------------- /src/xio/macos/coreservices/coreservices.nim: -------------------------------------------------------------------------------- 1 | {.pragma: coreservices, header: "CoreServices/CoreServices.h".} 2 | 3 | 4 | type 5 | CFAllocatorRef* {.coreservices, importc: "struct CFAllocatorRef"} = ptr object 6 | ConstFSEventStreamRef* {.coreservices, importc: "struct ConstFSEventStreamRef"} = ptr object 7 | size_t* {.coreservices, importc: "size_t"} = culong 8 | FSEventStreamEventFlags* {.coreservices, importc: "FSEventStreamEventFlags"} = uint32 9 | FSEventStreamEventId* {.coreservices, importc: "FSEventStreamEventId"} = uint64 10 | FSEventStreamContext* {.coreservices, importc: "struct FSEventStreamContext".} = object 11 | CFArrayRef* {.coreservices, importc: "struct CFArrayRef".} = object 12 | CFTimeInterval* {.coreservices, importc: "CFTimeInterval".} = float 13 | FSEventStreamCreateFlags* {.coreservices, importc: "FSEventStreamCreateFlags".} = uint32 14 | FSEventStreamRef* {.coreservices, importc: "struct FSEventStreamRef"} = ptr object 15 | 16 | FSEventStreamCallback* = proc ( 17 | streamRef: ConstFSEventStreamRef, 18 | clientCallBackInfo: pointer, 19 | numEvents: size_t, 20 | eventPaths: pointer, 21 | eventFlags: ptr FSEventStreamEventFlags, 22 | eventIds: ptr FSEventStreamEventId 23 | ) 24 | 25 | proc CFSTR*(str:cstring): cstring {.coreservices, importc:"CFSTR".} 26 | 27 | proc FSEventStreamCreate*( 28 | allocator: CFAllocatorRef, 29 | callback: FSEventStreamCallback, 30 | context: ptr FSEventStreamContext, 31 | pathsToWatch: CFArrayRef, 32 | sinceWhen: FSEventStreamEventId, 33 | latency: CFTimeInterval, 34 | flags: FSEventStreamCreateFlags 35 | ): FSEventStreamRef {.coreservices, importc: "FSEventStreamCreate".} 36 | -------------------------------------------------------------------------------- /src/xio/notes.md: -------------------------------------------------------------------------------- 1 | # Nim 的异步 2 | 3 | ```nim 4 | import asyncdispatch 5 | 6 | 7 | proc test() {.async.} = 8 | await sleepAsync(100) 9 | 10 | proc hello() {.async.} = 11 | let a = 12 12 | echo a 13 | await test() 14 | ``` 15 | 16 | Nim proc -> Nim iter 17 | 18 | ```nim 19 | proc hello() = 20 | var ret: FutureBase 21 | iterator helloIter() {.closure.} = 22 | let a = 12 23 | echo a 24 | discard 25 | var internalTmpFuture: FutureBase = test() 26 | yield internalTmpFuture 27 | read(cast[type(test())](internalTmpFuture)) 28 | 29 | complete(ret) 30 | 31 | proc helloRegister() = 32 | addCallBack(hello) 33 | 34 | 35 | helloRegister() 36 | return ret 37 | ``` 38 | 39 | Nim 使用定时器,每隔一段时间,从全局 dispatcher 中取出一个 Future, 如果完成,就调用 callback,如果未完成再将 `Future - callback` 重新加到(定时器数值调整) heapqueue 中,如果闭包迭代器已完成,则 do nothing。每次调用 callback,将执行一次闭包迭代器,该操作会改变闭包中的 Future 变量。 40 | 41 | ```nim 42 | proc hello(): callback = 43 | var state: 0 44 | proc stateMachine() = 45 | case state 46 | of 0: 47 | doSomething() 48 | state = 1 49 | of 1: 50 | doSomething() 51 | state = 2 52 | of 2: 53 | doSomething() 54 | state = 3 55 | else: 56 | discard 57 | 58 | proc hello() = 59 | var state: Future 60 | proc stateMachine() = 61 | if state == empty: 62 | return 63 | 64 | case state.finished 65 | of true: 66 | state = getNextState() 67 | addCallback(state, stateMachine) 68 | of false: 69 | addCallback(state, stateMachine) 70 | ``` 71 | 72 | 73 | ```nim 74 | import asyncdispatch, macros 75 | 76 | 77 | expandMacros: 78 | 79 | proc test(): Future[int] {.async.} = 80 | echo "htttt" 81 | return 12 82 | 83 | proc hello(): Future[int] {.async.} = 84 | var fet = newFuture[void]("uuu") 85 | if true: 86 | fet.complete() 87 | yield fet 88 | 89 | discard await test() 90 | let x = await test() 91 | echo x 92 | return 12 93 | 94 | discard waitFor hello() 95 | ``` 96 | 97 | 98 | ```nim 99 | proc test(): Future[int] = 100 | template await(f`gensym17972021: typed): untyped {.used.} = 101 | static : 102 | error "await expects Future[T], got " & $typeof(f`gensym17972021) 103 | 104 | template await[T](f`gensym17972022: Future[T]): auto {.used.} = 105 | var internalTmpFuture`gensym17972023_17975018: FutureBase = f`gensym17972022 106 | yield internalTmpFuture`gensym17972023_17975018 107 | (cast[type(f`gensym17972022)](internalTmpFuture`gensym17972023_17975018)).read() 108 | 109 | var retFuture_17972014 = newFuture("test") 110 | iterator testIter_17972015(): owned(FutureBase) {.closure.} = 111 | {.push, warning[resultshadowed]: false.} 112 | var result: int 113 | {.pop.} 114 | block: 115 | echo ["htttt"] 116 | complete(retFuture_17972014, 12) 117 | return nil 118 | complete(retFuture_17972014, result) 119 | 120 | let retFutUnown`gensym17972017 = retFuture_17972014 121 | var nameIterVar`gensym17972018 = testIter_17972015 122 | proc testNimAsyncContinue_17972016() {.closure.} = 123 | try: 124 | if not finished(nameIterVar`gensym17972018): 125 | var next`gensym17972019 = nameIterVar`gensym17972018() 126 | while not isNil(next`gensym17972019) and finished(next`gensym17972019): 127 | next`gensym17972019 = nameIterVar`gensym17972018() 128 | if finished(nameIterVar`gensym17972018): 129 | break 130 | if next`gensym17972019 == nil: 131 | if not finished(retFutUnown`gensym17972017): 132 | let msg`gensym17972020 = "Async procedure ($1) yielded `nil`, are you await\'ing a `nil` Future?" 133 | raise 134 | (ref AssertionDefect)(msg: msg`gensym17972020 % "test", parent: nil) 135 | else: 136 | {.gcsafe.}: 137 | {.push, hint[ConvFromXtoItselfNotNeeded]: false.} 138 | addCallback(next`gensym17972019, 139 | cast[proc () {.closure, gcsafe.}](testNimAsyncContinue_17972016)) 140 | {.pop.} 141 | except: 142 | if finished(retFutUnown`gensym17972017): 143 | raise 144 | else: 145 | fail(retFutUnown`gensym17972017, getCurrentException()) 146 | 147 | testNimAsyncContinue_17972016() 148 | return retFuture_17972014 149 | 150 | proc hello(): Future[int] = 151 | template await(f`gensym17995071: typed): untyped {.used.} = 152 | static : 153 | error "await expects Future[T], got " & $typeof(f`gensym17995071) 154 | 155 | template await[T](f`gensym17995072: Future[T]): auto {.used.} = 156 | var internalTmpFuture`gensym17995073_18000018: FutureBase = f`gensym17995072 157 | yield internalTmpFuture`gensym17995073_18000018 158 | (cast[type(f`gensym17995072)](internalTmpFuture`gensym17995073_18000018)).read() 159 | 160 | var retFuture_17995064 = newFuture("hello") 161 | iterator helloIter_17995065(): owned(FutureBase) {.closure.} = 162 | {.push, warning[resultshadowed]: false.} 163 | var result: int 164 | {.pop.} 165 | block: 166 | var fet = newFuture("uuu") 167 | if true: 168 | complete(fet) 169 | yield fet 170 | discard 171 | var internalTmpFuture`gensym17995073`gensym18000079: FutureBase = test() 172 | yield internalTmpFuture`gensym17995073`gensym18000079 173 | read(cast[type(test())](internalTmpFuture`gensym17995073`gensym18000079)) 174 | let x = 175 | var internalTmpFuture`gensym17995073`gensym18015016: FutureBase = test() 176 | yield internalTmpFuture`gensym17995073`gensym18015016 177 | read(cast[type(test())](internalTmpFuture`gensym17995073`gensym18015016)) 178 | echo [x] 179 | complete(retFuture_17995064, 12) 180 | return nil 181 | complete(retFuture_17995064, result) 182 | 183 | let retFutUnown`gensym17995067 = retFuture_17995064 184 | var nameIterVar`gensym17995068 = helloIter_17995065 185 | proc helloNimAsyncContinue_17995066() {.closure.} = 186 | try: 187 | if not finished(nameIterVar`gensym17995068): 188 | var next`gensym17995069 = nameIterVar`gensym17995068() 189 | while not isNil(next`gensym17995069) and finished(next`gensym17995069): 190 | next`gensym17995069 = nameIterVar`gensym17995068() 191 | if finished(nameIterVar`gensym17995068): 192 | break 193 | if next`gensym17995069 == nil: 194 | if not finished(retFutUnown`gensym17995067): 195 | let msg`gensym17995070 = "Async procedure ($1) yielded `nil`, are you await\'ing a `nil` Future?" 196 | raise 197 | (ref AssertionDefect)(msg: msg`gensym17995070 % "hello", parent: nil) 198 | else: 199 | {.gcsafe.}: 200 | {.push, hint[ConvFromXtoItselfNotNeeded]: false.} 201 | addCallback(next`gensym17995069, 202 | cast[proc () {.closure, gcsafe.}](helloNimAsyncContinue_17995066)) 203 | {.pop.} 204 | except: 205 | if finished(retFutUnown`gensym17995067): 206 | raise 207 | else: 208 | fail(retFutUnown`gensym17995067, getCurrentException()) 209 | 210 | helloNimAsyncContinue_17995066() 211 | return retFuture_17995064 212 | 213 | discard waitFor hello() 214 | ``` 215 | 216 | 217 | ```nim 218 | proc hello() = 219 | var ret: FutureBase 220 | var state = 0 221 | 222 | proc helloIter() {.closure.} = 223 | case state 224 | of 0: 225 | let f1 = sleepAsync(12) 226 | if not f1.finished: 227 | addCallback(f1, helloIter) 228 | else: 229 | state = 1 230 | of 1: 231 | let f2 = sleepAsync(12) 232 | if not f2.finished: 233 | addCallback(f1, helloIter) 234 | else: 235 | state = 2 236 | else: 237 | complete(ret, 5) 238 | ``` 239 | 240 | Nim 的 async 可以同 threadpool 一同使用,然而 threadpool 是阻塞的。 241 | 242 | ```nim 243 | async with worker: 244 | worker.spawn(task()) 245 | worker.spawn(task()) 246 | ``` 247 | -------------------------------------------------------------------------------- /src/xio/play.nim: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import compiler/parser 4 | 5 | echo expandFilename("windows") -------------------------------------------------------------------------------- /src/xio/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #pragma comment(lib, "ws2_32.lib") 4 | 5 | typedef struct 6 | { 7 | WSAOVERLAPPED Overlapped; 8 | SOCKET Socket; 9 | WSABUF wsaBuf; 10 | char Buffer[1024]; 11 | DWORD BytesSent; 12 | DWORD BytesToSend; 13 | } PER_IO_DATA, *LPPER_IO_DATA; 14 | 15 | static DWORD WINAPI ServerWorkerThread(LPVOID lpParameter) 16 | { 17 | HANDLE hCompletionPort = (HANDLE)lpParameter; 18 | DWORD NumBytesSent = 0; 19 | ULONG CompletionKey; 20 | LPPER_IO_DATA PerIoData; 21 | 22 | while (GetQueuedCompletionStatus(hCompletionPort, &NumBytesSent, &CompletionKey, (LPOVERLAPPED *)&PerIoData, INFINITE)) 23 | { 24 | if (!PerIoData) 25 | continue; 26 | 27 | if (NumBytesSent == 0) 28 | { 29 | std::cout << "Client disconnected!\r\n\r\n"; 30 | } 31 | else 32 | { 33 | PerIoData->BytesSent += NumBytesSent; 34 | if (PerIoData->BytesSent < PerIoData->BytesToSend) 35 | { 36 | PerIoData->wsaBuf.buf = &(PerIoData->Buffer[PerIoData->BytesSent]); 37 | PerIoData->wsaBuf.len = (PerIoData->BytesToSend - PerIoData->BytesSent); 38 | } 39 | else 40 | { 41 | PerIoData->wsaBuf.buf = PerIoData->Buffer; 42 | PerIoData->wsaBuf.len = strlen(PerIoData->Buffer); 43 | PerIoData->BytesSent = 0; 44 | PerIoData->BytesToSend = PerIoData->wsaBuf.len; 45 | } 46 | 47 | if (WSASend(PerIoData->Socket, &(PerIoData->wsaBuf), 1, &NumBytesSent, 0, &(PerIoData->Overlapped), NULL) == 0) 48 | continue; 49 | 50 | if (WSAGetLastError() == WSA_IO_PENDING) 51 | continue; 52 | } 53 | 54 | closesocket(PerIoData->Socket); 55 | delete PerIoData; 56 | } 57 | 58 | return 0; 59 | } 60 | 61 | int main() 62 | { 63 | WSADATA WsaDat; 64 | if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != 0) 65 | return 0; 66 | 67 | HANDLE hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); 68 | if (!hCompletionPort) 69 | return 0; 70 | 71 | SYSTEM_INFO systemInfo; 72 | GetSystemInfo(&systemInfo); 73 | 74 | for (DWORD i = 0; i < systemInfo.dwNumberOfProcessors; ++i) 75 | { 76 | HANDLE hThread = CreateThread(NULL, 0, ServerWorkerThread, hCompletionPort, 0, NULL); 77 | CloseHandle(hThread); 78 | } 79 | 80 | SOCKET listenSocket = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, WSA_FLAG_OVERLAPPED); 81 | if (listenSocket == INVALID_SOCKET) 82 | return 0; 83 | 84 | SOCKADDR_IN server; 85 | ZeroMemory(&server, sizeof(server)); 86 | server.sin_family = AF_INET; 87 | server.sin_addr.s_addr = INADDR_ANY; 88 | server.sin_port = htons(8888); 89 | 90 | if (bind(listenSocket, (SOCKADDR *)(&server), sizeof(server)) != 0) 91 | return 0; 92 | 93 | if (listen(listenSocket, 1) != 0) 94 | return 0; 95 | 96 | std::cout << "Waiting for incoming connection...\r\n"; 97 | 98 | SOCKET acceptSocket; 99 | do 100 | { 101 | sockaddr_in saClient; 102 | int nClientSize = sizeof(saClient); 103 | acceptSocket = WSAAccept(listenSocket, (SOCKADDR *)&saClient, &nClientSize, NULL, NULL); 104 | } while (acceptSocket == INVALID_SOCKET); 105 | 106 | std::cout << "Client connected!\r\n\r\n"; 107 | 108 | CreateIoCompletionPort((HANDLE)acceptSocket, hCompletionPort, 0, 0); 109 | 110 | LPPER_IO_DATA pPerIoData = new PER_IO_DATA; 111 | ZeroMemory(pPerIoData, sizeof(PER_IO_DATA)); 112 | 113 | strcpy(pPerIoData->Buffer, "Welcome to the server!\r\n"); 114 | 115 | pPerIoData->Overlapped.hEvent = WSACreateEvent(); 116 | pPerIoData->Socket = acceptSocket; 117 | pPerIoData->wsaBuf.buf = pPerIoData->Buffer; 118 | pPerIoData->wsaBuf.len = strlen(pPerIoData->Buffer); 119 | pPerIoData->BytesToSend = pPerIoData->wsaBuf.len; 120 | 121 | DWORD dwNumSent; 122 | if (WSASend(acceptSocket, &(pPerIoData->wsaBuf), 1, &dwNumSent, 0, &(pPerIoData->Overlapped), NULL) == SOCKET_ERROR) 123 | { 124 | if (WSAGetLastError() != WSA_IO_PENDING) 125 | { 126 | delete pPerIoData; 127 | return 0; 128 | } 129 | } 130 | 131 | while (TRUE) 132 | Sleep(1000); 133 | 134 | shutdown(acceptSocket, SD_BOTH); 135 | closesocket(acceptSocket); 136 | 137 | WSACleanup(); 138 | return 0; 139 | } 140 | -------------------------------------------------------------------------------- /src/xio/windows/base/basetsd.nim: -------------------------------------------------------------------------------- 1 | type 2 | uchar* {.importc: "unsigned char", nodecl.} = uint8 3 | POINTER_64_INT* = uint 4 | INT8* = cschar 5 | PINT8* = ptr INT8 6 | INT16* = cshort 7 | PINT16* = ptr INT16 8 | INT32* = cint 9 | PINT32* = ptr INT32 10 | INT64* = int64 11 | PINT64* = ptr INT64 12 | UINT8* = uchar 13 | PUINT8* = ptr UINT8 14 | UINT16* = cushort 15 | PUINT16* = ptr UINT16 16 | UINT32* = cuint 17 | PUINT32* = ptr UINT32 18 | UINT64* = uint64 19 | PUINT64* = ptr UINT64 20 | LONG32* = cint 21 | PLONG32* = ptr LONG32 22 | ULONG32* = cuint 23 | PULONG32* = ptr ULONG32 24 | DWORD32* = cuint 25 | PDWORD32* = ptr DWORD32 26 | INT_PTR* = int 27 | PINT_PTR* = ptr INT_PTR 28 | UINT_PTR* = uint 29 | PUINT_PTR* = ptr UINT_PTR 30 | LONG_PTR* = int 31 | PLONG_PTR* = ptr LONG_PTR 32 | ULONG_PTR* = uint 33 | PULONG_PTR* = ptr ULONG_PTR 34 | SHANDLE_PTR* = int 35 | HANDLE_PTR* = uint 36 | 37 | 38 | SIZE_T* = ULONG_PTR 39 | PSIZE_T* = ptr SIZE_T 40 | SSIZE_T* = LONG_PTR 41 | PSSIZE_T* = ptr SSIZE_T 42 | DWORD_PTR* = ULONG_PTR 43 | PDWORD_PTR* = ptr DWORD_PTR 44 | LONG64* = int64 45 | PLONG64* = ptr LONG64 46 | ULONG64* = uint64 47 | PULONG64* = ptr ULONG64 48 | DWORD64* = uint64 49 | PDWORD64* = ptr DWORD64 50 | KAFFINITY* = ULONG_PTR 51 | PKAFFINITY* = ptr KAFFINITY 52 | 53 | when sizeof(int) == 8: 54 | type 55 | UHALF_PTR* = cuint 56 | PUHALF_PTR* = ptr UHALF_PTR 57 | HALF_PTR* = cint 58 | PHALF_PTR* = ptr HALF_PTR 59 | elif sizeof(int) == 4: 60 | type 61 | UHALF_PTR* = cushort 62 | PUHALF_PTR* = ptr UHALF_PTR 63 | HALF_PTR* = cshort 64 | PHALF_PTR* = ptr HALF_PTR 65 | -------------------------------------------------------------------------------- /src/xio/windows/base/bsdtypes.nim: -------------------------------------------------------------------------------- 1 | import basetsd 2 | 3 | type 4 | uuchar* = uchar 5 | uushort* = cushort 6 | uuint* = cuint 7 | uulong* = culong 8 | uuint64* = culonglong 9 | -------------------------------------------------------------------------------- /src/xio/windows/base/commdlg.nim: -------------------------------------------------------------------------------- 1 | import minwindef, windef, winnt, basetsd 2 | 3 | 4 | {.pragma: libComdlg32, stdcall, dynlib: "Comdlg32.dll".} 5 | 6 | 7 | type 8 | LPOFNHOOKPROC = proc (x1: HWND, x2: UINT, x3: WPARAM, x4: LPARAM): UINT_PTR {.stdcall.} 9 | 10 | OPENFILENAMEA* {.bycopy.} = object 11 | lStructSize*: DWORD 12 | hwndOwner*: HWND 13 | hInstance*: HINSTANCE 14 | lpstrFilter*: LPCSTR 15 | lpstrCustomFilter*: LPSTR 16 | nMaxCustFilter*: DWORD 17 | nFilterIndex*: DWORD 18 | lpstrFile*: LPSTR 19 | nMaxFile*: DWORD 20 | lpstrFileTitle*: LPSTR 21 | nMaxFileTitle*: DWORD 22 | lpstrInitialDir*: LPCSTR 23 | lpstrTitle*: LPCSTR 24 | flags*: DWORD 25 | nFileOffset*: WORD 26 | nFileExtension*: WORD 27 | lpstrDefExt*: LPCSTR 28 | lCustData*: LPARAM 29 | lpfnHook*: LPOFNHOOKPROC 30 | lpTemplateName*: LPCSTR 31 | # lpEditInfo*: LPEDITMENU 32 | # lpstrPrompt*: LPCSTR 33 | pvReserved*: pointer 34 | dwReserved*: DWORD 35 | flagsEx*: DWORD 36 | 37 | LPOPENFILENAMEA* = ptr OPENFILENAMEA 38 | 39 | proc getOpenFileNameA*( 40 | Arg1: LPOPENFILENAMEA 41 | ) {.libComdlg32, importc: "GetOpenFileNameA".} 42 | 43 | # proc getOpenFileNameW*( 44 | # Arg1: LPOPENFILENAMEW 45 | # ) {.libComdlg32, importc: "GetOpenFileNameA".} -------------------------------------------------------------------------------- /src/xio/windows/base/crt/rt.nim: -------------------------------------------------------------------------------- 1 | type 2 | size_t* = uint 3 | -------------------------------------------------------------------------------- /src/xio/windows/base/errhandleapi.nim: -------------------------------------------------------------------------------- 1 | import minwindef 2 | 3 | {.pragma: libKernel32, stdcall, dynlib: "Kernel32.dll".} 4 | 5 | 6 | proc getLastError*(): DWORD {.libKernel32, importc: "GetLastError".} 7 | 8 | proc setLastError*(dwErrCode: DWORD) {.libKernel32, importc: "SetLastError".} 9 | -------------------------------------------------------------------------------- /src/xio/windows/base/fileapi.nim: -------------------------------------------------------------------------------- 1 | import ntdef, winnt, minwindef, minwinbase 2 | 3 | export ntdef, winnt, minwindef, minwinbase 4 | 5 | 6 | {.pragma: libKernel32, stdcall, dynlib: "Kernel32.dll".} 7 | 8 | 9 | const 10 | CREATE_NEW* = 1 11 | CREATE_ALWAYS* = 2 12 | OPEN_EXISTING* = 3 13 | OPEN_ALWAYS* = 4 14 | TRUNCATE_EXISTING* = 5 15 | 16 | 17 | proc createFileW*( 18 | lpFileName: WCHAR, 19 | dwDesiredAccess: DWORD, 20 | dwShareMode: DWORD, 21 | lpSecurityAttributes: LPSECURITY_ATTRIBUTES, 22 | dwCreationDisposition: DWORD, 23 | dwFlagsAndAttributes: DWORD, 24 | hTemplateFile: Handle 25 | ): Handle {.libKernel32, importc: "CreateFileW".} 26 | 27 | proc createFileA*( 28 | lpFileName: cstring, 29 | dwDesiredAccess: DWORD, 30 | dwShareMode: DWORD, 31 | lpSecurityAttributes: LPSECURITY_ATTRIBUTES, 32 | dwCreationDisposition: DWORD, 33 | dwFlagsAndAttributes: DWORD, 34 | hTemplateFile: Handle 35 | ): Handle {.libKernel32, importc: "CreateFileA".} 36 | 37 | proc getFileAttributesA*( 38 | lpFileName: LPCSTR 39 | ): DWORD {.libKernel32, importc: "GetFileAttributesA".} 40 | 41 | proc getFileAttributesW*( 42 | lpFileName: LPCWSTR 43 | ): DWORD {.libKernel32, importc: "GetFileAttributesW".} 44 | -------------------------------------------------------------------------------- /src/xio/windows/base/handleapi.nim: -------------------------------------------------------------------------------- 1 | import ntdef, minwindef 2 | 3 | 4 | {.pragma: libKernel32, stdcall, dynlib: "Kernel32.dll".} 5 | 6 | const 7 | INVALID_HANDLE_VALUE* = cast[Handle](-1) 8 | 9 | proc closeHandle*(hObject: Handle): WINBOOL {.libKernel32, importc: "CloseHandle"} 10 | 11 | proc duplicateHandle*( 12 | hSourceProcessHandle: Handle, 13 | hSourceHandle: Handle, 14 | hTargetProcessHandle: Handle, 15 | lpTargetHandle: LPHANDLE, 16 | dwDesiredAccess: DWORD, 17 | bInheritHandle: WINBOOL, 18 | dwOptions: DWORD 19 | ): WINBOOL {.libKernel32, importc: "DuplicateHandle"} 20 | 21 | proc compareObjectHandles*( 22 | hFirstObjectHandle: Handle; 23 | hSecondObjectHandle: Handle 24 | ): WINBOOL {.libKernel32, importc: "CompareObjectHandles"} 25 | 26 | proc getHandleInformation*( 27 | hObject: Handle; lpdwFlags: LPDWORD 28 | ): WINBOOL {.libKernel32, importc: "GetHandleInformation"} 29 | 30 | proc setHandleInformation*( 31 | hObject: Handle; dwMask: DWORD; dwFlags: DWORD 32 | ): WINBOOL {.libKernel32, importc: "SetHandleInformation"} 33 | -------------------------------------------------------------------------------- /src/xio/windows/base/inaddr.nim: -------------------------------------------------------------------------------- 1 | import bsdtypes 2 | 3 | 4 | type 5 | InAddr* {.importc: "IN_ADDR", header: "inaddr.h".} = object 6 | s_addr*: uulong 7 | 8 | PinAddr* = ptr InAddr 9 | LpinAddr* = ptr InAddr 10 | -------------------------------------------------------------------------------- /src/xio/windows/base/ioapiset.nim: -------------------------------------------------------------------------------- 1 | import ntdef, minwindef, minwinbase, basetsd 2 | 3 | 4 | {.pragma: libKernel32, stdcall, dynlib: "Kernel32.dll".} 5 | 6 | 7 | proc createIoCompletionPort*( 8 | FileHandle: Handle, 9 | ExistingCompletionPort: Handle, 10 | CompletionKey: ULONG_PTR, 11 | NumberOfConcurrentThreads: DWORD 12 | ): Handle {.libKernel32, importc: "CreateIoCompletionPort".} 13 | 14 | proc getQueuedCompletionStatus*( 15 | CompletionPort: Handle, 16 | lpNumberOfBytesTransferred: LPDWORD, 17 | lpCompletionKey: PULONG_PTR, 18 | lpOverlapped: ptr LPOVERLAPPED, 19 | dwMilliseconds: DWORD 20 | ): WINBOOL {.libKernel32, importc: "GetQueuedCompletionStatus".} 21 | 22 | proc getQueuedCompletionStatusEx*( 23 | CompletionPort: Handle, 24 | lpCompletionPortEntries: LPOVERLAPPED_ENTRY, 25 | ulCount: ULONG, ulNumEntriesRemoved: PULONG, 26 | dwMilliseconds: DWORD, fAlertable: WINBOOL 27 | ): WINBOOL {.libKernel32, importc: "GetQueuedCompletionStatusEx".} 28 | 29 | 30 | proc postQueuedCompletionStatus*( 31 | CompletionPort: Handle, 32 | dwNumberOfBytesTransferred: DWORD, 33 | dwCompletionKey: ULONG_PTR, 34 | lpOverlapped: LPOVERLAPPED 35 | ): WINBOOL {.libKernel32, importc: "PostQueuedCompletionStatus".} 36 | 37 | proc deviceIoControl*( 38 | hDevice: Handle, dwIoControlCode: DWORD, lpInBuffer: LPVOID, 39 | nInBufferSize: DWORD, lpOutBuffer: LPVOID, 40 | nOutBufferSize: DWORD, lpBytesReturned: LPDWORD, 41 | lpOverlapped: LPOVERLAPPED 42 | ): WINBOOL {.libKernel32, importc: "DeviceIoControl".} 43 | 44 | proc getOverlappedResult*( 45 | hFile: Handle, lpOverlapped: LPOVERLAPPED, 46 | lpNumberOfBytesTransferred: var DWORD, bWait: WINBOOL 47 | ): WINBOOL {.libKernel32, importc: "GetOverlappedResult".} 48 | 49 | proc cancelIoEx*(hFile: Handle, 50 | lpOverlapped: LPOVERLAPPED 51 | ): WINBOOL {.libKernel32, importc: "CancelIoEx".} 52 | 53 | proc cancelIo*(hFile: Handle): WINBOOL {.libKernel32, importc: "CancelIo".} 54 | 55 | proc getOverlappedResultEx*( 56 | hFile: Handle, lpOverlapped: LPOVERLAPPED, 57 | lpNumberOfBytesTransferred: LPDWORD, 58 | dwMilliseconds: DWORD, bAlertable: WINBOOL 59 | ): WINBOOL {.libKernel32, importc: "GetOverlappedResultEx".} 60 | 61 | proc cancelSynchronousIo*(hThread: Handle): WINBOOL {.libKernel32, importc: "CancelSynchronousIo".} 62 | -------------------------------------------------------------------------------- /src/xio/windows/base/minwinbase.nim: -------------------------------------------------------------------------------- 1 | import ntdef, minwindef, basetsd 2 | 3 | 4 | type 5 | OVERLAPPED_offset* = object 6 | offset*: DWORD 7 | offsetHigh*: DWORD 8 | 9 | OVERLAPPED_union* {.union.} = object 10 | offset*: OVERLAPPED_offset 11 | p*: PVOID 12 | 13 | OVERLAPPED* = object 14 | internal*: ULONG_PTR 15 | internalHigh*: ULONG_PTR 16 | union*: OVERLAPPED_union 17 | hevent*: Handle 18 | 19 | LPOVERLAPPED* = ptr OVERLAPPED 20 | 21 | LPOVERLAPPED_COMPLETION_ROUTINE* = proc (dwErrorCode: DWORD, dwNumberOfBytesTransfered: DWORD, 22 | lpOverlapped: LPOVERLAPPED) 23 | 24 | OVERLAPPED_ENTRY* = object 25 | lpCompletionKey*: ULONG_PTR 26 | lpOverlapped*: LPOVERLAPPED 27 | internal*: ULONG_PTR 28 | dwNumberOfBytesTransferred*: DWORD 29 | 30 | LPOVERLAPPED_ENTRY* = ptr OVERLAPPED_ENTRY 31 | 32 | WSAOVERLAPPED* = object 33 | internal*: ULONG_PTR 34 | internalHigh*: ULONG_PTR 35 | offset*: DWORD 36 | offsetHigh*: DWORD 37 | hevent*: Handle 38 | 39 | LPWSAOVERLAPPED* = ptr WSAOVERLAPPED 40 | 41 | LPWSAOVERLAPPED_COMPLETION_ROUTINE* = proc (dwError: DWORD, cbTransferred: DWORD, 42 | lpOverlapped: LPWSAOVERLAPPED, dwFlags: DWORD) 43 | 44 | WSABUF* = object 45 | len*: ULONG 46 | buf*: cstring 47 | 48 | LPWSABUF* = ptr WSABUF 49 | 50 | SECURITY_ATTRIBUTES* = object 51 | nLength*: DWORD 52 | lpSecurityDescriptor*: LPVOID 53 | bInheritHandle*: WINBOOL 54 | 55 | PSECURITY_ATTRIBUTES* = ptr SECURITY_ATTRIBUTES 56 | LPSECURITY_ATTRIBUTES* = ptr SECURITY_ATTRIBUTES 57 | -------------------------------------------------------------------------------- /src/xio/windows/base/minwindef.nim: -------------------------------------------------------------------------------- 1 | import ntdef, basetsd 2 | 3 | type 4 | BYTE* = uchar 5 | WORD* = cushort 6 | DWORD* = culong 7 | FLOAT* = cfloat 8 | PFLOAT* = ptr FLOAT 9 | PBYTE* = ptr BYTE 10 | LPBYTE* = ptr BYTE 11 | PINT* = ptr cint 12 | LPINT* = ptr cint 13 | PWORD* = ptr WORD 14 | LPWORD* = ptr WORD 15 | LPLONG* = ptr DWORD # __LONG32 16 | PDWORD* = ptr DWORD 17 | LPDWORD* = ptr DWORD 18 | ULONG* = culong 19 | PULONG* = ptr ULONG 20 | 21 | USHORT* = cushort 22 | PUSHORT* = ptr USHORT 23 | UCHAR* = uchar 24 | PUCHAR* = ptr UCHAR 25 | PSZ* = cstring 26 | 27 | WINBOOL* = int32 ## if WINBOOL != 0, it succeeds which is different from posix. 28 | 29 | LPHandle* = ptr Handle 30 | 31 | UINT* = cuint 32 | WPARAM* = UINT_PTR 33 | -------------------------------------------------------------------------------- /src/xio/windows/base/ntdef.nim: -------------------------------------------------------------------------------- 1 | type 2 | Handle* = pointer 3 | HDC* = Handle 4 | HGLRC* = Handle 5 | 6 | PVOID* = pointer 7 | LPVOID* = pointer 8 | -------------------------------------------------------------------------------- /src/xio/windows/base/qos.nim: -------------------------------------------------------------------------------- 1 | import minwindef 2 | 3 | 4 | type 5 | SERVICETYPE* = ULONG 6 | 7 | FLOWSPEC* = object 8 | tokenRate*: ULONG 9 | tokenBucketSize*: ULONG 10 | peakBandwidth*: ULONG 11 | latency*: ULONG 12 | delayVariation*: ULONG 13 | serviceType*: SERVICETYPE 14 | maxSduSize*: ULONG 15 | minimumPolicedSize*: ULONG 16 | 17 | PFLOWSPEC* = ptr FLOWSPEC 18 | LPFLOWSPEC* = ptr FLOWSPEC 19 | -------------------------------------------------------------------------------- /src/xio/windows/base/sockettypes.nim: -------------------------------------------------------------------------------- 1 | import basetsd 2 | 3 | type 4 | SocketHandle* = UINT_PTR 5 | 6 | 7 | const 8 | INVALID_SOCKET* = cast[SocketHandle](-1) 9 | SOCKET_ERROR* = -1'i32 10 | -------------------------------------------------------------------------------- /src/xio/windows/base/test.c: -------------------------------------------------------------------------------- 1 | #define SM_CXSCREEN 0 2 | #define SM_CYSCREEN 1 3 | #define SM_CXVSCROLL 2 4 | #define SM_CYHSCROLL 3 5 | #define SM_CYCAPTION 4 6 | #define SM_CXBORDER 5 7 | #define SM_CYBORDER 6 8 | #define SM_CXDLGFRAME 7 9 | #define SM_CYDLGFRAME 8 10 | #define SM_CYVTHUMB 9 11 | #define SM_CXHTHUMB 10 12 | #define SM_CXICON 11 13 | #define SM_CYICON 12 14 | #define SM_CXCURSOR 13 15 | #define SM_CYCURSOR 14 16 | #define SM_CYMENU 15 17 | #define SM_CXFULLSCREEN 16 18 | #define SM_CYFULLSCREEN 17 19 | #define SM_CYKANJIWINDOW 18 20 | #define SM_MOUSEPRESENT 19 21 | #define SM_CYVSCROLL 20 22 | #define SM_CXHSCROLL 21 23 | #define SM_DEBUG 22 24 | #define SM_SWAPBUTTON 23 25 | #define SM_RESERVED1 24 26 | #define SM_RESERVED2 25 27 | #define SM_RESERVED3 26 28 | #define SM_RESERVED4 27 29 | #define SM_CXMIN 28 30 | #define SM_CYMIN 29 31 | #define SM_CXSIZE 30 32 | #define SM_CYSIZE 31 33 | #define SM_CXFRAME 32 34 | #define SM_CYFRAME 33 35 | #define SM_CXMINTRACK 34 36 | #define SM_CYMINTRACK 35 37 | #define SM_CXDOUBLECLK 36 38 | #define SM_CYDOUBLECLK 37 39 | #define SM_CXICONSPACING 38 40 | #define SM_CYICONSPACING 39 41 | #define SM_MENUDROPALIGNMENT 40 42 | #define SM_PENWINDOWS 41 43 | #define SM_DBCSENABLED 42 44 | #define SM_CMOUSEBUTTONS 43 45 | 46 | #define SM_CXFIXEDFRAME SM_CXDLGFRAME 47 | #define SM_CYFIXEDFRAME SM_CYDLGFRAME 48 | #define SM_CXSIZEFRAME SM_CXFRAME 49 | #define SM_CYSIZEFRAME SM_CYFRAME 50 | 51 | #define SM_SECURE 44 52 | #define SM_CXEDGE 45 53 | #define SM_CYEDGE 46 54 | #define SM_CXMINSPACING 47 55 | #define SM_CYMINSPACING 48 56 | #define SM_CXSMICON 49 57 | #define SM_CYSMICON 50 58 | #define SM_CYSMCAPTION 51 59 | #define SM_CXSMSIZE 52 60 | #define SM_CYSMSIZE 53 61 | #define SM_CXMENUSIZE 54 62 | #define SM_CYMENUSIZE 55 63 | #define SM_ARRANGE 56 64 | #define SM_CXMINIMIZED 57 65 | #define SM_CYMINIMIZED 58 66 | #define SM_CXMAXTRACK 59 67 | #define SM_CYMAXTRACK 60 68 | #define SM_CXMAXIMIZED 61 69 | #define SM_CYMAXIMIZED 62 70 | #define SM_NETWORK 63 71 | #define SM_CLEANBOOT 67 72 | #define SM_CXDRAG 68 73 | #define SM_CYDRAG 69 74 | #define SM_SHOWSOUNDS 70 75 | #define SM_CXMENUCHECK 71 76 | #define SM_CYMENUCHECK 72 77 | #define SM_SLOWMACHINE 73 78 | #define SM_MIDEASTENABLED 74 79 | #define SM_MOUSEWHEELPRESENT 75 80 | #define SM_XVIRTUALSCREEN 76 81 | #define SM_YVIRTUALSCREEN 77 82 | #define SM_CXVIRTUALSCREEN 78 83 | #define SM_CYVIRTUALSCREEN 79 84 | #define SM_CMONITORS 80 85 | #define SM_SAMEDISPLAYFORMAT 81 86 | #define SM_IMMENABLED 82 87 | #define SM_CXFOCUSBORDER 83 88 | #define SM_CYFOCUSBORDER 84 89 | #define SM_TABLETPC 86 90 | #define SM_MEDIACENTER 87 91 | #define SM_STARTER 88 92 | #define SM_SERVERR2 89 93 | 94 | 95 | #define SM_DIGITIZER 94 96 | #define SM_MAXIMUMTOUCHES 95 97 | 98 | 99 | 100 | #define SM_CMETRICS 97 101 | 102 | 103 | #define SM_REMOTESESSION 0x1000 104 | #define SM_SHUTTINGDOWN 0x2000 105 | #define SM_REMOTECONTROL 0x2001 106 | #define SM_CARETBLINKINGENABLED 0x2002 107 | #define SM_CONVERTIBLESLATEMODE 0x2003 108 | #define SM_SYSTEMDOCKED 0x2004 109 | 110 | 111 | 112 | 113 | 114 | #define PMB_ACTIVE 0x00000001 115 | 116 | #define MNC_IGNORE 0 117 | #define MNC_CLOSE 1 118 | #define MNC_EXECUTE 2 119 | #define MNC_SELECT 3 120 | 121 | #define MNS_NOCHECK 0x80000000 122 | #define MNS_MODELESS 0x40000000 123 | #define MNS_DRAGDROP 0x20000000 124 | #define MNS_AUTODISMISS 0x10000000 125 | #define MNS_NOTIFYBYPOS 0x08000000 126 | #define MNS_CHECKORBMP 0x04000000 127 | 128 | #define MIM_MAXHEIGHT 0x00000001 129 | #define MIM_BACKGROUND 0x00000002 130 | #define MIM_HELPID 0x00000004 131 | #define MIM_MENUDATA 0x00000008 132 | #define MIM_STYLE 0x00000010 133 | #define MIM_APPLYTOSUBMENUS 0x80000000 134 | 135 | #define MND_CONTINUE 0 136 | #define MND_ENDMENU 1 137 | 138 | #define MNGOF_TOPGAP 0x00000001 139 | #define MNGOF_BOTTOMGAP 0x00000002 140 | 141 | #define MNGO_NOINTERFACE 0x00000000 142 | #define MNGO_NOERROR 0x00000001 143 | 144 | #define MIIM_STATE 0x00000001 145 | #define MIIM_ID 0x00000002 146 | #define MIIM_SUBMENU 0x00000004 147 | #define MIIM_CHECKMARKS 0x00000008 148 | #define MIIM_TYPE 0x00000010 149 | #define MIIM_DATA 0x00000020 150 | #define MIIM_STRING 0x00000040 151 | #define MIIM_BITMAP 0x00000080 152 | #define MIIM_FTYPE 0x00000100 153 | -------------------------------------------------------------------------------- /src/xio/windows/base/test.nim: -------------------------------------------------------------------------------- 1 | const 2 | SM_CXSCREEN* = 0 3 | SM_CYSCREEN* = 1 4 | SM_CXVSCROLL* = 2 5 | SM_CYHSCROLL* = 3 6 | SM_CYCAPTION* = 4 7 | SM_CXBORDER* = 5 8 | SM_CYBORDER* = 6 9 | SM_CXDLGFRAME* = 7 10 | SM_CYDLGFRAME* = 8 11 | SM_CYVTHUMB* = 9 12 | SM_CXHTHUMB* = 10 13 | SM_CXICON* = 11 14 | SM_CYICON* = 12 15 | SM_CXCURSOR* = 13 16 | SM_CYCURSOR* = 14 17 | SM_CYMENU* = 15 18 | SM_CXFULLSCREEN* = 16 19 | SM_CYFULLSCREEN* = 17 20 | SM_CYKANJIWINDOW* = 18 21 | SM_MOUSEPRESENT* = 19 22 | SM_CYVSCROLL* = 20 23 | SM_CXHSCROLL* = 21 24 | SM_DEBUG* = 22 25 | SM_SWAPBUTTON* = 23 26 | SM_RESERVED1* = 24 27 | SM_RESERVED2* = 25 28 | SM_RESERVED3* = 26 29 | SM_RESERVED4* = 27 30 | SM_CXMIN* = 28 31 | SM_CYMIN* = 29 32 | SM_CXSIZE* = 30 33 | SM_CYSIZE* = 31 34 | SM_CXFRAME* = 32 35 | SM_CYFRAME* = 33 36 | SM_CXMINTRACK* = 34 37 | SM_CYMINTRACK* = 35 38 | SM_CXDOUBLECLK* = 36 39 | SM_CYDOUBLECLK* = 37 40 | SM_CXICONSPACING* = 38 41 | SM_CYICONSPACING* = 39 42 | SM_MENUDROPALIGNMENT* = 40 43 | SM_PENWINDOWS* = 41 44 | SM_DBCSENABLED* = 42 45 | SM_CMOUSEBUTTONS* = 43 46 | SM_CXFIXEDFRAME* = SM_CXDLGFRAME 47 | SM_CYFIXEDFRAME* = SM_CYDLGFRAME 48 | SM_CXSIZEFRAME* = SM_CXFRAME 49 | SM_CYSIZEFRAME* = SM_CYFRAME 50 | SM_SECURE* = 44 51 | SM_CXEDGE* = 45 52 | SM_CYEDGE* = 46 53 | SM_CXMINSPACING* = 47 54 | SM_CYMINSPACING* = 48 55 | SM_CXSMICON* = 49 56 | SM_CYSMICON* = 50 57 | SM_CYSMCAPTION* = 51 58 | SM_CXSMSIZE* = 52 59 | SM_CYSMSIZE* = 53 60 | SM_CXMENUSIZE* = 54 61 | SM_CYMENUSIZE* = 55 62 | SM_ARRANGE* = 56 63 | SM_CXMINIMIZED* = 57 64 | SM_CYMINIMIZED* = 58 65 | SM_CXMAXTRACK* = 59 66 | SM_CYMAXTRACK* = 60 67 | SM_CXMAXIMIZED* = 61 68 | SM_CYMAXIMIZED* = 62 69 | SM_NETWORK* = 63 70 | SM_CLEANBOOT* = 67 71 | SM_CXDRAG* = 68 72 | SM_CYDRAG* = 69 73 | SM_SHOWSOUNDS* = 70 74 | SM_CXMENUCHECK* = 71 75 | SM_CYMENUCHECK* = 72 76 | SM_SLOWMACHINE* = 73 77 | SM_MIDEASTENABLED* = 74 78 | SM_MOUSEWHEELPRESENT* = 75 79 | SM_XVIRTUALSCREEN* = 76 80 | SM_YVIRTUALSCREEN* = 77 81 | SM_CXVIRTUALSCREEN* = 78 82 | SM_CYVIRTUALSCREEN* = 79 83 | SM_CMONITORS* = 80 84 | SM_SAMEDISPLAYFORMAT* = 81 85 | SM_IMMENABLED* = 82 86 | SM_CXFOCUSBORDER* = 83 87 | SM_CYFOCUSBORDER* = 84 88 | SM_TABLETPC* = 86 89 | SM_MEDIACENTER* = 87 90 | SM_STARTER* = 88 91 | SM_SERVERR2* = 89 92 | SM_DIGITIZER* = 94 93 | SM_MAXIMUMTOUCHES* = 95 94 | SM_CMETRICS* = 97 95 | SM_REMOTESESSION* = 0x00001000 96 | SM_SHUTTINGDOWN* = 0x00002000 97 | SM_REMOTECONTROL* = 0x00002001 98 | SM_CARETBLINKINGENABLED* = 0x00002002 99 | SM_CONVERTIBLESLATEMODE* = 0x00002003 100 | SM_SYSTEMDOCKED* = 0x00002004 101 | PMB_ACTIVE* = 0x00000001 102 | MNC_IGNORE* = 0 103 | MNC_CLOSE* = 1 104 | MNC_EXECUTE* = 2 105 | MNC_SELECT* = 3 106 | MNS_NOCHECK* = 0x80000000 107 | MNS_MODELESS* = 0x40000000 108 | MNS_DRAGDROP* = 0x20000000 109 | MNS_AUTODISMISS* = 0x10000000 110 | MNS_NOTIFYBYPOS* = 0x08000000 111 | MNS_CHECKORBMP* = 0x04000000 112 | MIM_MAXHEIGHT* = 0x00000001 113 | MIM_BACKGROUND* = 0x00000002 114 | MIM_HELPID* = 0x00000004 115 | MIM_MENUDATA* = 0x00000008 116 | MIM_STYLE* = 0x00000010 117 | MIM_APPLYTOSUBMENUS* = 0x80000000 118 | MND_CONTINUE* = 0 119 | MND_ENDMENU* = 1 120 | MNGOF_TOPGAP* = 0x00000001 121 | MNGOF_BOTTOMGAP* = 0x00000002 122 | MNGO_NOINTERFACE* = 0x00000000 123 | MNGO_NOERROR* = 0x00000001 124 | MIIM_STATE* = 0x00000001 125 | MIIM_ID* = 0x00000002 126 | MIIM_SUBMENU* = 0x00000004 127 | MIIM_CHECKMARKS* = 0x00000008 128 | MIIM_TYPE* = 0x00000010 129 | MIIM_DATA* = 0x00000020 130 | MIIM_STRING* = 0x00000040 131 | MIIM_BITMAP* = 0x00000080 132 | MIIM_FTYPE* = 0x00000100 133 | -------------------------------------------------------------------------------- /src/xio/windows/base/widestr2.nim: -------------------------------------------------------------------------------- 1 | # Nim's Runtime Library 2 | # (c) Copyright 2012 Andreas Rumpf 3 | # 4 | # See the file "copying.txt", included in this 5 | # distribution, for details about the copyright. 6 | const 7 | UNI_REPLACEMENT_CHAR = Utf16Char(0xFFFD'i16) 8 | UNI_MAX_BMP = 0x0000FFFF 9 | UNI_MAX_UTF16 = 0x0010FFFF 10 | UNI_MAX_UTF32 = 0x7FFFFFFF 11 | UNI_MAX_LEGAL_UTF32 = 0x0010FFFF 12 | 13 | halfShift = 10 14 | halfBase = 0x0010000 15 | halfMask = 0x3FF 16 | 17 | UNI_SUR_HIGH_START = 0xD800 18 | UNI_SUR_HIGH_END = 0xDBFF 19 | UNI_SUR_LOW_START = 0xDC00 20 | UNI_SUR_LOW_END = 0xDFFF 21 | UNI_REPL = 0xFFFD 22 | 23 | template ones(n: untyped): untyped = ((1 shl n)-1) 24 | 25 | proc toString*(w: UncheckedArray[Utf16Char], estimate: int, replacement: int = 0xFFFD): string = 26 | result = newStringOfCap(estimate + estimate shr 2) 27 | 28 | var i = 0 29 | while w[i].int16 != 0'i16: 30 | var ch = ord(w[i]) 31 | inc i 32 | if ch >= UNI_SUR_HIGH_START and ch <= UNI_SUR_HIGH_END: 33 | # If the 16 bits following the high surrogate are in the source buffer... 34 | let ch2 = ord(w[i]) 35 | 36 | # If it's a low surrogate, convert to UTF32: 37 | if ch2 >= UNI_SUR_LOW_START and ch2 <= UNI_SUR_LOW_END: 38 | ch = (((ch and halfMask) shl halfShift) + (ch2 and halfMask)) + halfBase 39 | inc i 40 | else: 41 | #invalid UTF-16 42 | ch = replacement 43 | elif ch >= UNI_SUR_LOW_START and ch <= UNI_SUR_LOW_END: 44 | #invalid UTF-16 45 | ch = replacement 46 | 47 | if ch < 0x80: 48 | result.add chr(ch) 49 | elif ch < 0x800: 50 | result.add chr((ch shr 6) or 0xc0) 51 | result.add chr((ch and 0x3f) or 0x80) 52 | elif ch < 0x10000: 53 | result.add chr((ch shr 12) or 0xe0) 54 | result.add chr(((ch shr 6) and 0x3f) or 0x80) 55 | result.add chr((ch and 0x3f) or 0x80) 56 | elif ch <= 0x10FFFF: 57 | result.add chr((ch shr 18) or 0xf0) 58 | result.add chr(((ch shr 12) and 0x3f) or 0x80) 59 | result.add chr(((ch shr 6) and 0x3f) or 0x80) 60 | result.add chr((ch and 0x3f) or 0x80) 61 | else: 62 | # replacement char(in case user give very large number): 63 | result.add chr(0xFFFD shr 12 or 0b1110_0000) 64 | result.add chr(0xFFFD shr 6 and ones(6) or 0b10_0000_00) 65 | result.add chr(0xFFFD and ones(6) or 0b10_0000_00) 66 | 67 | proc toString*(s: UncheckedArray[Utf16Char]): string = 68 | result = toString(s, 80) 69 | -------------------------------------------------------------------------------- /src/xio/windows/base/winbase.nim: -------------------------------------------------------------------------------- 1 | import ntdef, minwindef, minwinbase, winnt 2 | 3 | export ntdef, minwindef, minwinbase, winnt 4 | 5 | 6 | {.pragma: libKernel32, stdcall, dynlib: "Kernel32.dll".} 7 | 8 | 9 | const 10 | FILE_FLAG_WRITE_THROUGH* = 0x80000000 11 | FILE_FLAG_OVERLAPPED* = 0x40000000 12 | FILE_FLAG_NO_BUFFERING* = 0x20000000 13 | FILE_FLAG_RANDOM_ACCESS* = 0x10000000 14 | FILE_FLAG_SEQUENTIAL_SCAN* = 0x08000000 15 | FILE_FLAG_DELETE_ON_CLOSE* = 0x04000000 16 | FILE_FLAG_BACKUP_SEMANTICS* = 0x02000000 17 | FILE_FLAG_POSIX_SEMANTICS* = 0x01000000 18 | FILE_FLAG_SESSION_AWARE* = 0x00800000 19 | FILE_FLAG_OPEN_REPARSE_POINT* = 0x00200000 20 | FILE_FLAG_OPEN_NO_RECALL* = 0x00100000 21 | FILE_FLAG_FIRST_PIPE_INSTANCE* = 0x00080000 22 | 23 | 24 | proc readDirectoryChangesW*( 25 | hDirectory: Handle, 26 | lpBuffer: LPVOID, 27 | nBufferLength: DWORD, 28 | bWatchSubtree: WINBOOL, 29 | dwNotifyFilter: DWORD, 30 | lpBytesReturned: var DWORD, 31 | lpOverlapped: LPOVERLAPPED, 32 | lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE 33 | ): WINBOOL {.libKernel32, importc: "ReadDirectoryChangesW".} 34 | 35 | 36 | proc getComputerNameA*( 37 | lpBuffer: LPSTR, 38 | nSize: LPDWORD 39 | ): WINBOOL {.importc: "GetComputerNameA", libKernel32.} 40 | 41 | proc getComputerNameW*( 42 | lpBuffer: LPWSTR, 43 | nSize: LPDWORD 44 | ): WINBOOL {.importc: "GetComputerNameW", libKernel32.} 45 | -------------------------------------------------------------------------------- /src/xio/windows/base/windef.nim: -------------------------------------------------------------------------------- 1 | import ntdef, basetsd 2 | 3 | 4 | type 5 | HWND* = Handle 6 | HINSTANCE* = Handle 7 | LPARAM* = LONG_PTR 8 | -------------------------------------------------------------------------------- /src/xio/windows/base/winnt.nim: -------------------------------------------------------------------------------- 1 | import minwindef 2 | 3 | 4 | type 5 | WCHAR* = WideCString 6 | PWCHAR* = ptr WCHAR 7 | 8 | PVOID* = pointer 9 | PVOID64* = uint64 10 | VOID* = void 11 | CHAR* = cchar 12 | SHORT* = cshort 13 | LONG* = clong 14 | INT* = cint 15 | 16 | LPWCH* = ptr WCHAR 17 | PWCH* = ptr WCHAR 18 | LPCWCH* = ptr WCHAR 19 | PCWCH* = ptr WCHAR 20 | NWPSTR* = ptr WCHAR 21 | LPWSTR* = ptr WCHAR 22 | PWSTR* = ptr WCHAR 23 | PZPWSTR* = ptr PWSTR 24 | PCZPWSTR* = ptr PWSTR 25 | LPUWSTR* = ptr WCHAR 26 | PUWSTR* = ptr WCHAR 27 | LPCWSTR* = ptr WCHAR 28 | LPCTSTR* = LPCWSTR 29 | PCWSTR* = ptr WCHAR 30 | PZPCWSTR* = ptr PCWSTR 31 | PCZPCWSTR* = ptr PCWSTR 32 | LPCUWSTR* = ptr WCHAR 33 | PCUWSTR* = ptr WCHAR 34 | PZZWSTR* = ptr WCHAR 35 | PCZZWSTR* = ptr WCHAR 36 | PUZZWSTR* = ptr WCHAR 37 | PCUZZWSTR* = ptr WCHAR 38 | PNZWCH* = ptr WCHAR 39 | PCNZWCH* = ptr WCHAR 40 | PUNZWCH* = ptr WCHAR 41 | PCUNZWCH* = ptr WCHAR 42 | LPCWCHAR* = ptr WCHAR 43 | PCWCHAR* = ptr WCHAR 44 | LPCUWCHAR* = ptr WCHAR 45 | PCUWCHAR* = ptr WCHAR 46 | UCSCHAR* = culong 47 | 48 | PUCSCHAR* = ptr UCSCHAR 49 | PCUCSCHAR* = ptr UCSCHAR 50 | PUCSSTR* = ptr UCSCHAR 51 | PUUCSSTR* = ptr UCSCHAR 52 | PCUCSSTR* = ptr UCSCHAR 53 | PCUUCSSTR* = ptr UCSCHAR 54 | PUUCSCHAR* = ptr UCSCHAR 55 | PCUUCSCHAR* = ptr UCSCHAR 56 | PCHAR* = cstring 57 | LPCH* = cstring 58 | PCH* = cstring 59 | LPCCH* = cstring 60 | PCCH* = cstring 61 | NPSTR* = cstring 62 | LPSTR* = cstring 63 | PSTR* = cstring 64 | PZPSTR* = ptr PSTR 65 | PCZPSTR* = ptr PSTR 66 | LPCSTR* = cstring 67 | PCSTR* = cstring 68 | PZPCSTR* = ptr PCSTR 69 | PCZPCSTR* = ptr PCSTR 70 | PZZSTR* = cstring 71 | PCZZSTR* = cstring 72 | PNZCH* = cstring 73 | PCNZCH* = cstring 74 | 75 | LONGLONG* = int64 76 | 77 | FILE_NOTIFY_INFORMATION* = object 78 | NextEntryOffset*: DWORD 79 | Action*: DWORD 80 | FileNameLength*: DWORD 81 | FileName*: UncheckedArray[Utf16Char] 82 | 83 | PFILE_NOTIFY_INFORMATION* = ptr FILE_NOTIFY_INFORMATION 84 | 85 | 86 | const 87 | DELETE* = (0x00010000) 88 | READ_CONTROL* = (0x00020000) 89 | WRITE_DAC* = (0x00040000) 90 | WRITE_OWNER* = (0x00080000) 91 | SYNCHRONIZE* = (0x00100000) 92 | STANDARD_RIGHTS_REQUIRED* = (0x000F0000) 93 | STANDARD_RIGHTS_READ* = (READ_CONTROL) 94 | STANDARD_RIGHTS_WRITE* = (READ_CONTROL) 95 | STANDARD_RIGHTS_EXECUTE* = (READ_CONTROL) 96 | STANDARD_RIGHTS_ALL* = (0x001F0000) 97 | SPECIFIC_RIGHTS_ALL* = (0x0000FFFF) 98 | 99 | ## 100 | ## AccessSystemAcl access type 101 | ## 102 | 103 | const 104 | ACCESS_SYSTEM_SECURITY* = (0x01000000) 105 | 106 | ## 107 | ## MaximumAllowed access type 108 | ## 109 | 110 | const 111 | MAXIMUM_ALLOWED* = (0x02000000) 112 | 113 | ## 114 | ## These are the generic rights. 115 | ## 116 | 117 | const 118 | GENERIC_READ* = (0x80000000) 119 | GENERIC_WRITE* = (0x40000000) 120 | GENERIC_EXECUTE* = (0x20000000) 121 | GENERIC_ALL* = (0x10000000) 122 | 123 | const 124 | SYSTEM_CACHE_ALIGNMENT_SIZE* = 64.uint 125 | UCSCHAR_INVALID_CHARACTER* = cast[UCSCHAR](0xffffffff) 126 | MIN_UCSCHAR*: UCSCHAR = 0 127 | MAX_UCSCHAR*: UCSCHAR = 0x0010FFFF 128 | 129 | 130 | FILE_READ_DATA* = (0x00000001) ## file & pipe 131 | FILE_LIST_DIRECTORY* = (0x00000001) ## directory 132 | FILE_WRITE_DATA* = (0x00000002) ## file & pipe 133 | FILE_ADD_FILE* = (0x00000002) ## directory 134 | FILE_APPEND_DATA* = (0x00000004) ## file 135 | FILE_ADD_SUBDIRECTORY* = (0x00000004) ## directory 136 | FILE_CREATE_PIPE_INSTANCE* = (0x00000004) ## named pipe 137 | FILE_READ_EA* = (0x00000008) ## file & directory 138 | FILE_WRITE_EA* = (0x00000010) ## file & directory 139 | FILE_EXECUTE* = (0x00000020) ## file 140 | FILE_TRAVERSE* = (0x00000020) ## directory 141 | FILE_DELETE_CHILD* = (0x00000040) ## directory 142 | FILE_READ_ATTRIBUTES* = (0x00000080) ## all 143 | FILE_WRITE_ATTRIBUTES* = (0x00000100) ## all 144 | FILE_ALL_ACCESS* = (STANDARD_RIGHTS_REQUIRED or SYNCHRONIZE or 0x000001FF) 145 | FILE_GENERIC_READ* = (STANDARD_RIGHTS_READ or FILE_READ_DATA or 146 | FILE_READ_ATTRIBUTES or FILE_READ_EA or SYNCHRONIZE) 147 | FILE_GENERIC_WRITE* = (STANDARD_RIGHTS_WRITE or FILE_WRITE_DATA or 148 | FILE_WRITE_ATTRIBUTES or FILE_WRITE_EA or FILE_APPEND_DATA or SYNCHRONIZE) 149 | FILE_GENERIC_EXECUTE* = (STANDARD_RIGHTS_EXECUTE or FILE_READ_ATTRIBUTES or 150 | FILE_EXECUTE or SYNCHRONIZE) 151 | 152 | FILE_SHARE_READ* = 0x00000001 153 | FILE_SHARE_WRITE* = 0x00000002 154 | FILE_SHARE_DELETE* = 0x00000004 155 | FILE_ATTRIBUTE_READONLY* = 0x00000001 156 | FILE_ATTRIBUTE_HIDDEN* = 0x00000002 157 | FILE_ATTRIBUTE_SYSTEM* = 0x00000004 158 | FILE_ATTRIBUTE_DIRECTORY* = 0x00000010 159 | FILE_ATTRIBUTE_ARCHIVE* = 0x00000020 160 | FILE_ATTRIBUTE_DEVICE* = 0x00000040 161 | FILE_ATTRIBUTE_NORMAL* = 0x00000080 162 | FILE_ATTRIBUTE_TEMPORARY* = 0x00000100 163 | FILE_ATTRIBUTE_SPARSE_FILE* = 0x00000200 164 | FILE_ATTRIBUTE_REPARSE_POINT* = 0x00000400 165 | FILE_ATTRIBUTE_COMPRESSED* = 0x00000800 166 | FILE_ATTRIBUTE_OFFLINE* = 0x00001000 167 | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED* = 0x00002000 168 | FILE_ATTRIBUTE_ENCRYPTED* = 0x00004000 169 | FILE_ATTRIBUTE_INTEGRITY_STREAM* = 0x00008000 170 | FILE_ATTRIBUTE_VIRTUAL* = 0x00010000 171 | FILE_ATTRIBUTE_NO_SCRUB_DATA* = 0x00020000 172 | FILE_ATTRIBUTE_EA* = 0x00040000 173 | FILE_ATTRIBUTE_PINNED* = 0x00080000 174 | FILE_ATTRIBUTE_UNPINNED* = 0x00100000 175 | FILE_ATTRIBUTE_RECALL_ON_OPEN* = 0x00040000 176 | FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS* = 0x00400000 177 | TREE_CONNECT_ATTRIBUTE_PRIVACY* = 0x00004000 178 | TREE_CONNECT_ATTRIBUTE_INTEGRITY* = 0x00008000 179 | TREE_CONNECT_ATTRIBUTE_GLOBAL* = 0x00000004 180 | TREE_CONNECT_ATTRIBUTE_PINNED* = 0x00000002 181 | FILE_ATTRIBUTE_STRICTLY_SEQUENTIAL* = 0x20000000 182 | FILE_NOTIFY_CHANGE_FILE_NAME* = 0x00000001 183 | FILE_NOTIFY_CHANGE_DIR_NAME* = 0x00000002 184 | FILE_NOTIFY_CHANGE_ATTRIBUTES* = 0x00000004 185 | FILE_NOTIFY_CHANGE_SIZE* = 0x00000008 186 | FILE_NOTIFY_CHANGE_LAST_WRITE* = 0x00000010 187 | FILE_NOTIFY_CHANGE_LAST_ACCESS* = 0x00000020 188 | FILE_NOTIFY_CHANGE_CREATION* = 0x00000040 189 | FILE_NOTIFY_CHANGE_SECURITY* = 0x00000100 190 | 191 | FILE_ACTION_ADDED* = 0x00000001 192 | FILE_ACTION_REMOVED* = 0x00000002 193 | FILE_ACTION_MODIFIED* = 0x00000003 194 | FILE_ACTION_RENAMED_OLD_NAME* = 0x00000004 195 | FILE_ACTION_RENAMED_NEW_NAME* = 0x00000005 196 | 197 | MAILSLOT_NO_MESSAGE* = cast[DWORD](-1) 198 | MAILSLOT_WAIT_FOREVER* = cast[DWORD](-1) 199 | FILE_CASE_SENSITIVE_SEARCH* = 0x00000001 200 | FILE_CASE_PRESERVED_NAMES* = 0x00000002 201 | FILE_UNICODE_ON_DISK* = 0x00000004 202 | FILE_PERSISTENT_ACLS* = 0x00000008 203 | FILE_FILE_COMPRESSION* = 0x00000010 204 | FILE_VOLUME_QUOTAS* = 0x00000020 205 | FILE_SUPPORTS_SPARSE_FILES* = 0x00000040 206 | FILE_SUPPORTS_REPARSE_POINTS* = 0x00000080 207 | FILE_SUPPORTS_REMOTE_STORAGE* = 0x00000100 208 | FILE_RETURNS_CLEANUP_RESULT_INFO* = 0x00000200 209 | FILE_SUPPORTS_POSIX_UNLINK_RENAME* = 0x00000400 210 | FILE_VOLUME_IS_COMPRESSED* = 0x00008000 211 | FILE_SUPPORTS_OBJECT_IDS* = 0x00010000 212 | FILE_SUPPORTS_ENCRYPTION* = 0x00020000 213 | FILE_NAMED_STREAMS* = 0x00040000 214 | FILE_READ_ONLY_VOLUME* = 0x00080000 215 | FILE_SEQUENTIAL_WRITE_ONCE* = 0x00100000 216 | FILE_SUPPORTS_TRANSACTIONS* = 0x00200000 217 | FILE_SUPPORTS_HARD_LINKS* = 0x00400000 218 | FILE_SUPPORTS_EXTENDED_ATTRIBUTES* = 0x00800000 219 | FILE_SUPPORTS_OPEN_BY_FILE_ID* = 0x01000000 220 | FILE_SUPPORTS_USN_JOURNAL* = 0x02000000 221 | FILE_SUPPORTS_INTEGRITY_STREAMS* = 0x04000000 222 | FILE_SUPPORTS_BLOCK_REFCOUNTING* = 0x08000000 223 | FILE_SUPPORTS_SPARSE_VDL* = 0x10000000 224 | FILE_DAX_VOLUME* = 0x20000000 225 | FILE_SUPPORTS_GHOSTING* = 0x40000000 226 | FILE_INVALID_FILE_ID* = cast[LONGLONG](-1) 227 | -------------------------------------------------------------------------------- /src/xio/windows/base/winuser.nim: -------------------------------------------------------------------------------- 1 | import windef, winnt, minwindef 2 | 3 | {.pragma: libUser32, stdcall, dynlib: "User32.dll".} 4 | 5 | 6 | const 7 | SB_HORZ* = 0 8 | SB_VERT* = 1 9 | SB_CTL* = 2 10 | SB_BOTH* = 3 11 | SB_LINEUP* = 0 12 | SB_LINELEFT* = 0 13 | SB_LINEDOWN* = 1 14 | SB_LINERIGHT* = 1 15 | SB_PAGEUP* = 2 16 | SB_PAGELEFT* = 2 17 | SB_PAGEDOWN* = 3 18 | SB_PAGERIGHT* = 3 19 | SB_THUMBPOSITION* = 4 20 | SB_THUMBTRACK* = 5 21 | SB_TOP* = 6 22 | SB_LEFT* = 6 23 | SB_BOTTOM* = 7 24 | SB_RIGHT* = 7 25 | SB_ENDSCROLL* = 8 26 | 27 | 28 | SW_HIDE* = 0 29 | SW_SHOWNORMAL* = 1 30 | SW_NORMAL* = 1 31 | SW_SHOWMINIMIZED* = 2 32 | SW_SHOWMAXIMIZED* = 3 33 | SW_MAXIMIZE* = 3 34 | SW_SHOWNOACTIVATE* = 4 35 | SW_SHOW* = 5 36 | SW_MINIMIZE* = 6 37 | SW_SHOWMINNOACTIVE* = 7 38 | SW_SHOWNA* = 8 39 | SW_RESTORE* = 9 40 | SW_SHOWDEFAULT* = 10 41 | SW_FORCEMINIMIZE* = 11 42 | SW_MAX* = 11 43 | HIDE_WINDOW* = 0 44 | SHOW_OPENWINDOW* = 1 45 | SHOW_ICONWINDOW* = 2 46 | SHOW_FULLSCREEN* = 3 47 | SHOW_OPENNOACTIVATE* = 4 48 | SW_PARENTCLOSING* = 1 49 | SW_OTHERZOOM* = 2 50 | SW_PARENTOPENING* = 3 51 | SW_OTHERUNZOOM* = 4 52 | 53 | 54 | AW_HOR_POSITIVE* = 0x00000001 55 | AW_HOR_NEGATIVE* = 0x00000002 56 | AW_VER_POSITIVE* = 0x00000004 57 | AW_VER_NEGATIVE* = 0x00000008 58 | AW_CENTER* = 0x00000010 59 | AW_HIDE* = 0x00010000 60 | AW_ACTIVATE* = 0x00020000 61 | AW_SLIDE* = 0x00040000 62 | AW_BLEND* = 0x00080000 63 | KF_EXTENDED* = 0x00000100 64 | KF_DLGMODE* = 0x00000800 65 | KF_MENUMODE* = 0x00001000 66 | KF_ALTDOWN* = 0x00002000 67 | KF_REPEAT* = 0x00004000 68 | KF_UP* = 0x00008000 69 | 70 | 71 | VK_LBUTTON* = 0x00000001 72 | VK_RBUTTON* = 0x00000002 73 | VK_CANCEL* = 0x00000003 74 | VK_MBUTTON* = 0x00000004 75 | VK_XBUTTON1* = 0x00000005 76 | VK_XBUTTON2* = 0x00000006 77 | VK_BACK* = 0x00000008 78 | VK_TAB* = 0x00000009 79 | VK_CLEAR* = 0x0000000C 80 | VK_RETURN* = 0x0000000D 81 | VK_SHIFT* = 0x00000010 82 | VK_CONTROL* = 0x00000011 83 | VK_MENU* = 0x00000012 84 | VK_PAUSE* = 0x00000013 85 | VK_CAPITAL* = 0x00000014 86 | VK_KANA* = 0x00000015 87 | VK_HANGEUL* = 0x00000015 88 | VK_HANGUL* = 0x00000015 89 | VK_JUNJA* = 0x00000017 90 | VK_FINAL* = 0x00000018 91 | VK_HANJA* = 0x00000019 92 | VK_KANJI* = 0x00000019 93 | VK_ESCAPE* = 0x0000001B 94 | VK_CONVERT* = 0x0000001C 95 | VK_NONCONVERT* = 0x0000001D 96 | VK_ACCEPT* = 0x0000001E 97 | VK_MODECHANGE* = 0x0000001F 98 | VK_SPACE* = 0x00000020 99 | VK_PRIOR* = 0x00000021 100 | VK_NEXT* = 0x00000022 101 | VK_END* = 0x00000023 102 | VK_HOME* = 0x00000024 103 | VK_LEFT* = 0x00000025 104 | VK_UP* = 0x00000026 105 | VK_RIGHT* = 0x00000027 106 | VK_DOWN* = 0x00000028 107 | VK_SELECT* = 0x00000029 108 | VK_PRINT* = 0x0000002A 109 | VK_EXECUTE* = 0x0000002B 110 | VK_SNAPSHOT* = 0x0000002C 111 | VK_INSERT* = 0x0000002D 112 | VK_DELETE* = 0x0000002E 113 | VK_HELP* = 0x0000002F 114 | VK_LWIN* = 0x0000005B 115 | VK_RWIN* = 0x0000005C 116 | VK_APPS* = 0x0000005D 117 | VK_SLEEP* = 0x0000005F 118 | VK_NUMPAD0* = 0x00000060 119 | VK_NUMPAD1* = 0x00000061 120 | VK_NUMPAD2* = 0x00000062 121 | VK_NUMPAD3* = 0x00000063 122 | VK_NUMPAD4* = 0x00000064 123 | VK_NUMPAD5* = 0x00000065 124 | VK_NUMPAD6* = 0x00000066 125 | VK_NUMPAD7* = 0x00000067 126 | VK_NUMPAD8* = 0x00000068 127 | VK_NUMPAD9* = 0x00000069 128 | VK_MULTIPLY* = 0x0000006A 129 | VK_ADD* = 0x0000006B 130 | VK_SEPARATOR* = 0x0000006C 131 | VK_SUBTRACT* = 0x0000006D 132 | VK_DECIMAL* = 0x0000006E 133 | VK_DIVIDE* = 0x0000006F 134 | VK_F1* = 0x00000070 135 | VK_F2* = 0x00000071 136 | VK_F3* = 0x00000072 137 | VK_F4* = 0x00000073 138 | VK_F5* = 0x00000074 139 | VK_F6* = 0x00000075 140 | VK_F7* = 0x00000076 141 | VK_F8* = 0x00000077 142 | VK_F9* = 0x00000078 143 | VK_F10* = 0x00000079 144 | VK_F11* = 0x0000007A 145 | VK_F12* = 0x0000007B 146 | VK_F13* = 0x0000007C 147 | VK_F14* = 0x0000007D 148 | VK_F15* = 0x0000007E 149 | VK_F16* = 0x0000007F 150 | VK_F17* = 0x00000080 151 | VK_F18* = 0x00000081 152 | VK_F19* = 0x00000082 153 | VK_F20* = 0x00000083 154 | VK_F21* = 0x00000084 155 | VK_F22* = 0x00000085 156 | VK_F23* = 0x00000086 157 | VK_F24* = 0x00000087 158 | VK_NUMLOCK* = 0x00000090 159 | VK_SCROLL* = 0x00000091 160 | VK_OEM_NEC_EQUAL* = 0x00000092 161 | VK_OEM_FJ_JISHO* = 0x00000092 162 | VK_OEM_FJ_MASSHOU* = 0x00000093 163 | VK_OEM_FJ_TOUROKU* = 0x00000094 164 | VK_OEM_FJ_LOYA* = 0x00000095 165 | VK_OEM_FJ_ROYA* = 0x00000096 166 | VK_LSHIFT* = 0x000000A0 167 | VK_RSHIFT* = 0x000000A1 168 | VK_LCONTROL* = 0x000000A2 169 | VK_RCONTROL* = 0x000000A3 170 | VK_LMENU* = 0x000000A4 171 | VK_RMENU* = 0x000000A5 172 | VK_BROWSER_BACK* = 0x000000A6 173 | VK_BROWSER_FORWARD* = 0x000000A7 174 | VK_BROWSER_REFRESH* = 0x000000A8 175 | VK_BROWSER_STOP* = 0x000000A9 176 | VK_BROWSER_SEARCH* = 0x000000AA 177 | VK_BROWSER_FAVORITES* = 0x000000AB 178 | VK_BROWSER_HOME* = 0x000000AC 179 | VK_VOLUME_MUTE* = 0x000000AD 180 | VK_VOLUME_DOWN* = 0x000000AE 181 | VK_VOLUME_UP* = 0x000000AF 182 | VK_MEDIA_NEXT_TRACK* = 0x000000B0 183 | VK_MEDIA_PREV_TRACK* = 0x000000B1 184 | VK_MEDIA_STOP* = 0x000000B2 185 | VK_MEDIA_PLAY_PAUSE* = 0x000000B3 186 | VK_LAUNCH_MAIL* = 0x000000B4 187 | VK_LAUNCH_MEDIA_SELECT* = 0x000000B5 188 | VK_LAUNCH_APP1* = 0x000000B6 189 | VK_LAUNCH_APP2* = 0x000000B7 190 | VK_OEM_1* = 0x000000BA 191 | VK_OEM_PLUS* = 0x000000BB 192 | VK_OEM_COMMA* = 0x000000BC 193 | VK_OEM_MINUS* = 0x000000BD 194 | VK_OEM_PERIOD* = 0x000000BE 195 | VK_OEM_2* = 0x000000BF 196 | VK_OEM_3* = 0x000000C0 197 | VK_OEM_4* = 0x000000DB 198 | VK_OEM_5* = 0x000000DC 199 | VK_OEM_6* = 0x000000DD 200 | VK_OEM_7* = 0x000000DE 201 | VK_OEM_8* = 0x000000DF 202 | VK_OEM_AX* = 0x000000E1 203 | VK_OEM_102* = 0x000000E2 204 | VK_ICO_HELP* = 0x000000E3 205 | VK_ICO_00* = 0x000000E4 206 | VK_PROCESSKEY* = 0x000000E5 207 | VK_ICO_CLEAR* = 0x000000E6 208 | VK_PACKET* = 0x000000E7 209 | VK_OEM_RESET* = 0x000000E9 210 | VK_OEM_JUMP* = 0x000000EA 211 | VK_OEM_PA1* = 0x000000EB 212 | VK_OEM_PA2* = 0x000000EC 213 | VK_OEM_PA3* = 0x000000ED 214 | VK_OEM_WSCTRL* = 0x000000EE 215 | VK_OEM_CUSEL* = 0x000000EF 216 | VK_OEM_ATTN* = 0x000000F0 217 | VK_OEM_FINISH* = 0x000000F1 218 | VK_OEM_COPY* = 0x000000F2 219 | VK_OEM_AUTO* = 0x000000F3 220 | VK_OEM_ENLW* = 0x000000F4 221 | VK_OEM_BACKTAB* = 0x000000F5 222 | VK_ATTN* = 0x000000F6 223 | VK_CRSEL* = 0x000000F7 224 | VK_EXSEL* = 0x000000F8 225 | VK_EREOF* = 0x000000F9 226 | VK_PLAY* = 0x000000FA 227 | VK_ZOOM* = 0x000000FB 228 | VK_NONAME* = 0x000000FC 229 | VK_PA1* = 0x000000FD 230 | VK_OEM_CLEAR* = 0x000000FE 231 | 232 | 233 | WH_MIN* = (-1) 234 | WH_MSGFILTER* = (-1) 235 | WH_JOURNALRECORD* = 0 236 | WH_JOURNALPLAYBACK* = 1 237 | WH_KEYBOARD* = 2 238 | WH_GETMESSAGE* = 3 239 | WH_CALLWNDPROC* = 4 240 | WH_CBT* = 5 241 | WH_SYSMSGFILTER* = 6 242 | WH_MOUSE* = 7 243 | WH_HARDWARE* = 8 244 | WH_DEBUG* = 9 245 | WH_SHELL* = 10 246 | WH_FOREGROUNDIDLE* = 11 247 | WH_CALLWNDPROCRET* = 12 248 | WH_KEYBOARD_LL* = 13 249 | WH_MOUSE_LL* = 14 250 | WH_MAX* = 14 251 | WH_MINHOOK* = WH_MIN 252 | WH_MAXHOOK* = WH_MAX 253 | HC_ACTION* = 0 254 | HC_GETNEXT* = 1 255 | HC_SKIP* = 2 256 | HC_NOREMOVE* = 3 257 | HC_NOREM* = HC_NOREMOVE 258 | HC_SYSMODALON* = 4 259 | HC_SYSMODALOFF* = 5 260 | HCBT_MOVESIZE* = 0 261 | HCBT_MINMAX* = 1 262 | HCBT_QS* = 2 263 | HCBT_CREATEWND* = 3 264 | HCBT_DESTROYWND* = 4 265 | HCBT_ACTIVATE* = 5 266 | HCBT_CLICKSKIPPED* = 6 267 | HCBT_KEYSKIPPED* = 7 268 | HCBT_SYSCOMMAND* = 8 269 | HCBT_SETFOCUS* = 9 270 | 271 | 272 | 273 | SM_CXSCREEN* = 0 274 | SM_CYSCREEN* = 1 275 | SM_CXVSCROLL* = 2 276 | SM_CYHSCROLL* = 3 277 | SM_CYCAPTION* = 4 278 | SM_CXBORDER* = 5 279 | SM_CYBORDER* = 6 280 | SM_CXDLGFRAME* = 7 281 | SM_CYDLGFRAME* = 8 282 | SM_CYVTHUMB* = 9 283 | SM_CXHTHUMB* = 10 284 | SM_CXICON* = 11 285 | SM_CYICON* = 12 286 | SM_CXCURSOR* = 13 287 | SM_CYCURSOR* = 14 288 | SM_CYMENU* = 15 289 | SM_CXFULLSCREEN* = 16 290 | SM_CYFULLSCREEN* = 17 291 | SM_CYKANJIWINDOW* = 18 292 | SM_MOUSEPRESENT* = 19 293 | SM_CYVSCROLL* = 20 294 | SM_CXHSCROLL* = 21 295 | SM_DEBUG* = 22 296 | SM_SWAPBUTTON* = 23 297 | SM_RESERVED1* = 24 298 | SM_RESERVED2* = 25 299 | SM_RESERVED3* = 26 300 | SM_RESERVED4* = 27 301 | SM_CXMIN* = 28 302 | SM_CYMIN* = 29 303 | SM_CXSIZE* = 30 304 | SM_CYSIZE* = 31 305 | SM_CXFRAME* = 32 306 | SM_CYFRAME* = 33 307 | SM_CXMINTRACK* = 34 308 | SM_CYMINTRACK* = 35 309 | SM_CXDOUBLECLK* = 36 310 | SM_CYDOUBLECLK* = 37 311 | SM_CXICONSPACING* = 38 312 | SM_CYICONSPACING* = 39 313 | SM_MENUDROPALIGNMENT* = 40 314 | SM_PENWINDOWS* = 41 315 | SM_DBCSENABLED* = 42 316 | SM_CMOUSEBUTTONS* = 43 317 | SM_CXFIXEDFRAME* = SM_CXDLGFRAME 318 | SM_CYFIXEDFRAME* = SM_CYDLGFRAME 319 | SM_CXSIZEFRAME* = SM_CXFRAME 320 | SM_CYSIZEFRAME* = SM_CYFRAME 321 | SM_SECURE* = 44 322 | SM_CXEDGE* = 45 323 | SM_CYEDGE* = 46 324 | SM_CXMINSPACING* = 47 325 | SM_CYMINSPACING* = 48 326 | SM_CXSMICON* = 49 327 | SM_CYSMICON* = 50 328 | SM_CYSMCAPTION* = 51 329 | SM_CXSMSIZE* = 52 330 | SM_CYSMSIZE* = 53 331 | SM_CXMENUSIZE* = 54 332 | SM_CYMENUSIZE* = 55 333 | SM_ARRANGE* = 56 334 | SM_CXMINIMIZED* = 57 335 | SM_CYMINIMIZED* = 58 336 | SM_CXMAXTRACK* = 59 337 | SM_CYMAXTRACK* = 60 338 | SM_CXMAXIMIZED* = 61 339 | SM_CYMAXIMIZED* = 62 340 | SM_NETWORK* = 63 341 | SM_CLEANBOOT* = 67 342 | SM_CXDRAG* = 68 343 | SM_CYDRAG* = 69 344 | SM_SHOWSOUNDS* = 70 345 | SM_CXMENUCHECK* = 71 346 | SM_CYMENUCHECK* = 72 347 | SM_SLOWMACHINE* = 73 348 | SM_MIDEASTENABLED* = 74 349 | SM_MOUSEWHEELPRESENT* = 75 350 | SM_XVIRTUALSCREEN* = 76 351 | SM_YVIRTUALSCREEN* = 77 352 | SM_CXVIRTUALSCREEN* = 78 353 | SM_CYVIRTUALSCREEN* = 79 354 | SM_CMONITORS* = 80 355 | SM_SAMEDISPLAYFORMAT* = 81 356 | SM_IMMENABLED* = 82 357 | SM_CXFOCUSBORDER* = 83 358 | SM_CYFOCUSBORDER* = 84 359 | SM_TABLETPC* = 86 360 | SM_MEDIACENTER* = 87 361 | SM_STARTER* = 88 362 | SM_SERVERR2* = 89 363 | SM_DIGITIZER* = 94 364 | SM_MAXIMUMTOUCHES* = 95 365 | SM_CMETRICS* = 97 366 | SM_REMOTESESSION* = 0x00001000 367 | SM_SHUTTINGDOWN* = 0x00002000 368 | SM_REMOTECONTROL* = 0x00002001 369 | SM_CARETBLINKINGENABLED* = 0x00002002 370 | SM_CONVERTIBLESLATEMODE* = 0x00002003 371 | SM_SYSTEMDOCKED* = 0x00002004 372 | PMB_ACTIVE* = 0x00000001 373 | MNC_IGNORE* = 0 374 | MNC_CLOSE* = 1 375 | MNC_EXECUTE* = 2 376 | MNC_SELECT* = 3 377 | MNS_NOCHECK* = 0x80000000 378 | MNS_MODELESS* = 0x40000000 379 | MNS_DRAGDROP* = 0x20000000 380 | MNS_AUTODISMISS* = 0x10000000 381 | MNS_NOTIFYBYPOS* = 0x08000000 382 | MNS_CHECKORBMP* = 0x04000000 383 | MIM_MAXHEIGHT* = 0x00000001 384 | MIM_BACKGROUND* = 0x00000002 385 | MIM_HELPID* = 0x00000004 386 | MIM_MENUDATA* = 0x00000008 387 | MIM_STYLE* = 0x00000010 388 | MIM_APPLYTOSUBMENUS* = 0x80000000 389 | MND_CONTINUE* = 0 390 | MND_ENDMENU* = 1 391 | MNGOF_TOPGAP* = 0x00000001 392 | MNGOF_BOTTOMGAP* = 0x00000002 393 | MNGO_NOINTERFACE* = 0x00000000 394 | MNGO_NOERROR* = 0x00000001 395 | MIIM_STATE* = 0x00000001 396 | MIIM_ID* = 0x00000002 397 | MIIM_SUBMENU* = 0x00000004 398 | MIIM_CHECKMARKS* = 0x00000008 399 | MIIM_TYPE* = 0x00000010 400 | MIIM_DATA* = 0x00000020 401 | MIIM_STRING* = 0x00000040 402 | MIIM_BITMAP* = 0x00000080 403 | MIIM_FTYPE* = 0x00000100 404 | 405 | MB_OK* = 0x00000000 406 | MB_OKCANCEL* = 0x00000001 407 | MB_ABORTRETRYIGNORE* = 0x00000002 408 | MB_YESNOCANCEL* = 0x00000003 409 | MB_YESNO* = 0x00000004 410 | MB_RETRYCANCEL* = 0x00000005 411 | MB_CANCELTRYCONTINUE* = 0x00000006 412 | MB_ICONHAND* = 0x00000010 413 | MB_ICONQUESTION* = 0x00000020 414 | MB_ICONEXCLAMATION* = 0x00000030 415 | MB_ICONASTERISK* = 0x00000040 416 | MB_USERICON* = 0x00000080 417 | MB_ICONWARNING* = MB_ICONEXCLAMATION 418 | MB_ICONERROR* = MB_ICONHAND 419 | MB_ICONINFORMATION* = MB_ICONASTERISK 420 | MB_ICONSTOP* = MB_ICONHAND 421 | MB_DEFBUTTON1* = 0x00000000 422 | MB_DEFBUTTON2* = 0x00000100 423 | MB_DEFBUTTON3* = 0x00000200 424 | MB_DEFBUTTON4* = 0x00000300 425 | MB_APPLMODAL* = 0x00000000 426 | MB_SYSTEMMODAL* = 0x00001000 427 | MB_TASKMODAL* = 0x00002000 428 | MB_HELP* = 0x00004000 429 | MB_NOFOCUS* = 0x00008000 430 | MB_SETFOREGROUND* = 0x00010000 431 | MB_DEFAULT_DESKTOP_ONLY* = 0x00020000 432 | MB_TOPMOST* = 0x00040000 433 | MB_RIGHT* = 0x00080000 434 | MB_RTLREADING* = 0x00100000 435 | MB_SERVICE_NOTIFICATION* = 0x00200000 436 | MB_SERVICE_NOTIFICATION_NT3X* = 0x00040000 437 | MB_TYPEMASK* = 0x0000000F 438 | MB_ICONMASK* = 0x000000F0 439 | MB_DEFMASK* = 0x00000F00 440 | MB_MODEMASK* = 0x00003000 441 | MB_MISCMASK* = 0x0000C000 442 | 443 | 444 | 445 | proc messageBoxA*( 446 | hwnd: HWND, 447 | lpText: LPCTSTR, 448 | lpCation: LPCTSTR, 449 | uType: UINT 450 | ): int {.libUser32, importc: "MessageBoxA".} 451 | 452 | proc messageBoxW*( 453 | hwnd: HWND, 454 | lpText: LPCWSTR, 455 | lpCation: LPCWSTR, 456 | uType: UINT 457 | ): int {.libUser32, importc: "MessageBoxW".} 458 | 459 | proc getSystemMetrics*(nIndex: cint): cint {.importc: "GetSystemMetrics", libUser32.} = 460 | ## Retrieves the specified system metric or system configuration setting. 461 | 462 | proc lockWorkStation*(): bool {.importc: "LockWorkStation", libUser32.} = 463 | ## Locks the screen. 464 | 465 | 466 | # type 467 | # ComputerNameFormat* {.pure.} = enum 468 | # ComputerNameNetBIOS, 469 | # ComputerNameDnsHostname, 470 | # ComputerNameDnsDomain, 471 | # ComputerNameDnsFullyQualified, 472 | # ComputerNamePhysicalNetBIOS, 473 | # ComputerNamePhysicalDnsHostname, 474 | # ComputerNamePhysicalDnsDomain, 475 | # ComputerNamePhysicalDnsFullyQualified, 476 | # ComputerNameMax 477 | 478 | # proc getComputerNameExA*(nameType: ComputerNameFormat, 479 | # lpBuffer: LPSTR, nSize: LPDWORD): bool {.importc: "GetComputerNameExA", libUser32.} 480 | 481 | # proc getComputerNameExW*(nameType: ComputerNameFormat, 482 | # lpBuffer: LPWSTR, nSize: LPDWORD): bool {.importc: "GetComputerNameExW", libUser32.} 483 | 484 | # proc getComputerNameEx*(nameType: ComputerNameFormat, 485 | # lpBuffer: string, nSize: int): bool {.inline.} = 486 | # ## Gets a type of the computer name(UTF-8 version). 487 | # getComputerNameExA(nameType, lpBuffer.cstring, cast[LPDWORD](nSize)) 488 | 489 | # proc getComputerNameEx*(nameType: ComputerNameFormat, 490 | # lpBuffer: WideCStringObj, nSize: int): bool {.inline.} = 491 | # ## Gets a type of the computer name(Unicode version). 492 | # getComputerNameExW(nameType, cast[LPCWSTR](lpBuffer), cast[LPDWORD](nSize)) 493 | -------------------------------------------------------------------------------- /src/xio/windows/base/ws2def.nim: -------------------------------------------------------------------------------- 1 | import crt/rt 2 | import ws2types, winnt 3 | 4 | 5 | type 6 | AddrInfoA* = object 7 | aiFlags*: cint 8 | aiFamily*: cint 9 | aiSocktype*: cint 10 | aiProtocol*: cint 11 | aiAddrlen*: size_t 12 | aiCanonname*: cstring 13 | aiAddr*: ptr SockAddr 14 | aiNext*: ptr AddrInfoA 15 | 16 | PAddrInfoA* = ptr AddrInfoA 17 | 18 | AddrInfoW* = object 19 | aiFlags*: cint 20 | aiFamily*: cint 21 | aiSocktype*: cint 22 | aiProtocol*: cint 23 | aiAddrlen*: size_t 24 | aiCanonname*: PWSTR 25 | aiAddr*: ptr SockAddr 26 | aiNext*: ptr AddrInfoA 27 | 28 | PAddrInfoW* = ptr AddrInfoW 29 | 30 | 31 | const 32 | AI_CANONNAME* = 0x00000002 33 | AI_NUMERICHOST* = 0x00000004 34 | AI_NUMERICSERV* = 0x00000008 35 | AI_DNS_ONLY* = 0x00000010 36 | AI_ALL* = 0x00000100 37 | AI_ADDRCONFIG* = 0x00000400 38 | AI_V4MAPPED* = 0x00000800 39 | AI_NON_AUTHORITATIVE* = 0x00004000 40 | AI_SECURE* = 0x00008000 41 | AI_RETURN_PREFERRED_NAMES* = 0x00010000 42 | AI_FQDN* = 0x00020000 43 | AI_FILESERVER* = 0x00040000 44 | AI_DISABLE_IDN_ENCODING* = 0x00080000 45 | AI_EXTENDED* = 0x80000000 46 | AI_RESOLUTION_HANDLE* = 0x40000000 47 | 48 | IPPROTO_IP* = 0 49 | IPPROTO_HOPOPTS* = 0 50 | IPPROTO_ICMP* = 1 51 | IPPROTO_IGMP* = 2 52 | IPPROTO_GGP* = 3 53 | IPPROTO_IPV4* = 4 54 | IPPROTO_TCP* = 6 55 | IPPROTO_PUP* = 12 56 | IPPROTO_UDP* = 17 57 | IPPROTO_IDP* = 22 58 | IPPROTO_IPV6* = 41 59 | IPPROTO_ROUTING* = 43 60 | IPPROTO_FRAGMENT* = 44 61 | IPPROTO_ESP* = 50 62 | IPPROTO_AH* = 51 63 | IPPROTO_ICMPV6* = 58 64 | IPPROTO_NONE* = 59 65 | IPPROTO_DSTOPTS* = 60 66 | IPPROTO_ND* = 77 67 | IPPROTO_ICLFXBM* = 78 68 | IPPROTO_RAW* = 255 69 | IPPROTO_MAX* = 256 70 | IPPORT_ECHO* = 7 71 | IPPORT_DISCARD* = 9 72 | IPPORT_SYSTAT* = 11 73 | IPPORT_DAYTIME* = 13 74 | IPPORT_NETSTAT* = 15 75 | IPPORT_FTP* = 21 76 | IPPORT_TELNET* = 23 77 | IPPORT_SMTP* = 25 78 | IPPORT_TIMESERVER* = 37 79 | IPPORT_NAMESERVER* = 42 80 | IPPORT_WHOIS* = 43 81 | IPPORT_MTP* = 57 82 | IPPORT_TFTP* = 69 83 | IPPORT_RJE* = 77 84 | IPPORT_FINGER* = 79 85 | IPPORT_TTYLINK* = 87 86 | IPPORT_SUPDUP* = 95 87 | IPPORT_EXECSERVER* = 512 88 | IPPORT_LOGINSERVER* = 513 89 | IPPORT_CMDSERVER* = 514 90 | IPPORT_EFSSERVER* = 520 91 | IPPORT_BIFFUDP* = 512 92 | IPPORT_WHOSERVER* = 513 93 | IPPORT_ROUTESERVER* = 520 94 | IPPORT_RESERVED* = 1024 95 | IMPLINK_IP* = 155 96 | IMPLINK_LOWEXPER* = 156 97 | IMPLINK_HIGHEXPER* = 158 98 | -------------------------------------------------------------------------------- /src/xio/windows/base/ws2tcpip.nim: -------------------------------------------------------------------------------- 1 | import winnt, ws2def 2 | 3 | 4 | {.pragma: libWs2_32, stdcall, dynlib: "Ws2_32.dll".} 5 | 6 | 7 | proc getAddrInfo*( 8 | pNodeName: PCSTR, 9 | pServiceName: PCSTR, 10 | pHints: ptr AddrInfoA, 11 | ppResult: ptr PAddrInfoA 12 | ): cint {.libWs2_32, importc: "getaddrinfo"} 13 | 14 | proc getAddrInfo*( 15 | pNodeName: PCWSTR, 16 | pServiceName: PCWSTR, 17 | pHints: ptr AddrInfoW, 18 | ppResult: ptr PAddrInfoW 19 | ): cint {.libWs2_32, importc: "GetaddrinfoW"} 20 | 21 | proc freeAddrInfo*(pAddrInfo: PADDRINFOA) {.libWs2_32, importc: "freeaddrinfo"} 22 | -------------------------------------------------------------------------------- /src/xio/windows/base/ws2types.nim: -------------------------------------------------------------------------------- 1 | import bsdtypes, inaddr 2 | 3 | type 4 | SockAddr* = object 5 | saFamily*: uushort 6 | saData*: array[14, cchar] 7 | 8 | SockaddrIn* = object 9 | sinFamily*: cshort 10 | sinPort*: uushort 11 | sinAddr*: InAddr 12 | sinZero*: array[8, cchar] 13 | 14 | 15 | proc newSockAddr*(saFamily: uushort, saData: array[14, char]): SockAddr = 16 | SockAddr(saFamily: saFamily, saData: saData) 17 | -------------------------------------------------------------------------------- /src/xio/windows/guiddef.nim: -------------------------------------------------------------------------------- 1 | import base/basetsd 2 | 3 | type 4 | GUID* = object 5 | data1: culong 6 | data2: cushort 7 | data3: cushort 8 | data4: array[8, uchar] 9 | -------------------------------------------------------------------------------- /src/xio/windows/iocp.nim: -------------------------------------------------------------------------------- 1 | {.pragma: libKernel32, stdcall, dynlib: "Kernel32.dll".} 2 | 3 | import base / [basetsd, ntdef, minwindef, handleapi, minwinbase] 4 | 5 | export handleapi, ntdef, minwindef, minwinbase 6 | 7 | proc createIoCompletionPort*( 8 | fileHandle: Handle, 9 | existingCompletionPort: Handle, 10 | completionKey: ULONG_PTR, 11 | numberOfConcurrentThreads: DWORD 12 | ): Handle {.libKernel32, importc: "CreateIoCompletionPort".} 13 | ## Creates an input/output (I/O) completion port and associates it with a specified file handle. 14 | ## Or creates an input/output (I/O) completion port which is not yet associated with file handles. 15 | ## Params: 16 | ## - ``FileHandle``: An open file handle or INVALID_HANDLE_VALUE. 17 | ## - ``ExistingCompletionPort``: A handle to an existing I/O completion port or NULL. 18 | ## - ``CompletionKey``: User-defined completion key. 19 | ## - ``NumberOfConcurrentThreads`` 20 | ## Returns: 21 | ## - ``Handle`` 22 | ## 23 | ## .. code-block:: Nim 24 | ## # creates an input/output (I/O) completion port 25 | ## discard createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) 26 | 27 | proc getQueuedCompletionStatus*( 28 | completionPort: Handle, 29 | lpNumberOfBytesTransferred: var DWORD, # LPDWORD 30 | lpCompletionKey: var ULONG_PTR, # LPULONG_PTR 31 | lpOverlapped: LPOVERLAPPED, # LPOVERLAPPED 32 | dwMilliseconds: DWORD 33 | ): WINBOOL {.libKernel32, importc: "GetQueuedCompletionStatus".} 34 | ## Gets one completion port entry. 35 | 36 | proc getQueuedCompletionStatusEx*( 37 | completionPort: Handle, 38 | lpCompletionPortEntries: LPOVERLAPPED_ENTRY, # LPOVERLAPPED_ENTRY 39 | ulCount: ULONG, 40 | ulNumEntriesRemoved: var ULONG, # LPULONG 41 | dwMilliseconds: DWORD, 42 | fAlertable: WINBOOL 43 | ): WINBOOL {.libKernel32, importc: "GetQueuedCompletionStatusEx".} 44 | ## Gets multiple completion port entries simultaneously. 45 | 46 | 47 | when isMainModule: 48 | echo cast[int](createIoCompletionPort(INVALID_HANDLE_VALUE, cast[Handle](0), 0, 1)) 49 | -------------------------------------------------------------------------------- /src/xio/windows/sysinfoapi.nim: -------------------------------------------------------------------------------- 1 | import base/[minwindef, ntdef, basetsd] 2 | 3 | 4 | {.pragma: libKernel32, stdcall, dynlib: "Kernel32.dll".} 5 | 6 | 7 | type 8 | SYSTEM_INFO_UNION1_STRUCT1* = object 9 | wProcessorArchitecture*: WORD 10 | wReserved*: WORD 11 | 12 | SYSTEM_INFO_UNION1* {.union, pure, final.} = object 13 | dwOemId*: DWORD 14 | s1*: SYSTEM_INFO_UNION1_STRUCT1 15 | 16 | SYSTEM_INFO* {.pure, final.} = object 17 | u1*: SYSTEM_INFO_UNION1 18 | dwPageSize*: DWORD 19 | lpMinimumApplicationAddress*: LPVOID 20 | lpMaximumApplicationAddress*: LPVOID 21 | dwActiveProcessorMask*: DWORD_PTR 22 | dwNumberOfProcessors*: DWORD 23 | dwProcessorType*: DWORD 24 | dwAllocationGranularity*: DWORD 25 | wProcessorLevel*: WORD 26 | wProcessorRevision*: WORD 27 | 28 | LPSYSTEM_INFO* = ptr SYSTEM_INFO 29 | 30 | 31 | proc getSystemInfo*(lpSystemInfo: ptr SYSTEM_INFO) {.libKernel32, importc: "GetSystemInfo".} 32 | 33 | 34 | when isMainModule: 35 | # Error: var info = SYSTEM_INFO() 36 | var info: SYSTEM_INFO 37 | getSystemInfo(addr info) 38 | echo info 39 | -------------------------------------------------------------------------------- /src/xio/windows/winsock2.nim: -------------------------------------------------------------------------------- 1 | import wsadata, guiddef 2 | import base / [ntdef, minwinbase, sockettypes, winnt, ws2types, minwindef, qos, bsdtypes, inaddr] 3 | 4 | 5 | {.pragma: libWs2_32, stdcall, dynlib: "Ws2_32.dll".} 6 | 7 | 8 | const 9 | MAX_PROTOCOL_CHAIN = 7 10 | WSAPROTOCOL_LEN = 255 11 | 12 | type 13 | GROUP* = uint32 14 | 15 | WSAPROTOCOLCHAIN* = object 16 | chainLen*: cint 17 | chainEntries*: array[MAX_PROTOCOL_CHAIN, DWORD] 18 | 19 | LPWSAPROTOCOLCHAIN* = ptr WSAPROTOCOLCHAIN 20 | 21 | WSAPROTOCOL_INFOW* = object 22 | dwServiceFlags1*: DWORD 23 | dwServiceFlags2*: DWORD 24 | dwServiceFlags3*: DWORD 25 | dwServiceFlags4*: DWORD 26 | dwProviderFlags*: DWORD 27 | providerId*: GUID 28 | dwCatalogEntryId*: DWORD 29 | protocolChain*: WSAPROTOCOLCHAIN 30 | iVersion*: cint 31 | iAddressFamily*: cint 32 | iMaxSockAddr*: cint 33 | iMinSockAddr*: cint 34 | iSocketType*: cint 35 | iProtocol*: cint 36 | iProtocolMaxOffset*: cint 37 | iNetworkByteOrder*: cint 38 | iSecurityScheme*: cint 39 | dwMessageSize*: DWORD 40 | dwProviderReserved*: DWORD 41 | szProtocol*: array[WSAPROTOCOL_LEN + 1, WChar] 42 | 43 | LPWSAPROTOCOL_INFOW* = ptr WSAPROTOCOL_INFOW 44 | 45 | QOS* = object 46 | sendingFlowspec*: FLOWSPEC 47 | receivingFlowspec*: FLOWSPEC 48 | providerSpecific*: WSABUF 49 | 50 | LPQOS* = ptr QOS 51 | 52 | PROTOENT* = object 53 | p_name*: cstring 54 | p_aliases*: cstringArray 55 | p_proto*: cshort 56 | 57 | PPROTOENT* = ptr PROTOENT 58 | LPPROTOENT* = ptr PPROTOENT 59 | 60 | 61 | const 62 | WSA_IO_PENDING* = 997'i32 63 | INFINITE* = -1'i32 64 | 65 | AF_UNSPEC* = 0 66 | AF_UNIX* = 1 67 | AF_INET* = 2 68 | AF_IMPLINK* = 3 69 | AF_PUP* = 4 70 | AF_CHAOS* = 5 71 | AF_NS* = 6 72 | AF_IPX* = AF_NS 73 | AF_ISO* = 7 74 | AF_OSI* = AF_ISO 75 | AF_ECMA* = 8 76 | AF_DATAKIT* = 9 77 | AF_CCITT* = 10 78 | AF_SNA* = 11 79 | AF_DECnet* = 12 80 | AF_DLI* = 13 81 | AF_LAT* = 14 82 | AF_HYLINK* = 15 83 | AF_APPLETALK* = 16 84 | AF_NETBIOS* = 17 85 | AF_VOICEVIEW* = 18 86 | AF_FIREFOX* = 19 87 | AF_UNKNOWN1* = 20 88 | AF_BAN* = 21 89 | AF_ATM* = 22 90 | AF_INET6* = 23 91 | AF_CLUSTER* = 24 92 | AF_12844* = 25 93 | AF_IRDA* = 26 94 | AF_NETDES* = 28 95 | AF_TCNPROCESS* = 29 96 | AF_TCNMESSAGE* = 30 97 | AF_ICLFXBM* = 31 98 | AF_BTH* = 32 99 | AF_MAX* = 33 100 | 101 | 102 | SD_RECEIVE* = 0 ## Shutdown receive operations. 103 | SD_SEND* = 1 ## Shutdown send operations. 104 | SD_BOTH* = 2 ## Shutdown both send and receive operations. 105 | 106 | SOL_SOCKET* = 0xffff 107 | SOMAXCONN* = 0x7fffffff 108 | 109 | proc WSAStartup*(wVersionRequested: Word, 110 | lpWSAData: LPWSADATA): cint {.libWs2_32, importc: "WSAStartup".} 111 | 112 | proc WSASocket*( 113 | af: cint, 114 | theType: cint, 115 | protocol: cint, 116 | lpProtocolInfo: LPWSAPROTOCOL_INFOW, 117 | g: GROUP, 118 | dwFlags: DWORD 119 | ): SocketHandle {.libWs2_32, importc: "WSASocketW".} 120 | 121 | proc socket*(af, typ, protocol: cint): SocketHandle {.libWs2_32, importc: "socket".} 122 | 123 | proc accept*(s: SocketHandle, a: ptr SockAddr, 124 | addrlen: var cint): SocketHandle {.libWs2_32, importc: "accept".} 125 | 126 | proc bindAddr*(s: SocketHandle, name: ptr SockAddr, 127 | namelen: cint): cint {.libWs2_32, importc: "bind".} 128 | # SOMAXCONN 129 | 130 | proc closeSocket*(s: SocketHandle): cint {.libWs2_32, importc: "closesocket".} 131 | 132 | proc connect*(s: SocketHandle, name: ptr SockAddr, 133 | namelen: cint): cint {.libWs2_32, importc: "connect".} 134 | ## The client connects to an address. 135 | 136 | proc recv*(s: SocketHandle, buf: cstring, len, flags: cint): cint {.libWs2_32, importc: "recv".} 137 | 138 | proc recvfrom*(s: SocketHandle, buf: cstring, len, flags: cint, 139 | fromm: ptr SockAddr, fromlen: var cint): cint {.libWs2_32, importc: "recvfrom".} 140 | 141 | # proc select*(nfds: cint, readfds, writefds, exceptfds: ptr TFdSet, 142 | # timeout: ptr Timeval): cint 143 | 144 | proc send*(s: SocketHandle, buf: cstring, 145 | len, flags: cint): cint {.libWs2_32, importc: "send".} 146 | 147 | proc sendto*(s: SocketHandle, buf: pointer, len, flags: cint, 148 | to: ptr SockAddr, tolen: cint): cint {.libWs2_32, importc: "sendto".} 149 | 150 | proc shutdown*(s: SocketHandle, how: cint): cint {.libWs2_32, importc: "shutdown".} 151 | 152 | proc ioctlsocket*(s: SocketHandle, cmd: clong, 153 | argp: var uulong): cint {.libWs2_32, importc: "ioctlsocket".} 154 | 155 | proc getsockname*(s: SocketHandle, name: ptr SockAddr, 156 | namelen: var cint): cint {.libWs2_32, importc: "getsockname".} 157 | 158 | proc getpeername*(s: SocketHandle, name: ptr SockAddr, 159 | namelen: var cint): cint {.libWs2_32, importc: "getpeername".} 160 | 161 | proc getsockopt*(s: SocketHandle, level, optname: cint, optval: cstring, 162 | optlen: var cint): cint {.libWs2_32, importc: "getsockopt".} 163 | 164 | proc setsockopt*(s: SocketHandle, level, optname: cint, optval: cstring, 165 | optlen: cint): cint {.libWs2_32, importc: "setsockopt".} 166 | 167 | proc htonl*(hostlong: uulong): uulong {.libWs2_32, importc: "htonl".} = 168 | ## Converts a uulong from host to TCP/IP network byte order (which is big-endian). 169 | runnableExamples: 170 | doAssert htonl(0x1) == 16777216 171 | 172 | proc htons*(hostshort: uushort): uushort {.libWs2_32, importc: "htons".} = 173 | ## Converts a uushort from host to TCP/IP network byte order (which is big-endian). 174 | runnableExamples: 175 | doAssert htons(0x1) == 256 176 | 177 | proc inet_addr*(cp: cstring): culong {.libWs2_32, importc: "inet_addr".} = 178 | ## Converts an IPv4 address from dotted-quad string to 32-bit packed binary format. 179 | ## 180 | ## Notes: 181 | ## Pays attenetion to byte order (which is the same as host). 182 | runnableExamples: 183 | # Only in Intel 184 | doAssert inet_addr("127.0.0.1") == 16777343 185 | doAssert inet_addr("1.0.0.127") == 2130706433 186 | 187 | proc inet_ntoa*(inAddr: InAddr): cstring {.libWs2_32, importc: "inet_ntoa".} 188 | 189 | proc getprotobyname*( 190 | name: cstring 191 | ): ptr Protoent {.libWs2_32, importc: "getprotobyname"} 192 | 193 | proc getprotobynumber*( 194 | proto: cint 195 | ): ptr Protoent {.libWs2_32, importc: "getprotobynumber"} 196 | 197 | proc listen*(s: SocketHandle, backlog: cint): cint {.libWs2_32, importc: "listen".} 198 | 199 | proc WSAConnect*( 200 | s: SocketHandle, 201 | name: ptr SockAddr, 202 | namelen: cint, 203 | lpCallerData: LPWSABUF, # LPWSABUF 204 | lpCalleeData: LPWSABUF, #LPWSABUF 205 | lpSQOS: LPQOS, # LPQOS 206 | lpGQOS: LPQOS # LPQOS 207 | ): cint {.libWs2_32, importc: "WSAConnect".} 208 | 209 | proc WSAIoctl*( 210 | s: SocketHandle, 211 | dwIoControlCode: DWORD, lpvInBuffer: LPVOID, 212 | cbInBuffer: DWORD, lpvOutBuffer: LPVOID, cbOutBuffer: DWORD, 213 | lpcbBytesReturned: var DWORD, # LPDWORD 214 | lpOverlapped: LPOVERLAPPED, # LPOVERLAPPED 215 | lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 216 | ): cint {.libWs2_32, importc: "WSAIoctl".} 217 | 218 | proc WSASend*( 219 | s: SocketHandle, 220 | lpBuffers: LPWSABUF, # LPWSABUF 221 | dwBufferCount: DWORD, 222 | lpNumberOfBytesSent: var DWORD, # LPDWORD 223 | dwFlags: DWORD, 224 | lpOverlapped: LPWSAOVERLAPPED, # LPWSAOVERLAPPED 225 | lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 226 | ): cint {.libWs2_32, importc: "WSASend".} 227 | 228 | proc WSARecv*( 229 | s: SocketHandle, 230 | lpBuffers: LPWSABUF, # LPWSABUF 231 | dwBufferCount: DWORD, 232 | lpNumberOfBytesRecvd: var DWORD, # LPDWORD 233 | lpFlags: var DWORD, # LPDWORD 234 | lpOverlapped: LPWSAOVERLAPPED, # LPWSAOVERLAPPED 235 | lpCompletionRoutine: LPWSAOVERLAPPED_COMPLETION_ROUTINE 236 | ): cint {.libWs2_32, importc: "WSARecv".} 237 | 238 | proc WSACleanup*(): cint {.libWs2_32, importc: "WSACleanup".} 239 | 240 | proc WSAGetLastError*(): cint {.libWs2_32, importc: "WSAGetLastError".} 241 | -------------------------------------------------------------------------------- /src/xio/windows/wsadata.nim: -------------------------------------------------------------------------------- 1 | import base / minwindef 2 | 3 | 4 | const 5 | WSADESCRIPTION_LEN = 256 6 | WSASYS_STATUS_LEN = 128 7 | 8 | 9 | # _wsadata.h 10 | type 11 | WSADATA* = object 12 | wVersion*: WORD 13 | wHighVersion*: WORD 14 | 15 | iMaxSockets*: cushort 16 | iMaxUdpDg*: cushort 17 | lpVendorInfo*: cstring 18 | szDescription*: array[WSADESCRIPTION_LEN+1, cchar] 19 | szSystemStatus*: array[WSASYS_STATUS_LEN+1, cchar] 20 | 21 | LPWSADATA* = ptr WSADATA 22 | -------------------------------------------------------------------------------- /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /tests/t_socket.nim: -------------------------------------------------------------------------------- 1 | import ../src/xio/windows/[winsock2, wsadata] 2 | 3 | var wsa: WSAData 4 | 5 | if WSAStartup(0x0101, addr wsa) != 0: 6 | doAssert false 7 | 8 | block: 9 | let x = getprotobyname("tcp") 10 | if x != nil: 11 | doAssert x.p_proto == 6 12 | 13 | block: 14 | let x = getprotobyname("udp") 15 | if x != nil: 16 | doAssert x.p_proto == 17, $x.p_proto 17 | 18 | block: 19 | let x = getprotobyname("icmp") 20 | if x != nil: 21 | doAssert x.p_proto == 1, $x.p_proto 22 | 23 | doAssert WSAGetLastError() == 0 24 | discard WSACleanup() 25 | -------------------------------------------------------------------------------- /tests/xio.nim: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ringabout/xio/73321c2c163e7eebbee1b5774ccde8a74ef6fdbf/tests/xio.nim -------------------------------------------------------------------------------- /xio.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.1.0" 4 | author = "Zeshen Xing" 5 | description = "Cross platform system API for os and net." 6 | license = "Apache-2.0" 7 | srcDir = "src" 8 | 9 | 10 | 11 | # Dependencies 12 | 13 | requires "nim >= 1.2.6" 14 | --------------------------------------------------------------------------------