├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.txt ├── Makefile ├── README.md ├── cmake ├── FindLua.cmake ├── config.h.in ├── uv │ └── CMakeLists.txt └── zmq │ └── CMakeLists.txt ├── examples ├── bench.lua ├── codec.lua ├── fiber.lua ├── fs.lua ├── http.lua ├── http_hellosvr.lua ├── proc.lua ├── stdfh.lua ├── tcp_client.lua ├── tcp_echosvr.lua ├── thread.lua ├── timer.lua ├── timer2.lua ├── zmq.lua ├── zmq_pull.lua └── zmq_push.lua ├── luv-scm-1.rockspec └── src ├── Makefile ├── luv.c ├── luv.h ├── luv_codec.c ├── luv_cond.c ├── luv_fiber.c ├── luv_fs.c ├── luv_idle.c ├── luv_net.c ├── luv_object.c ├── luv_pipe.c ├── luv_process.c ├── luv_state.c ├── luv_stream.c ├── luv_thread.c ├── luv_timer.c ├── luv_zmq.c └── ngx-queue.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.[oa] 2 | *.so 3 | *.sw? 4 | build 5 | 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/uv"] 2 | path = src/uv 3 | url = https://github.com/joyent/libuv.git 4 | [submodule "src/zmq"] 5 | path = src/zmq 6 | url = https://github.com/zeromq/zeromq3-x.git 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | project(luv) 4 | 5 | # general info 6 | set(VERSION_MAJOR 0) 7 | set(VERSION_MINOR 1) 8 | 9 | # configurable options 10 | set(USE_ZMQ OFF) 11 | set(USE_HTTP OFF) 12 | set(USE_STRICT ON) 13 | 14 | option(USE_ZMQ "Include zmq" ${USE_ZMQ}) 15 | option(USE_HTTP "Include http" ${USE_HTTP}) 16 | option(USE_STRICT "Treat warning as errors" ${USE_STRICT}) 17 | ## TODO: include config.h into luv.h 18 | #configure_file(cmake/config.h.in config.h) 19 | 20 | # setup compiler flags 21 | add_definitions(-fPIC) 22 | if(USE_STRICT) 23 | add_definitions(-Werror) 24 | endif() 25 | 26 | # collect source files 27 | list(APPEND SOURCES 28 | src/luv.c src/luv_cond.c src/luv_state.c src/luv_fiber.c 29 | src/luv_thread.c src/luv_codec.c src/luv_object.c 30 | src/luv_timer.c src/luv_idle.c src/luv_fs.c src/luv_stream.c 31 | src/luv_pipe.c src/luv_net.c src/luv_process.c 32 | ) 33 | 34 | # find lua/luajit 35 | include(cmake/FindLua.cmake) 36 | include_directories(${LUA_INCLUDE_DIR}) 37 | list(APPEND LIBS ${LUA_LIBRARIES}) 38 | 39 | # setup linker flags 40 | find_package(Threads REQUIRED) 41 | list(APPEND LIBS ${CMAKE_THREAD_LIBS_INIT}) 42 | 43 | # build libuv 44 | execute_process(WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" 45 | COMMAND git submodule update --init src/uv) 46 | file(COPY "${PROJECT_SOURCE_DIR}/cmake/uv/CMakeLists.txt" 47 | DESTINATION "${PROJECT_SOURCE_DIR}/src/uv") 48 | add_subdirectory("${PROJECT_SOURCE_DIR}/src/uv") 49 | list(APPEND LIBS uv rt) 50 | 51 | # build libzmq 52 | if(USE_ZMQ) 53 | add_definitions(-DUSE_ZMQ) 54 | list(APPEND SOURCES src/luv_zmq.c) 55 | execute_process(WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}" 56 | COMMAND git submodule update --init src/zmq) 57 | file(COPY "${PROJECT_SOURCE_DIR}/cmake/zmq/CMakeLists.txt" 58 | DESTINATION "${PROJECT_SOURCE_DIR}/src/zmq") 59 | add_subdirectory("${PROJECT_SOURCE_DIR}/src/zmq") 60 | list(APPEND LIBS zmq stdc++) 61 | endif(USE_ZMQ) 62 | 63 | # build target module luv.so 64 | add_library(luv SHARED ${SOURCES}) 65 | target_link_libraries(luv ${LIBS}) 66 | set_target_properties(luv PROPERTIES PREFIX "") 67 | 68 | # install 69 | if(INSTALL_CMOD) 70 | install(TARGETS luv LIBRARY DESTINATION "${INSTALL_CMOD}") 71 | else() 72 | message("WARNING: " "No install destination is given. Manually copy luv.so") 73 | endif() 74 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | git submodule update --init ./src/uv 3 | git submodule update --init ./src/zmq 4 | make -C ./src 5 | 6 | clean: 7 | make -C ./src clean 8 | 9 | realclean: 10 | make -C ./src realclean 11 | 12 | .PHONY: all clean realclean 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NAME 2 | 3 | Luv - Thermonuclear battery pack for Lua 4 | 5 | # SYNOPSIS 6 | 7 | local luv = require("luv") 8 | 9 | # FEATURES 10 | 11 | * scheduled fibers 12 | * [libuv] TCP sockets 13 | * [libuv] timers 14 | * [libuv] filesystem operations 15 | * [libuv] OS threads 16 | * [libuv] pipes 17 | * [libuv] idle watchers 18 | * [zmq] ØMQ 3.x for the rest 19 | * binary serialization 20 | 21 | # INSTALLATION 22 | 23 | Run `make` and copy the luv.so to where you need it. In 24 | theory both ØMQ and libuv support WIN32, but I have no 25 | idea how that build system works there, so patches welcome. 26 | 27 | # DESCRIPTION 28 | 29 | Luv is an attempt to do libuv bindings to Lua in a style more 30 | suited to a language with coroutines than edge-triggered 31 | event-loop style programming with callbacks. 32 | 33 | So how is it different? 34 | 35 | At the heart of Luv is a reasonably fast coroutine scheduler 36 | (20,000,000 context switches / second on my laptop). 37 | 38 | Coroutines are wrapped as 'fibers' which add some extra bits 39 | which allow them to be suspended and resumed by the libuv event 40 | loop. This makes programming with them feel more like threads 41 | with a nice linear flow, but without the impressive crashes. 42 | 43 | Here's the canonical TCP echo server: 44 | 45 | ```Lua 46 | local main = luv.fiber.create(function() 47 | local server = luv.net.tcp() 48 | server:bind("127.0.0.1", 8080) 49 | server:listen() 50 | 51 | while true do 52 | local client = luv.net.tcp() 53 | server:accept(client) 54 | 55 | local child = luv.fiber.create(function() 56 | while true do 57 | local got, str = client:read() 58 | if got then 59 | client:write("you said: "..str) 60 | else 61 | client:close() 62 | break 63 | end 64 | end 65 | end) 66 | 67 | -- put it in the ready queue 68 | child:ready() 69 | end 70 | end) 71 | 72 | main:join() 73 | ``` 74 | 75 | A key point is that fibers run at highest priority, so that a process 76 | under I/O load gets doesn't starve the tasks which actually process 77 | the data. 78 | 79 | Once all pending fibers have been given a chance to run, the 80 | event loop kicks in and polls for events and the wakes up 81 | any suspended fibers waiting on events. 82 | 83 | ## States 84 | 85 | A state is an execution context which can be suspended or resumed. 86 | A state can be either a thread - including the main thread - or a fiber. 87 | 88 | States are not resumed immediately when ready, but are fair queued 89 | to be resumed at the next possible time, after they are signalled 90 | as being `ready`, typically by a libuv callback. 91 | 92 | The semantics of suspending a state depends on whether it is a thread 93 | or a fiber. 94 | 95 | * Suspending a fiber is equivalent to removing it from the scheduing queue. 96 | * Suspending a thread is equivalent to running the event loop and scheduler. 97 | * Readying a fiber is equivalent to inserting it into the scheduling queue. 98 | * Readying a thread is equivalent to interrupting the event loop. 99 | 100 | Any objects (timers, tcp, idle, etc.) may also run from the main thread 101 | while not blocking active fibers. 102 | 103 | ## Fibers 104 | 105 | Fibers are cooperatively scheduled plain Lua coroutines with one important 106 | difference: when using I/O objects or timers, the scheduling is done for 107 | you so that you don't explicitly need to call `coroutine.yield`. 108 | 109 | This makes fibers more like green threads, but without preemption. 110 | So most of the time, you just let them run and forget about the scheduling. 111 | 112 | ### luv.fiber.create(func, [arg1, ..., argN]) 113 | 114 | Fibers are created by calling `luv.fiber.create` and passing it the function 115 | to be run inside the fiber, along with any additional arguments which are 116 | passed to the function in turn. 117 | 118 | NOTE: The fiber is *not* run until it is put in the ready queue and the main 119 | thread is suspended. See `fiber:ready` and `fiber:join` below. 120 | 121 | ### fiber:ready() 122 | 123 | Insert the fiber into the scheduler's ready queue. The scheduler is run 124 | by the containing thread when it is suspended. 125 | 126 | ### fiber:join() 127 | 128 | Inserts the fiber into the thread's scheduler and suspend the current 129 | state until the fiber exits. Returns any values returned by the fiber. 130 | 131 | ### Fiber example: 132 | 133 | ```Lua 134 | local f1 = luv.fiber.create(function(mesg) 135 | print("inside fiber, mesg: ", mesg) 136 | 137 | local f2 = luv.fiber.create(function() 138 | print("in child") 139 | return "answer", 42 140 | end) 141 | 142 | local k, v = f2:join() -- join gets return values 143 | print(k, v) -- prints: answer, 42 144 | 145 | end, "Hello World!") 146 | 147 | f1:join() 148 | ``` 149 | 150 | ## Timers 151 | 152 | Timers allow you to suspend states for periods and wake them up again 153 | after the period has expired. 154 | 155 | ### luv.timer.create() 156 | 157 | Constructor. Takes no arguments. Returns a timer instance. 158 | 159 | ### timer:start(delay, repeat) 160 | 161 | Takes two parameters, `delay` and `repeat` are in milliseconds. 162 | 163 | ### timer:wait() 164 | 165 | Suspend the currently running state and resume when the timer 166 | fires. 167 | 168 | ### timer:stop() 169 | 170 | Stop the timer. 171 | 172 | ### timer:again() 173 | 174 | Stop the timer, and if it is repeating restart it using the repeat value 175 | as the timeout 176 | 177 | ### Timer Example: 178 | 179 | ```Lua 180 | local luv = require("luv") 181 | 182 | local timer = luv.timer.create() 183 | -- start after 1 second and repeat after 100ms 184 | timer:start(1000, 100) 185 | for i=1, 10 do 186 | -- the call to wait blocks here, but would schedule any 187 | -- ready fibers (if there were any) 188 | timer:wait() 189 | print("tick") 190 | end 191 | timer:stop() 192 | ``` 193 | 194 | ## Idle watchers 195 | 196 | Idle watchers run when there's nothing else to do. The object will rouse 197 | the waiting fibers repeatedly. 198 | 199 | ### luv.idle.create() 200 | 201 | Create an idle watcher. 202 | 203 | ### idle:start() 204 | 205 | Start the idle watcher. 206 | 207 | ### idle:stop() 208 | 209 | Stop the idle watcher. 210 | 211 | ### idle:wait() 212 | 213 | Suspend the current state until there's nothing else to do. 214 | 215 | ### Idle Example 216 | 217 | This is from the examples. During the timer pauses, the idle watcher 218 | unblocks the call to `idle:wait()`. 219 | 220 | ```Lua 221 | local luv = require('luv') 222 | 223 | local idle = luv.idle.create() 224 | idle:start() 225 | 226 | local idle_count = 0 227 | local f1 = luv.fiber.create(function() 228 | while true do 229 | idle:wait() 230 | idle_count = idle_count + 1 231 | end 232 | end) 233 | 234 | f1:ready() 235 | 236 | local timer = luv.timer.create() 237 | timer:start(10, 10) 238 | 239 | local f2 = luv.fiber.create(function() 240 | for i=1, 10 do 241 | print("TIMER NEXT:", timer:wait()) 242 | end 243 | timer:stop() 244 | end) 245 | 246 | f2:join() 247 | idle:stop() 248 | print("IDLE COUNT:", idle_count) 249 | 250 | ``` 251 | 252 | ## Filesystem operations 253 | 254 | In general, file system operations return an integer on success 255 | (usually 0) and `false` along with an error message on failure. 256 | 257 | ### luv.fs.open(path, mode, perm) 258 | 259 | Open a file. 260 | 261 | * `path` is the path of the file to open. 262 | * `mode` is one of `w`, `w+`, `r`, `r+`, `a`, `a+` 263 | * `perm` is a string representation of an octal number: i.e. `644` 264 | 265 | Returns a `file` object. See below for `file` object methods. 266 | 267 | ### luv.fs.unlink(path) 268 | 269 | Delete a file. 270 | 271 | ### luv.fs.mkdir(path) 272 | 273 | Create a directory. 274 | 275 | ### luv.fs.rmdir(path) 276 | 277 | Delete a directory. 278 | 279 | ### luv.fs.readdir(path) 280 | 281 | Reads the entries of a directory. On sucess returns a table 282 | including the entry names. 283 | 284 | ### luv.fs.stat(path) 285 | 286 | Stats the supplied path and returns a table of key, value 287 | pairs. 288 | 289 | ### luv.fs.rename(path, newpath) 290 | 291 | Renames a file or directory. 292 | 293 | ### luv.fs.sendfile(outfile, infile) 294 | 295 | Efficiently copy data from infile to outfile. 296 | 297 | ### luv.fs.chmod(path, mode) 298 | 299 | Change file or directory mode. The `mode` argument is a string 300 | representation of an octal number: i.e. '644' 301 | 302 | ### luv.fs.chown(path, uid, gid) 303 | 304 | Change the ownership of a path to the supplied `uid` and `gid`. 305 | Both `uid` and `gid` are integers. 306 | 307 | ### luv.fs.utime(path, atime, mtime) 308 | 309 | Change the access and modification time of a path 310 | 311 | ### luv.fs.lstat(path) 312 | 313 | Stat a link. 314 | 315 | ### luv.fs.link(srcpath, dstpath) 316 | 317 | Create a hard link from `srcpath` to `dstpath` 318 | 319 | ### luv.fs.symlink(srcpath, dstpath, mode) 320 | 321 | Create a symbolic link from `srcpath` to `dstpath` with `mode` flags. 322 | The `mode` argument takes the same values as for `luv.fs.open`. 323 | 324 | ### luv.fs.readlink(path) 325 | 326 | Dereference a symbolic link and return the target. 327 | 328 | ### luv.fs.cwd() 329 | 330 | Returns the current working directory of the running process. 331 | 332 | ### luv.fs.chdir(path) 333 | 334 | Change directory to `path`. 335 | 336 | ### luv.fs.exepath() 337 | 338 | Returns the path of the executable. 339 | 340 | ### file:read(len[, offset]) 341 | 342 | Attempts to read `len` number of bytes from the file and returns the number 343 | of bytes actually read followed by the data. If the optional `offset` is 344 | given, then start reading there. 345 | 346 | ### file:write(data[, offset]) 347 | 348 | Write `data` to the file. If the optional `offset` argument is given, then 349 | write start at that offset. Otherwise write from the start of the file. 350 | 351 | ### file:close() 352 | 353 | Close the file. 354 | 355 | ### file:stat() 356 | 357 | Stat the file. Same return value as for `luv.fs.stat` 358 | 359 | ### file:sync() 360 | 361 | Sync all pending data and metadata changes to disk. 362 | 363 | ### file:datasync() 364 | 365 | Sync data to disk. 366 | 367 | ### file:utime(atime, mtime) 368 | 369 | Like `luv.fs.utime` but uses the current file object. 370 | 371 | ### file:chmod(mode) 372 | 373 | Like `luv.fs.chmod` but uses the current file object. 374 | 375 | ### file:chown(uid, gid) 376 | 377 | Like `luv.fs.chown` but uses the current file object. 378 | 379 | ### file:truncate() 380 | 381 | Truncate the file. 382 | 383 | ## TCP Streams 384 | 385 | ### luv.net.tcp() 386 | 387 | Creates and returns a new unbound and disconnected TCP socket. 388 | 389 | ### tcp:bind(host, port) 390 | 391 | Bind to the given `host` and `port` 392 | 393 | ### tcp:listen([backlog]) 394 | 395 | Start listening for incoming connections. If `backlog` is given then 396 | that sets the maximum backlog for pending connections. If no `backlog` 397 | is given, then it defaults to 128. 398 | 399 | ### tcp:accept(tcp2) 400 | 401 | Calls `accept` with `tcp2` becoming the client socket. Used as follows: 402 | 403 | ```Lua 404 | 405 | local server = luv.net.tcp() 406 | server:bind(host, port) 407 | while true do 408 | local client = luv.net.tcp() 409 | server:accept(client) 410 | -- do something with the client, then close 411 | client:close() 412 | end 413 | 414 | ``` 415 | 416 | ### tcp:connect(host, port) 417 | 418 | Connect to a given `host` on `port`. Note that host must be a dotted quad. 419 | To resolve a domain name to IP address, use `getaddrinfo` 420 | 421 | ### tcp:getsockname() 422 | 423 | Returns a table with fields `family`, `port` and `address` filled of the 424 | current socket. Can be called on both connected and bound sockets. 425 | 426 | ### tcp:getpeername() 427 | 428 | Returns a table with fields `family`, `port` and `address` filled of the 429 | peer socket. Can only be called on connected sockets. 430 | 431 | ### tcp:keepalive(enable, seconds) 432 | 433 | Enable or disable TCP keepalive. The first arugment is a boolean. If 434 | `true` then sets the keepalive time to the number of `seconds`. If 435 | `false` then disables and `seconds` is ignored. 436 | 437 | ### tcp:nodelay(enable) 438 | 439 | Enable or disable nagle's algorithm for this socket. The `enable` 440 | argument must be a boolean. 441 | 442 | ### tcp:read([length]) 443 | 444 | Reads data from the socket. Returns the number of bytes read followed 445 | by the data itself. If the optional `length` argument is provided then 446 | that is the size, in bytes, of the buffer used internally. Defaults 447 | to the value of `LUV_BUF_SIZE` defined in luv.h (4096, currently). 448 | 449 | ### tcp:readable() 450 | 451 | Does a non-blocking check to see if the socket is readable. 452 | 453 | ### tcp:write(data) 454 | 455 | Writes `data` to the socket. 456 | 457 | ### tcp:writable() 458 | 459 | Does a non-blocking check to see if the socket is writable. 460 | 461 | ### tcp:shutdown() 462 | 463 | Shutdown the socket (inform the peer that we've finished with it) 464 | 465 | ### tcp:close() 466 | 467 | Close the socket. 468 | 469 | ### tcp:start() 470 | 471 | Start reading from the socket. Called automatically during read. 472 | 473 | ### tcp:stop() 474 | 475 | Stop reading from a socket. Called automatically during libuv's read 476 | callback if there are no fibers waiting to be roused. 477 | 478 | ## Processes 479 | 480 | See ./examples/proc.lua for now. 481 | 482 | ## Pipes 483 | 484 | TODO : add docs - their use is similar to TCP streams 485 | 486 | ### luv.pipe.create() 487 | 488 | ### pipe:open() 489 | 490 | ### pipe:bind() 491 | 492 | ### pipe:connect() 493 | 494 | ### pipe:listen() 495 | 496 | ### pipe:accept() 497 | 498 | ### pipe:read() 499 | 500 | ### pipe:write(data) 501 | 502 | ### pipe:close() 503 | 504 | ## Threads 505 | 506 | Threads are real OS threads and run concurrently (so no global locks) 507 | in distinct global Lua states. This means that sharing data between 508 | threads should be done with ØMQ sockets. However, functions passed 509 | to `luv.thread.spawn` are ordinary Lua functions and may contain 510 | upvalues. 511 | 512 | These upvalues are serialized as best as possible automatically 513 | and deserialized during thread entry. The same rules apply as for 514 | `luv.codec.encode` (see below). 515 | 516 | Return values passed back via `thread:join()` pass through the same 517 | serialize/deserialize process, with the same caveats. So bear in 518 | mind that there's no true shared address space when using threads. 519 | This is A Good Thing (tm), I'm told. 520 | 521 | Some of Luv's own objects and library tables are handled transparently. 522 | 523 | In particular ØMQ context objects can be passed to threads or referenced 524 | as upvalues. ØMQ sockets and other libuv objects cannot. 525 | 526 | Each thread has it's own libuv event loop, with the main thread running 527 | libuv's default loop. Threads may spawn other threads as well as fibers. 528 | 529 | ### luv.thread.spawn(func, arg1, ..., argN) 530 | 531 | Spawn a thread, using the Lua function `func` as the entry, 532 | and serialize the rest of the arguments and pass them deserialized 533 | back to `func` inside the new thread's global state. 534 | 535 | Threads are spawned immediately during a call to `luv.thread.spawn`, so 536 | they differ to fibers in that there's no call to `ready` them first. 537 | 538 | Returns a thread object. 539 | 540 | ### thread:join() 541 | 542 | Wait for the thread to finish. Returns the values returned by the thread 543 | if any. 544 | 545 | Threads may join on threads or fibers. I have no idea what happens if 546 | a fiber joins on a thread. Bad Things probably. Haven't tried it yet. 547 | 548 | ## Utilities 549 | 550 | ### luv.self() 551 | 552 | Returns the currently running state, which can be either a thread or a fiber. 553 | 554 | ### luv.stdin, luv.stdout and luv.stderr 555 | 556 | Fiber friendly stream versions of the standard file descriptors 557 | 558 | ### luv.sleep(seconds) 559 | 560 | Fiber friendly version of sleep(). The `seconds` argument may be fractional 561 | with millisecond resolution. 562 | 563 | ### luv.hrtime() 564 | 565 | Returns the current high-resolution time expressed in nanoseconds since 566 | some arbitrary time in the past. May not have nanosecond resolution though. 567 | 568 | ### luv.mem_total() 569 | 570 | Returns the total memory in bytes. 571 | 572 | ### luv.mem_free() 573 | 574 | Returns the free memory in bytes. 575 | 576 | ### luv.cpu_info() 577 | 578 | Returns a table containing an entry for each logical cpu. The entries 579 | have the following fields: 580 | 581 | * model - string containing the model name 582 | * speed - number in mhz 583 | * times - table with the following fields: 584 | * user 585 | * nice 586 | * sys 587 | * idle 588 | * irq 589 | 590 | ### luv.interface_addresses() 591 | 592 | Returns a table containing an entry for each interface address. The 593 | entries have the following fields: 594 | 595 | * name - string 596 | * is_internal - boolean 597 | * address - string (ip4 or ip6 address) 598 | 599 | ## Serialization 600 | 601 | Luv ships with a binary serializer which can serialize and deserialize 602 | Lua tuples. Tuples can contain tables (with cycles), Lua functions (with 603 | upvalues) any scalar value. Function upvalues must themselves be of a type 604 | which can be serialized. Coroutines and C functions can *not* be serialized. 605 | 606 | ### luv.codec.encode(arg1, ..., argN) 607 | 608 | Serializes tuple `arg1` through `argN` and returns a string which can 609 | be passed to `luv.codec.decode`. 610 | 611 | ### luv.codec.decode(string) 612 | 613 | Deserializes `string` previously serialized with a call to `luv.codec.encode` 614 | 615 | Returns the decoded tuple. 616 | 617 | ### Serialization hook 618 | 619 | For userdata and tables, a special hook is provided. If the metatable 620 | has a `__codec` method defined, then that is called. The `__codec` 621 | hook is called with the object as argument and is expected to return 622 | two values. 623 | 624 | The first return value may be either a serializable function or a 625 | string. The second value may be any serializable value. 626 | 627 | If the first return value is a function, then it is called during 628 | deserialization with the second return value as parameter. 629 | 630 | If the first return value is a string, then the deserializer looks 631 | for a function keyed on that string inside Lua's registry table, 632 | and then in the global table, in that order. If no function is 633 | found, then an error is raised. If a function is found, then this 634 | function is called with the second return value is argument. 635 | 636 | For example: 637 | 638 | ```Lua 639 | local luv = require("luv") 640 | 641 | module("my.module", package.seeall) 642 | 643 | Point = { } 644 | Point.__index = Point 645 | Point.new = function() 646 | return setmetatable({ }, Point) 647 | end 648 | Point.move = function(self, x, y) 649 | self.x = x 650 | self.y = y 651 | end 652 | Point.__codec = function(self) 653 | return function(spec) 654 | local Point = require('my.module').Point 655 | return setmetatable(spec, Point) 656 | end, { x = self.x, y = self.y } 657 | end 658 | 659 | 660 | module("main", package.seeall) 661 | 662 | local Point = require("my.module").Point 663 | local obj = Point.new() 664 | obj:move(1, 2) 665 | 666 | local str = luv.codec.encode(obj) 667 | local dec = luv.codec.decode(str) 668 | 669 | assert(dec.x == 1 and dec.y == 2) 670 | assert(type(dec.move) == 'function') 671 | ``` 672 | 673 | ## ØMQ 674 | 675 | Luv provides bindings to ØMQ. The primary motivation for all this is that 676 | I really wanted threads. And ØMQ and threads fit together like a fist in 677 | the eye socket. ØMQ is tied into libuv's polling mechanism and has the same 678 | suspend/resume states behaviour as other I/O watchers. 679 | 680 | You can, of course, use ØMQ from fibers as well. 681 | 682 | ### luv.zmq.create(nthreads) 683 | 684 | Creates a new ØMQ context object. Context objects can be shared across 685 | different threads and may be referenced as upvalues, or passed as 686 | arguments and return values (they survive serialization). 687 | 688 | The `nthreads` argument controls the number of worker threads spawned 689 | by ØMQ's internals, and defaults to `1`. 690 | 691 | ### zmq:socket(type) 692 | 693 | Called on the ØMQ context object to create a socket of type `type`. Socket 694 | types are described by constants defined in the `luv.zmq` table and map 695 | to the standard ØMQ socket types with prefix removed. They are: 696 | 697 | * REQ 698 | * REP 699 | * DEALER 700 | * ROUTER 701 | * PUB 702 | * SUB 703 | * PUSH 704 | * PULL 705 | * PAIR 706 | 707 | The ØMQ docs explain what they all mean: http://zguide.zeromq.org/ 708 | 709 | The socket returned may _not_ be shared between threads. 710 | 711 | ### socket:bind(addr) 712 | 713 | Bind this ØMQ socket to the address provided by `addr`. The `addr` 714 | string is the same as documented by ØMQ (i.e. "tcp://127.0.0.1:8080", etc.) 715 | 716 | ### socket:connect(addr) 717 | 718 | Connect this ØMQ socket to the address provided by `addr`. The `addr` 719 | string is the same as documented by ØMQ (i.e. "tcp://127.0.0.1:8080", etc.) 720 | 721 | ### socket:send(mesg) 722 | 723 | Send a message on the ØMQ socket. 724 | 725 | ### socket:recv() 726 | 727 | Receive a message from the ØMQ socket. 728 | 729 | ### socket:close() 730 | 731 | Close the ØMQ socket. 732 | 733 | ### socket:getsockopt(opt) 734 | 735 | Get a socket option named `opt`. Note that `opt` is a string, not a numeric 736 | constant as with the socket constructor. I'm still undecided which is better. 737 | 738 | See the ØMQ docs. 739 | 740 | ### socket:setsockopt(opt, val) 741 | 742 | Set a socket option named `opt`. Note that `opt` is a string, not a numeric 743 | constant as with the socket constructor. I'm still undecided which is better. 744 | 745 | See the ØMQ docs. 746 | 747 | ### ØMQ Example 748 | 749 | ```Lua 750 | local zmq = luv.zmq.create(1) 751 | local prod = luv.thread.create(function() 752 | local pub = zmq:socket(luv.zmq.PAIR) 753 | pub:bind('inproc://#1') 754 | 755 | print("enter prod:") 756 | for i=1, 10 do 757 | pub:send("tick: "..i) 758 | end 759 | pub:send("STOP") 760 | assert(pub:recv() == "OK") 761 | pub:close() 762 | end) 763 | 764 | local cons = luv.thread.create(function() 765 | local sub = zmq:socket(luv.zmq.PAIR) 766 | sub:connect('inproc://#1') 767 | 768 | print("enter cons") 769 | while true do 770 | local msg = sub:recv() 771 | if msg == "STOP" then 772 | sub:send("OK") 773 | break 774 | end 775 | end 776 | sub:close() 777 | end) 778 | 779 | cons:join() 780 | ``` 781 | 782 | 783 | # ACKNOWLEDGEMENTS 784 | 785 | * Tim Caswell (creationix) and the Luvit authors 786 | * Aleksandar Kordic 787 | * Vladimir Dronnikov (dvv) 788 | 789 | # LICENSE 790 | 791 | Parts Copyright The Luvit Authors 792 | 793 | Copyright 2012 Richard Hundt 794 | 795 | Licensed under the Apache License, Version 2.0 (the "License"); 796 | you may not use this file except in compliance with the License. 797 | You may obtain a copy of the License at 798 | 799 | http://www.apache.org/licenses/LICENSE-2.0 800 | 801 | Unless required by applicable law or agreed to in writing, software 802 | distributed under the License is distributed on an "AS IS" BASIS, 803 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 804 | See the License for the specific language governing permissions and 805 | limitations under the License. 806 | 807 | # TODO 808 | 809 | * uv_poll_t wrapper 810 | * test UDP stuff 811 | * ØMQ devices and utils 812 | * unify ØMQ constants (either strings or numbers) 813 | * finish docs 814 | 815 | 816 | -------------------------------------------------------------------------------- /cmake/FindLua.cmake: -------------------------------------------------------------------------------- 1 | # Locate Lua/LuaJIT library 2 | # This module defines 3 | # LUA_EXECUTABLE, if found 4 | # LUA_FOUND, if false, do not try to link to Lua 5 | # LUA_LIBRARIES 6 | # LUA_INCLUDE_DIR, where to find lua.h 7 | # LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8) 8 | # 9 | # Note that the expected include convention is 10 | # #include "lua.h" 11 | # and not 12 | # #include 13 | # This is because, the lua location is not standardized and may exist 14 | # in locations other than lua/ 15 | 16 | #============================================================================= 17 | # Copyright 2007-2009 Kitware, Inc. 18 | # Modified to support Lua 5.2 by LuaDist 2012 19 | # 20 | # Distributed under the OSI-approved BSD License (the "License"); 21 | # see accompanying file Copyright.txt for details. 22 | # 23 | # This software is distributed WITHOUT ANY WARRANTY; without even the 24 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 25 | # See the License for more information. 26 | #============================================================================= 27 | # (To distribute this file outside of CMake, substitute the full 28 | # License text for the above reference.) 29 | # 30 | # The required version of Lua can be specified using the 31 | # standard syntax, e.g. FIND_PACKAGE(Lua 5.1) 32 | # Otherwise the module will search for any available Lua implementation 33 | 34 | # Always search for non-versioned lua first (recommended) 35 | SET(_POSSIBLE_LUA_INCLUDE include include/lua include/luajit-2.0) 36 | SET(_POSSIBLE_LUA_EXECUTABLE lua luajit luajit2) 37 | SET(_POSSIBLE_LUA_LIBRARY lua) 38 | 39 | # Determine possible naming suffixes (there is no standard for this) 40 | IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 41 | SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}") 42 | ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 43 | SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1") 44 | ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR) 45 | 46 | # Set up possible search names and locations 47 | FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES}) 48 | LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}") 49 | LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}") 50 | LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}") 51 | ENDFOREACH(_SUFFIX) 52 | 53 | # Find the lua executable 54 | FIND_PROGRAM(LUA_EXECUTABLE 55 | NAMES ${_POSSIBLE_LUA_EXECUTABLE} 56 | ) 57 | 58 | # Find the lua header 59 | FIND_PATH(LUA_INCLUDE_DIR lua.h 60 | HINTS 61 | $ENV{LUA_DIR} 62 | PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE} 63 | PATHS 64 | ~/Library/Frameworks 65 | /Library/Frameworks 66 | /usr/local 67 | /usr 68 | /sw # Fink 69 | /opt/local # DarwinPorts 70 | /opt/csw # Blastwave 71 | /opt 72 | ) 73 | 74 | # Find the lua library 75 | FIND_LIBRARY(LUA_LIBRARY 76 | NAMES ${_POSSIBLE_LUA_LIBRARY} 77 | HINTS 78 | $ENV{LUA_DIR} 79 | PATH_SUFFIXES lib64 lib 80 | PATHS 81 | ~/Library/Frameworks 82 | /Library/Frameworks 83 | /usr/local 84 | /usr 85 | /sw 86 | /opt/local 87 | /opt/csw 88 | /opt 89 | ) 90 | 91 | IF(NOT LUA_LIBRARY) 92 | SET(LUA_LIBRARY "rt") 93 | ENDIF() 94 | 95 | IF(LUA_LIBRARY) 96 | # include the math library for Unix 97 | IF(UNIX AND NOT APPLE) 98 | FIND_LIBRARY(LUA_MATH_LIBRARY m) 99 | SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries") 100 | # For Windows and Mac, don't need to explicitly include the math library 101 | ELSE(UNIX AND NOT APPLE) 102 | SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries") 103 | ENDIF(UNIX AND NOT APPLE) 104 | ENDIF(LUA_LIBRARY) 105 | 106 | # Determine Lua version 107 | IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h") 108 | FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_str REGEX "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua .+\"") 109 | 110 | STRING(REGEX REPLACE "^#define[ \t]+LUA_RELEASE[ \t]+\"Lua ([^\"]+)\".*" "\\1" LUA_VERSION_STRING "${lua_version_str}") 111 | UNSET(lua_version_str) 112 | ENDIF() 113 | 114 | INCLUDE(FindPackageHandleStandardArgs) 115 | # handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if 116 | # all listed variables are TRUE 117 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua 118 | REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR 119 | VERSION_VAR LUA_VERSION_STRING) 120 | 121 | MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE) 122 | -------------------------------------------------------------------------------- /cmake/config.h.in: -------------------------------------------------------------------------------- 1 | #define VERSION_MAJOR @VERSION_MAJOR@ 2 | #define VERSION_MINOR @VERSION_MINOR@ 3 | 4 | #cmakedefine USE_ZMQ 5 | #cmakedefine USE_HTTP 6 | -------------------------------------------------------------------------------- /cmake/uv/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_LEGACY_CYGWIN_WIN32 0) 2 | PROJECT(uv) 3 | cmake_minimum_required(VERSION 2.8) 4 | 5 | include_directories(include include/uv-private src src/ares) 6 | 7 | set(UV_COMMON_SRCS 8 | include/uv.h 9 | include/uv-private/ngx-queue.h 10 | include/uv-private/tree.h 11 | src/fs-poll.c 12 | src/inet.c 13 | src/uv-common.c 14 | src/uv-common.h 15 | ) 16 | 17 | 18 | set(UV_WIN32_SRCS 19 | include/uv-private/uv-win.h 20 | src/win/async.c 21 | src/win/atomicops-inl.h 22 | src/win/core.c 23 | src/win/dl.c 24 | src/win/error.c 25 | src/win/fs.c 26 | src/win/fs-event.c 27 | src/win/getaddrinfo.c 28 | src/win/handle.c 29 | src/win/handle-inl.h 30 | src/win/internal.h 31 | src/win/loop-watcher.c 32 | src/win/pipe.c 33 | src/win/thread.c 34 | src/win/poll.c 35 | src/win/process.c 36 | src/win/process-stdio.c 37 | src/win/req.c 38 | src/win/req-inl.h 39 | src/win/signal.c 40 | src/win/stream.c 41 | src/win/stream-inl.h 42 | src/win/tcp.c 43 | src/win/tty.c 44 | src/win/threadpool.c 45 | src/win/timer.c 46 | src/win/udp.c 47 | src/win/util.c 48 | src/win/winapi.c 49 | src/win/winapi.h 50 | src/win/winsock.c 51 | src/win/winsock.h 52 | ) 53 | 54 | set(UV_POSIX_SRCS 55 | include/uv-private/ev.h 56 | include/uv-private/uv-unix.h 57 | include/uv-private/uv-linux.h 58 | include/uv-private/uv-sunos.h 59 | include/uv-private/uv-darwin.h 60 | include/uv-private/uv-bsd.h 61 | src/unix/async.c 62 | src/unix/core.c 63 | src/unix/dl.c 64 | src/unix/error.c 65 | src/unix/ev/ev.c 66 | src/unix/ev/ev_vars.h 67 | src/unix/ev/ev_wrap.h 68 | src/unix/ev/event.h 69 | src/unix/fs.c 70 | src/unix/getaddrinfo.c 71 | src/unix/internal.h 72 | src/unix/loop.c 73 | src/unix/loop-watcher.c 74 | src/unix/pipe.c 75 | src/unix/poll.c 76 | src/unix/process.c 77 | src/unix/signal.c 78 | src/unix/stream.c 79 | src/unix/tcp.c 80 | src/unix/thread.c 81 | src/unix/threadpool.c 82 | src/unix/timer.c 83 | src/unix/tty.c 84 | src/unix/udp.c 85 | ) 86 | 87 | set(UV_TEST_SRCS 88 | test/blackhole-server.c 89 | test/echo-server.c 90 | test/run-tests.c 91 | test/runner.c 92 | test/runner.h 93 | test/test-get-loadavg.c 94 | test/task.h 95 | test/test-util.c 96 | test/test-active.c 97 | test/test-async.c 98 | test/test-callback-stack.c 99 | test/test-callback-order.c 100 | test/test-connection-fail.c 101 | test/test-cwd-and-chdir.c 102 | test/test-delayed-accept.c 103 | test/test-error.c 104 | test/test-fail-always.c 105 | test/test-fs.c 106 | test/test-fs-event.c 107 | test/test-get-currentexe.c 108 | test/test-get-memory.c 109 | test/test-getaddrinfo.c 110 | test/test-getsockname.c 111 | test/test-hrtime.c 112 | test/test-idle.c 113 | test/test-ipc.c 114 | test/test-ipc-send-recv.c 115 | test/test-list.h 116 | test/test-loop-handles.c 117 | test/test-walk-handles.c 118 | test/test-multiple-listen.c 119 | test/test-pass-always.c 120 | test/test-ping-pong.c 121 | test/test-pipe-bind-error.c 122 | test/test-pipe-connect-error.c 123 | test/test-platform-output.c 124 | test/test-poll.c 125 | test/test-poll-close.c 126 | test/test-process-title.c 127 | test/test-ref.c 128 | test/test-run-once.c 129 | test/test-semaphore.c 130 | test/test-shutdown-close.c 131 | test/test-shutdown-eof.c 132 | test/test-signal.c 133 | test/test-spawn.c 134 | test/test-fs-poll.c 135 | test/test-stdio-over-pipes.c 136 | test/test-tcp-bind-error.c 137 | test/test-tcp-bind6-error.c 138 | test/test-tcp-close.c 139 | test/test-tcp-close-while-connecting.c 140 | test/test-tcp-connect-error-after-write.c 141 | test/test-tcp-shutdown-after-write.c 142 | test/test-tcp-flags.c 143 | test/test-tcp-connect-error.c 144 | test/test-tcp-connect-timeout.c 145 | test/test-tcp-connect6-error.c 146 | test/test-tcp-open.c 147 | test/test-tcp-write-error.c 148 | test/test-tcp-write-to-half-open-connection.c 149 | test/test-tcp-writealot.c 150 | test/test-tcp-unexpected-read.c 151 | test/test-threadpool.c 152 | test/test-mutexes.c 153 | test/test-signal.c 154 | test/test-thread.c 155 | test/test-timer-again.c 156 | test/test-timer.c 157 | test/test-tty.c 158 | test/test-udp-dgram-too-big.c 159 | test/test-udp-ipv6.c 160 | test/test-udp-open.c 161 | test/test-udp-options.c 162 | test/test-udp-send-and-recv.c 163 | test/test-udp-multicast-join.c 164 | test/test-dlerror.c 165 | test/test-udp-multicast-ttl.c 166 | ) 167 | 168 | set(UV_BENCHMARK_SRCS 169 | test/benchmark-async.c 170 | test/benchmark-async-pummel.c 171 | test/benchmark-fs-stat.c 172 | test/benchmark-getaddrinfo.c 173 | test/benchmark-list.h 174 | test/benchmark-loop-count.c 175 | test/benchmark-million-timers.c 176 | test/benchmark-multi-accept.c 177 | test/benchmark-ping-pongs.c 178 | test/benchmark-pound.c 179 | test/benchmark-pump.c 180 | test/benchmark-sizes.c 181 | test/benchmark-spawn.c 182 | test/benchmark-thread.c 183 | test/benchmark-tcp-write-batch.c 184 | test/benchmark-udp-pummel.c 185 | test/dns-server.c 186 | test/echo-server.c 187 | test/blackhole-server.c 188 | test/run-benchmarks.c 189 | test/runner.c 190 | test/runner.h 191 | test/task.h 192 | ) 193 | 194 | # set UV_SRCS and UV_LIBS 195 | 196 | if(WIN32) 197 | add_definitions(-D_WIN32_WINNT=0x0600 -DEIO_STACKSIZE=262144 -D_GNU_SOURCE) 198 | set(UV_SRCS ${UV_WIN32_SRCS} ${UV_COMMON_SRCS}) 199 | set(UV_TESTRUNNER_SRCS test/runner-win.c test/runner-win.h) 200 | set(UV_LIBS ws2_32 psapi iphlpapi) 201 | else() 202 | # POSIX common 203 | include_directories(src/unix/ev) 204 | add_definitions(-D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -D_GNU_SOURCE -DEIO_STACKSIZE=262144) 205 | set(UV_TESTRUNNER_SRCS test/runner-unix.c test/runner-unix.h) 206 | 207 | # OS specific thingy 208 | # FIXME: solaris here 209 | if(APPLE) 210 | add_definitions( 211 | -D_DARWIN_USE_64_BIT_INODE=1 212 | -DEV_CONFIG_H=\"config_darwin.h\" 213 | -DEIO_CONFIG_H=\"config_darwin.h\") 214 | set(UV_SRCS 215 | ${UV_POSIX_SRCS} 216 | ${UV_COMMON_SRCS} 217 | src/unix/darwin.c 218 | src/unix/fsevents.c 219 | src/unix/kqueue.c) 220 | set(UV_LIBS m) 221 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") 222 | add_definitions(-DEV_CONFIG_H=\"config_linux.h\" -DEIO_CONFIG_H=\"config_linux.h\") 223 | set(UV_SRCS 224 | ${UV_POSIX_SRCS} 225 | ${UV_COMMON_SRCS} 226 | src/unix/linux/linux-core.c 227 | src/unix/linux/inotify.c 228 | src/unix/linux/syscalls.c 229 | src/unix/linux/syscalls.h 230 | ) 231 | set(UV_LIBS rt m dl) 232 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") 233 | add_definitions(-DEV_CONFIG_H=\"config_freebsd.h\" 234 | -DEIO_CONFIG_H=\"config_freebsd.h\") 235 | set(UV_SRCS 236 | ${UV_POSIX_SRCS} 237 | ${UV_COMMON_SRCS} 238 | src/unix/freebsd.c 239 | src/unix/kqueue.c 240 | ) 241 | set(UV_LIBS m pthread kvm) 242 | elseif(${CMAKE_SYSTEM_NAME} STREQUAL "CYGWIN") 243 | add_definitions(-DEV_CONFIG_H=\"config_cygwin.h\" 244 | -DEIO_CONFIG_H=\"config_cygwin.h\") 245 | set(UV_SRCS 246 | ${UV_POSIX_SRCS} 247 | ${UV_COMMON_SRCS} 248 | src/unix/cygwin.c 249 | ) 250 | set(UV_LIBS rt m dl) 251 | else() 252 | message(FATAL_ERROR 253 | "We don't support this system for now: ${CMAKE_SYSTEM_NAME}") 254 | endif() 255 | endif() 256 | 257 | add_library(uv ${UV_SRCS}) 258 | 259 | 260 | # Tests 261 | 262 | option(UV_BUILD_TESTS "Build libuv tests" OFF) 263 | option(UV_BUILD_BENCHMARKS "Build libuv benchmarks" OFF) 264 | 265 | if(UV_BUILD_TESTS) 266 | if(${CMAKE_SYSTEM_NAME} STREQUAL "CYGWIN") 267 | set_source_files_properties(test/test-fs.c 268 | PROPERTIES 269 | COMPILE_DEFINITIONS "_POSIX_C_SOURCE=1") 270 | endif() 271 | add_executable(uv-tests ${UV_TEST_SRCS} ${UV_TESTRUNNER_SRCS}) 272 | target_link_libraries(uv-tests uv ${UV_LIBS}) 273 | endif() 274 | 275 | if(UV_BUILD_BENCHMARKS) 276 | add_executable(uv-benchmarks 277 | ${UV_BENCHMARK_SRCS} 278 | ${UV_TESTRUNNER_SRCS} test/${nam}) 279 | target_link_libraries(uv-benchmarks uv ${UV_LIBS}) 280 | endif() 281 | 282 | -------------------------------------------------------------------------------- /cmake/zmq/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # CMake build script for ZeroMQ on Windows 2 | 3 | cmake_minimum_required (VERSION 2.8) 4 | project (zmq) 5 | 6 | option (WITH_OPENPGM "Build with support for OpenPGM" OFF) 7 | 8 | # WARNING: Windows Python will override Cygwin yet not work with Asciidoc. 9 | #find_package (PythonInterp REQUIRED) 10 | # Workaround, manually set Python location 11 | set(PYTHON_EXECUTABLE c:/cygwin/bin/python2.6.exe CACHE FILEPATH "Python interpreter executable") 12 | # TODO: Replace with FindAsciidoc.cmake 13 | set(ASCIIDOC_EXECUTABLE c:/cygwin/bin/asciidoc CACHE FILEPATH "AsciiDoc executable") 14 | 15 | set(OPENPGM_ROOT /libpgm/libpgm-5.1.118-1~dfsg/openpgm/pgm CACHE PATH "Location of OpenPGM") 16 | 17 | mark_as_advanced(PYTHON_EXECUTABLE ASCIIDOC_EXECUTABLE) 18 | 19 | #----------------------------------------------------------------------------- 20 | # force off-tree build 21 | 22 | if(${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR}) 23 | message(FATAL_ERROR "CMake generation is not allowed within the source directory! 24 | Remove the CMakeCache.txt file and try again from another folder, e.g.: 25 | 26 | del CMakeCache.txt 27 | mkdir cmake-make 28 | cd cmake-make 29 | cmake .. 30 | ") 31 | endif(${PROJECT_SOURCE_DIR} STREQUAL ${PROJECT_BINARY_DIR}) 32 | 33 | #----------------------------------------------------------------------------- 34 | # default to Release build 35 | 36 | if(NOT CMAKE_BUILD_TYPE) 37 | set(CMAKE_BUILD_TYPE Release CACHE STRING 38 | "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." 39 | FORCE) 40 | endif(NOT CMAKE_BUILD_TYPE) 41 | 42 | set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}) 43 | 44 | #----------------------------------------------------------------------------- 45 | # platform specifics 46 | 47 | if (WIN32) 48 | add_definitions( 49 | -DWIN32 50 | -DDLL_EXPORT 51 | # NB: May require tweaking for highly connected applications. 52 | -DFD_SETSIZE=1024 53 | -D_CRT_SECURE_NO_WARNINGS 54 | ) 55 | 56 | # Parallel make. 57 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") 58 | 59 | # Optimization flags. 60 | # http://msdn.microsoft.com/en-us/magazine/cc301698.aspx 61 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /GL") 62 | set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} /LTCG") 63 | set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /LTCG") 64 | set(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /LTCG") 65 | 66 | endif (WIN32) 67 | 68 | #----------------------------------------------------------------------------- 69 | # source files 70 | 71 | set(cxx-sources 72 | address.cpp 73 | clock.cpp 74 | ctx.cpp 75 | dealer.cpp 76 | decoder.cpp 77 | devpoll.cpp 78 | dist.cpp 79 | encoder.cpp 80 | epoll.cpp 81 | err.cpp 82 | fq.cpp 83 | io_object.cpp 84 | io_thread.cpp 85 | ip.cpp 86 | ipc_address.cpp 87 | ipc_connecter.cpp 88 | ipc_listener.cpp 89 | kqueue.cpp 90 | lb.cpp 91 | mailbox.cpp 92 | msg.cpp 93 | mtrie.cpp 94 | object.cpp 95 | options.cpp 96 | own.cpp 97 | pair.cpp 98 | pgm_receiver.cpp 99 | pgm_sender.cpp 100 | pgm_socket.cpp 101 | pipe.cpp 102 | poll.cpp 103 | poller_base.cpp 104 | precompiled.cpp 105 | proxy.cpp 106 | pub.cpp 107 | pull.cpp 108 | push.cpp 109 | random.cpp 110 | reaper.cpp 111 | rep.cpp 112 | req.cpp 113 | router.cpp 114 | select.cpp 115 | session_base.cpp 116 | signaler.cpp 117 | socket_base.cpp 118 | stream_engine.cpp 119 | sub.cpp 120 | tcp.cpp 121 | tcp_address.cpp 122 | tcp_connecter.cpp 123 | tcp_listener.cpp 124 | thread.cpp 125 | trie.cpp 126 | v1_decoder.cpp 127 | v1_encoder.cpp 128 | xpub.cpp 129 | xsub.cpp 130 | zmq.cpp 131 | zmq_utils.cpp 132 | ) 133 | 134 | set(rc-sources 135 | version.rc 136 | ) 137 | 138 | include_directories(include ${PROJECT_BINARY_DIR}) 139 | 140 | #----------------------------------------------------------------------------- 141 | # optional modules 142 | 143 | if(WITH_OPENPGM) 144 | add_definitions( 145 | -DZMQ_HAVE_OPENPGM 146 | ) 147 | 148 | include_directories( 149 | ${OPENPGM_ROOT}/include 150 | ) 151 | if (CMAKE_SIZEOF_VOID_P EQUAL 8) 152 | # Win64 153 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 154 | set(OPENPGM_LIBRARYDIR ${OPENPGM_ROOT}/debug64/lib) 155 | else (CMAKE_BUILD_TYPE STREQUAL "Debug") 156 | set(OPENPGM_LIBRARYDIR ${OPENPGM_ROOT}/build64/lib) 157 | endif (CMAKE_BUILD_TYPE STREQUAL "Debug") 158 | else (CMAKE_SIZEOF_VOID_P EQUAL 8) 159 | # Win32 160 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 161 | set(OPENPGM_LIBRARYDIR ${OPENPGM_ROOT}/debug/lib) 162 | else (CMAKE_BUILD_TYPE STREQUAL "Debug") 163 | set(OPENPGM_LIBRARYDIR ${OPENPGM_ROOT}/build/lib) 164 | endif (CMAKE_BUILD_TYPE STREQUAL "Debug") 165 | endif (CMAKE_SIZEOF_VOID_P EQUAL 8) 166 | link_directories( 167 | ${OPENPGM_LIBRARYDIR} 168 | ) 169 | endif(WITH_OPENPGM) 170 | 171 | #----------------------------------------------------------------------------- 172 | # source generators 173 | 174 | foreach (source ${cxx-sources}) 175 | list(APPEND sources ${PROJECT_SOURCE_DIR}/src/${source}) 176 | endforeach() 177 | 178 | foreach (source ${rc-sources}) 179 | list(APPEND sources ${PROJECT_BINARY_DIR}/${source}) 180 | configure_file(${PROJECT_SOURCE_DIR}/src/${source}.in ${PROJECT_BINARY_DIR}/${source}) 181 | endforeach() 182 | 183 | if (WIN32) 184 | add_custom_command( 185 | OUTPUT ${PROJECT_BINARY_DIR}/platform.hpp 186 | COMMAND ${CMAKE_COMMAND} 187 | ARGS -E 188 | copy 189 | ${PROJECT_SOURCE_DIR}/builds/msvc/platform.hpp 190 | ${PROJECT_BINARY_DIR}/platform.hpp 191 | DEPENDS ${PROJECT_SOURCE_DIR}/builds/msvc/platform.hpp 192 | ) 193 | else () 194 | file(WRITE "${PROJECT_BINARY_DIR}/platform.hpp" "#define ZMQ_HAVE_LINUX\n#define ZMQ_HAVE_UIO 1\n") 195 | endif (WIN32) 196 | list(APPEND sources ${PROJECT_BINARY_DIR}/platform.hpp) 197 | 198 | if (CMAKE_SIZEOF_VOID_P EQUAL 8) 199 | set (nsis-template ${PROJECT_SOURCE_DIR}/cmake/NSIS.template64.in) 200 | else (CMAKE_SIZEOF_VOID_P EQUAL 8) 201 | set (nsis-template ${PROJECT_SOURCE_DIR}/cmake/NSIS.template32.in) 202 | endif (CMAKE_SIZEOF_VOID_P EQUAL 8) 203 | add_custom_command( 204 | OUTPUT ${PROJECT_BINARY_DIR}/NSIS.template.in 205 | COMMAND ${CMAKE_COMMAND} 206 | ARGS -E 207 | copy 208 | ${nsis-template} 209 | ${PROJECT_BINARY_DIR}/NSIS.template.in 210 | DEPENDS ${nsis-template} 211 | ) 212 | 213 | #----------------------------------------------------------------------------- 214 | # output 215 | 216 | 217 | if (UNIX) 218 | add_library(zmq STATIC ${sources} ${PROJECT_BINARY_DIR}/NSIS.template.in) 219 | find_package(Threads REQUIRED) 220 | target_link_libraries(zmq stdc++ ${CMAKE_THREAD_LIBS_INIT} rt) 221 | #set_target_properties(libzmq PROPERTIES PREFIX "") 222 | else () 223 | add_library(libzmq SHARED ${sources} ${PROJECT_BINARY_DIR}/NSIS.template.in) 224 | target_link_libraries(libzmq ws2_32.lib rpcrt4.lib) 225 | set_target_properties(libzmq PROPERTIES RELEASE_POSTFIX "${_zmq_COMPILER}-mt" DEBUG_POSTFIX "${_zmq_COMPILER}-mt-gd") 226 | endif (UNIX) 227 | 228 | include (InstallRequiredSystemLibraries) 229 | 230 | # end of file 231 | -------------------------------------------------------------------------------- /examples/bench.lua: -------------------------------------------------------------------------------- 1 | local luv = require("luv") 2 | 3 | local work = function(a) 4 | for i=1, 10000000 do --> that's a big number, so use LuaJIT-2 :) 5 | coroutine.yield() 6 | end 7 | end 8 | 9 | local f1 = luv.fiber.create(work, "a") 10 | local f2 = luv.fiber.create(work, "b") 11 | 12 | f1:ready() 13 | f2:ready() 14 | 15 | f1:join() 16 | f2:join() 17 | 18 | 19 | -------------------------------------------------------------------------------- /examples/codec.lua: -------------------------------------------------------------------------------- 1 | local luv = require("luv") 2 | local reg = debug.getregistry() 3 | assert(reg["luv:lib:decoder"]) 4 | assert(type(reg["luv:lib:decoder"]) == "function") 5 | 6 | local str = luv.codec.encode(luv) 7 | print(string.format('%q', str)) 8 | 9 | local lib = luv.codec.decode(str) 10 | print(lib) 11 | assert(lib == luv) 12 | 13 | local tup = { answer = 42 } 14 | local fun = function() 15 | return tup 16 | end 17 | 18 | local str = luv.codec.encode(fun) 19 | print(string.format('%q', str)) 20 | local fun = luv.codec.decode(str) 21 | print(fun().answer) 22 | 23 | local obj = { answer = 42 } 24 | setmetatable(obj, { 25 | __codec = function(o) 26 | print("__codec called with:", o) 27 | local u = { "upval" } 28 | return function(v) 29 | print("decoder called with:", v, "upvalue is ", u[1]) 30 | return { answer = v } 31 | end, 42 32 | end 33 | }) 34 | 35 | local str = luv.codec.encode(obj) 36 | print(string.format('%q', str)) 37 | local obj = luv.codec.decode(str) 38 | assert(obj.answer == 42) 39 | 40 | local obj = { } 41 | obj.a = { } 42 | obj.a.b = { } 43 | obj.a.b.c = obj.a.b 44 | local str = luv.codec.encode(obj) 45 | local dec = luv.codec.decode(str) 46 | assert(obj.a) 47 | assert(obj.a.b) 48 | assert(obj.a.b.c == obj.a.b) 49 | 50 | 51 | -------------------------------------------------------------------------------- /examples/fiber.lua: -------------------------------------------------------------------------------- 1 | local luv = require('luv') 2 | assert(luv) 3 | 4 | local boss = luv.fiber.create(function() 5 | 6 | print("boss enter") 7 | 8 | local work = function(a, b) 9 | print("work:", a, b) 10 | for i=1, 10 do 11 | print(a, "tick:", i, a) 12 | luv.fiber.yield() 13 | end 14 | return b, "cheese" 15 | end 16 | 17 | local f1 = luv.fiber.create(work, "a1", "b1") 18 | local f2 = luv.fiber.create(work, "a2", "b2") 19 | 20 | f1:ready() 21 | f2:ready() 22 | 23 | print("join f1:", f1:join()) 24 | print("join f2:", f2:join()) 25 | 26 | return 1,2,3 27 | end) 28 | print("BOSS: ", boss) 29 | --boss:ready() 30 | 31 | print("join boss:", boss:join()) 32 | 33 | -------------------------------------------------------------------------------- /examples/fs.lua: -------------------------------------------------------------------------------- 1 | local luv = require("luv") 2 | 3 | local t = luv.fs.stat("/tmp") 4 | for k,v in pairs(t) do 5 | print(k, "=>", v) 6 | end 7 | 8 | local f1 = luv.fiber.create(function() 9 | print("enter") 10 | local file = luv.fs.open("/tmp/cheese.ric", "w+", "664") 11 | print("file:", file) 12 | print("write:", file:write("Hey Bro!")) 13 | print("close:", file:close()) 14 | end) 15 | 16 | f1:ready() 17 | f1:join() 18 | 19 | local file = luv.fs.open("/tmp/cheese.ric", "r", "664") 20 | print("READ:", file:read()) 21 | file:close() 22 | print("DELETE:", luv.fs.unlink("/tmp/cheese.ric")) 23 | 24 | -------------------------------------------------------------------------------- /examples/http.lua: -------------------------------------------------------------------------------- 1 | local luv = require('luv') 2 | local http_parser = require('http.parser') 3 | 4 | local http = { } 5 | 6 | function http.create_server(handler, port, host, backlog) 7 | 8 | port = port or 8080 9 | host = host or '127.0.0.1' 10 | 11 | local server = luv.net.tcp() 12 | --print('SERVER:', server) 13 | server:bind(host, port) 14 | server:listen(backlog) 15 | 16 | while not server.done do 17 | --print('ACCEPT LOOP TOP') 18 | 19 | -- accept client 20 | local client = luv.net.tcp() 21 | local rc = server:accept(client) -- block here 22 | if not rc then 23 | handler(client, 'error', 'accept') 24 | -- continue?! 25 | end 26 | 27 | -- setup client reader 28 | luv.fiber.create(function(client) 29 | --print('CHILD') 30 | local request = { } 31 | -- setup http parser 32 | local parser 33 | parser = http_parser.request({ 34 | on_message_begin = function() 35 | --print('MSGBEGIN') 36 | request = { } 37 | request.client = client 38 | request.headers = { } 39 | end, 40 | on_url = function(url) 41 | --print('URL', url) 42 | request.url = url 43 | -- TODO: parse url 44 | end, 45 | on_header = function(hkey, hval) 46 | --print('HEADER', hkey, hval) 47 | request.headers[hkey:lower()] = hval 48 | end, 49 | on_headers_complete = function() 50 | --print('HDRDONE') 51 | request.method = parser:method() 52 | request.upgrade = parser:is_upgrade() 53 | request.should_keep_alive = parser:should_keep_alive() 54 | end, 55 | on_body = function(chunk) 56 | --print('BODY', chunk) 57 | if chunk ~= nil then 58 | handler(request, 'data', chunk) 59 | else 60 | --print('MSGEND!') 61 | --p(request) 62 | handler(request, 'end') 63 | end 64 | end, 65 | on_message_complete = function() 66 | --print('MSGDONE') 67 | --if parser.should_keep_alive() then 68 | parser:reset() 69 | --end 70 | end 71 | }) 72 | -- feed client data to http parser 73 | while true do 74 | --print('READ LOOP TOP') 75 | local nread, chunk = client:read() 76 | --print('READ:', nread, chunk) 77 | if nread then 78 | local nparsed = parser:execute(chunk) 79 | -- parsed not the whole chunk? 80 | if nparsed < nread then 81 | -- pass unparsed data verbatim in case of upgrade mode 82 | if request.upgrade then 83 | handler(request, 'data', chunk:sub(nparsed + 1)) 84 | -- report parse error unless upgrade mode 85 | else 86 | handler(request, 'error', 'parse error') 87 | end 88 | end 89 | else 90 | --print('CLOSING') 91 | -- read error? 92 | --if nread == false then 93 | -- report error 94 | --handler(request, 'error', 'short read', nread) 95 | handler(request, 'error', 'short read', chunk) 96 | -- and close the client 97 | client:close() 98 | --end 99 | break 100 | end 101 | end 102 | end, client):ready() 103 | end 104 | 105 | return server 106 | 107 | end 108 | 109 | -- export 110 | return http 111 | -------------------------------------------------------------------------------- /examples/http_hellosvr.lua: -------------------------------------------------------------------------------- 1 | pcall(require, 'luarocks.loader') -- honor luarocks search path 2 | local luv = require('luv') 3 | local http = require('http') 4 | 5 | local function p(x) 6 | for k, v in pairs(x) do 7 | print(k, v) 8 | end 9 | end 10 | 11 | local function handler(req, event, ...) 12 | print(req, event, ...) 13 | if event ~= 'end' then return end 14 | local response = ("Hello\n") --:rep(1000) 15 | local rc 16 | if req.should_keep_alive then 17 | rc = req.client:write("HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nContent-Length: " .. #response .. "\r\n\r\n" .. response) 18 | else 19 | rc = req.client:write("HTTP/1.0 200 OK\r\nConnection: close\r\nContent-Length: " .. #response .. "\r\n\r\n" .. response) 20 | end 21 | if rc < 0 then 22 | print("WRITE ERR:", rc) 23 | end 24 | req.client:shutdown() 25 | end 26 | 27 | local main = luv.fiber.create(function() 28 | local server = http.create_server(handler, 8080, '0.0.0.0', 32768) 29 | end):ready() 30 | 31 | print('HTTP server listening to http://0.0.0.0:8080/. Hit CTRL+C to exit.') 32 | main:join() 33 | -------------------------------------------------------------------------------- /examples/proc.lua: -------------------------------------------------------------------------------- 1 | local luv = require("luv") 2 | 3 | local f = luv.fiber.create(function() 4 | local fh = luv.fs.open("/tmp/foo", "w+",'644') 5 | fh:write("Hey Globe!") 6 | fh:close() 7 | local p = luv.process.spawn("/bin/cat", { "/tmp/foo", stdout = luv.stdout }) 8 | print("SPAWNED:", p) 9 | end) 10 | f:ready() 11 | f:join() 12 | 13 | -------------------------------------------------------------------------------- /examples/stdfh.lua: -------------------------------------------------------------------------------- 1 | local luv = require('luv') 2 | 3 | luv.stdout:write("Hello World!") 4 | local mesg = luv.stdin:read() 5 | luv.stderr:write("thanks, you said:"..mesg) 6 | 7 | 8 | -------------------------------------------------------------------------------- /examples/tcp_client.lua: -------------------------------------------------------------------------------- 1 | local luv = require("luv") 2 | local host = luv.net.getaddrinfo("www.google.com") 3 | local sock = luv.net.tcp() 4 | print("conn:", sock:connect(host, 80)) 5 | print("write:", sock:write("GET / HTTP/1.0\r\nHost: www.google.com\r\n\r\n")) 6 | print("read:", sock:read()) 7 | 8 | -------------------------------------------------------------------------------- /examples/tcp_echosvr.lua: -------------------------------------------------------------------------------- 1 | local luv = require('luv') 2 | 3 | local server = luv.net.tcp() 4 | 5 | print("SERVER:", server) 6 | print("BIND:", server:bind("127.0.0.1", 8080)) 7 | print("LISTEN:", server:listen()) 8 | 9 | while true do 10 | print("ACCEPT LOOP TOP") 11 | 12 | local client = luv.net.tcp() 13 | print("ACCEPT:", server:accept(client)) -- block here 14 | 15 | local fib = luv.fiber.create(function() 16 | print("CHILD") 17 | while true do 18 | print("READ LOOP TOP") 19 | local got, str = client:read(1024) 20 | print("READ:", got, str) 21 | if got then 22 | client:write("you said: "..str) 23 | print("WRITE OK") 24 | else 25 | print("CLOSING") 26 | client:close() 27 | break 28 | end 29 | end 30 | end) 31 | fib:ready() 32 | end 33 | 34 | -------------------------------------------------------------------------------- /examples/thread.lua: -------------------------------------------------------------------------------- 1 | local luv = require("luv") 2 | 3 | local t1 = luv.thread.spawn(function(id) 4 | local f1 = luv.fiber.create(function() 5 | for i=1, 100 do 6 | print(id, "tick: ", i) 7 | end 8 | end) 9 | f1:join() 10 | return "answer" 11 | end, "A") 12 | local t2 = luv.thread.spawn(function(id) 13 | local f1 = luv.fiber.create(function() 14 | for i=1, 100 do 15 | print(id, "tick: ", i) 16 | end 17 | end) 18 | f1:join() 19 | return 42 20 | end, "B") 21 | 22 | 23 | print("threads:", t1, t2) 24 | 25 | print("JOIN:", t1:join(), t2:join()) 26 | 27 | -------------------------------------------------------------------------------- /examples/timer.lua: -------------------------------------------------------------------------------- 1 | local luv = require('luv') 2 | 3 | local f1 = luv.fiber.create(function() 4 | print("ENTER") 5 | local t1 = luv.timer.create() 6 | t1:start(1000, 100) 7 | for i=1, 10 do 8 | print("tick:", i) 9 | print(t1:wait()) 10 | end 11 | t1:stop() 12 | end) 13 | 14 | f1:join() 15 | 16 | -------------------------------------------------------------------------------- /examples/timer2.lua: -------------------------------------------------------------------------------- 1 | local luv = require("luv") 2 | 3 | local t1 = luv.timer.create() 4 | t1:start(1000, 200) 5 | 6 | local worker1 = function() 7 | for i=1, 10 do 8 | t1:wait() 9 | print("A", "tick:", i) 10 | end 11 | end 12 | 13 | print("sleep half second...") 14 | luv.sleep(0.5) 15 | print("ok") 16 | 17 | local worker2 = function() 18 | for i=1, 10 do 19 | t1:wait() 20 | print("B", "tick:", i) 21 | end 22 | end 23 | 24 | local f1 = luv.fiber.create(worker1) 25 | local f2 = luv.fiber.create(worker2) 26 | 27 | f1:ready() 28 | f2:ready() 29 | 30 | f1:join() 31 | f2:join() 32 | 33 | t1:stop() 34 | 35 | print("DONE") 36 | 37 | -------------------------------------------------------------------------------- /examples/zmq.lua: -------------------------------------------------------------------------------- 1 | local luv = require('luv') 2 | 3 | local zmq = luv.zmq.create(1) 4 | local prod = luv.thread.create(function() 5 | local pub = zmq:socket(luv.zmq.PAIR) 6 | pub:bind('inproc://#1') 7 | 8 | print("enter prod:") 9 | for i=1, 1000000 do 10 | pub:send("tick: "..i) 11 | end 12 | print("waiting for OK") 13 | print(pub:recv()) 14 | print("GOT IT") 15 | pub:close() 16 | end) 17 | 18 | local cons = luv.thread.create(function() 19 | local sub = zmq:socket(luv.zmq.PAIR) 20 | sub:connect('inproc://#1') 21 | 22 | print("enter cons") 23 | for i=1, 1000000 do 24 | sub:recv() 25 | end 26 | 27 | sub:send('OK') 28 | sub:close() 29 | end) 30 | 31 | cons:join() 32 | 33 | 34 | -------------------------------------------------------------------------------- /examples/zmq_pull.lua: -------------------------------------------------------------------------------- 1 | local luv = require('luv') 2 | 3 | local zmq = luv.zmq.create(2) 4 | 5 | local cons = luv.fiber.create(function() 6 | local sub = zmq:socket(luv.zmq.PULL) 7 | sub:connect('tcp://127.0.0.1:1234') 8 | 9 | print("enter cons") 10 | for i=1, 10 do 11 | local msg = sub:recv() 12 | print("GOT: "..msg) 13 | end 14 | 15 | sub:close() 16 | end) 17 | 18 | cons:ready() 19 | cons:join() 20 | 21 | -------------------------------------------------------------------------------- /examples/zmq_push.lua: -------------------------------------------------------------------------------- 1 | local luv = require('luv') 2 | 3 | local zmq = luv.zmq.create(2) 4 | 5 | local pub = zmq:socket(luv.zmq.PUSH) 6 | pub:bind('tcp://127.0.0.1:1234') 7 | 8 | local prod = luv.fiber.create(function() 9 | print("enter prod:") 10 | for i=1, 10 do 11 | pub:send("tick: "..i) 12 | end 13 | pub:close() 14 | end) 15 | 16 | prod:ready() 17 | prod:join() 18 | 19 | -------------------------------------------------------------------------------- /luv-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "luv" 2 | version = "scm-1" 3 | source = { 4 | url = "git://github.com/richardhundt/luv", 5 | } 6 | description = { 7 | summary = "Thermonuclear battery pack for Lua", 8 | detailed = "Luv is an attempt to do libuv bindings to Lua in a style more suited to a language with coroutines than edge-triggered event-loop style programming with callbacks", 9 | homepage = "https://github.com/richardhundt/luv", 10 | license = "http://www.apache.org/licenses/LICENSE-2.0", 11 | } 12 | dependencies = { 13 | "lua >= 5.1" 14 | } 15 | build = { 16 | type = "command", 17 | build_command = "cmake -E make_directory build && cd build && cmake -D INSTALL_CMOD=$(LIBDIR) .. && $(MAKE)", 18 | install_command = "cd build && $(MAKE) install", 19 | } 20 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # configuration 3 | # 4 | # comment out to not include zmq. may be useful for embedded devices 5 | USE_ZMQ := 1 6 | 7 | ########## 8 | 9 | LUADIR = /usr/local/include/luajit-2.0 10 | 11 | COPT = -O2 -fPIC 12 | 13 | CWARNS = -Wall -Werror 14 | 15 | CFLAGS += $(CWARNS) $(COPT) -I$(LUADIR) -I./uv/include 16 | ifdef USE_ZMQ 17 | CFLAGS += -I./zmq/include 18 | endif 19 | 20 | OS_NAME = $(shell uname -s) 21 | MH_NAME = $(shell uname -m) 22 | 23 | LDFLAGS += -lm -ldl -lpthread 24 | 25 | AR = ar 26 | 27 | ifeq ($(OS_NAME), Darwin) 28 | LDFLAGS += -bundle -undefined dynamic_lookup -framework CoreServices 29 | ifeq ($(MH_NAME), x86_64) 30 | endif 31 | else 32 | LDFLAGS += -shared -lrt 33 | ifdef USE_ZMQ 34 | LDFLAGS += -lstdc++ 35 | endif 36 | endif 37 | 38 | #SRCS := $(wildcard *.c) 39 | #SRCS := luv.c luv_object.c luv_cond.c luv_core.c luv_fs.c luv_timer.c luv_stream.c luv_net.c luv_zmq.c luv_thread.c 40 | SRCS := luv.c \ 41 | luv_cond.c \ 42 | luv_state.c \ 43 | luv_fiber.c \ 44 | luv_thread.c \ 45 | luv_codec.c \ 46 | luv_object.c \ 47 | luv_timer.c \ 48 | luv_idle.c \ 49 | luv_fs.c \ 50 | luv_stream.c \ 51 | luv_pipe.c \ 52 | luv_net.c \ 53 | luv_process.c 54 | ifdef USE_ZMQ 55 | CFLAGS += -DUSE_ZMQ 56 | SRCS += luv_zmq.c 57 | endif 58 | OBJS := $(patsubst %.c,%.o,$(SRCS)) 59 | 60 | LIBS = uv/libuv.a 61 | ifdef USE_ZMQ 62 | LIBS += zmq/src/.libs/libzmq.a 63 | endif 64 | 65 | all: luv.so libluv.a 66 | deps: $(LIBS) 67 | 68 | luv.so: $(OBJS) $(LIBS) 69 | $(CC) -o $@ $^ $(LDFLAGS) 70 | 71 | libluv.a: $(OBJS) $(LIBS) 72 | $(AR) -rcs $@ $^ 73 | 74 | $(OBJS): 75 | $(CC) -c $(CFLAGS) $(SRCS) 76 | 77 | uv/libuv.a: 78 | $(MAKE) CFLAGS="-fPIC" -C ./uv 79 | 80 | zmq/src/.libs/libzmq.a: zmq/Makefile 81 | $(MAKE) CXXFLAGS="-fPIC" -C ./zmq 82 | 83 | zmq/Makefile: 84 | # N.B. this requires auto* tools which is PITA for embedded systems 85 | cd ./zmq && ./autogen.sh && ./configure --disable-shared 86 | 87 | clean: 88 | rm -f *.o *.so *.a 89 | 90 | realclean: clean 91 | $(MAKE) -C ./uv clean 92 | $(MAKE) -C ./zmq clean 93 | 94 | .PHONY: all clean realclean deps 95 | .SILENT: 96 | -------------------------------------------------------------------------------- /src/luv.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | #include "lua.h" 8 | #include "lualib.h" 9 | #include "lauxlib.h" 10 | 11 | #ifdef __cplusplus 12 | } 13 | #endif 14 | 15 | 16 | #include "luv.h" 17 | 18 | static int MAIN_INITIALIZED = 0; 19 | 20 | int luvL_traceback(lua_State* L) { 21 | lua_getfield(L, LUA_GLOBALSINDEX, "debug"); 22 | if (!lua_istable(L, -1)) { 23 | lua_pop(L, 1); 24 | return 1; 25 | } 26 | lua_getfield(L, -1, "traceback"); 27 | if (!lua_isfunction(L, -1)) { 28 | lua_pop(L, 2); 29 | return 1; 30 | } 31 | 32 | lua_pushvalue(L, 1); /* pass error message */ 33 | lua_pushinteger(L, 2); /* skip this function and traceback */ 34 | lua_call(L, 2, 1); /* call debug.traceback */ 35 | 36 | return 1; 37 | } 38 | 39 | int luvL_lib_decoder(lua_State* L) { 40 | const char* name = lua_tostring(L, -1); 41 | lua_getfield(L, LUA_REGISTRYINDEX, name); 42 | TRACE("LIB DECODE HOOK: %s\n", name); 43 | assert(lua_istable(L, -1)); 44 | return 1; 45 | } 46 | 47 | /* return "luv:lib:decoder", */ 48 | int luvL_lib_encoder(lua_State* L) { 49 | TRACE("LIB ENCODE HOOK\n"); 50 | lua_pushstring(L, "luv:lib:decoder"); 51 | lua_getfield(L, 1, "__name"); 52 | assert(!lua_isnil(L, -1)); 53 | return 2; 54 | } 55 | 56 | int luvL_new_module(lua_State* L, const char* name, luaL_Reg* funcs) { 57 | lua_newtable(L); 58 | 59 | lua_pushstring(L, name); 60 | lua_setfield(L, -2, "__name"); 61 | 62 | lua_pushvalue(L, -1); 63 | lua_setmetatable(L, -2); 64 | 65 | lua_pushcfunction(L, luvL_lib_encoder); 66 | lua_setfield(L, -2, "__codec"); 67 | 68 | lua_pushvalue(L, -1); 69 | lua_setfield(L, LUA_REGISTRYINDEX, name); 70 | 71 | if (funcs) { 72 | luaL_register(L, NULL, funcs); 73 | } 74 | return 1; 75 | } 76 | 77 | int luvL_new_class(lua_State* L, const char* name, luaL_Reg* meths) { 78 | luaL_newmetatable(L, name); 79 | lua_pushvalue(L, -1); 80 | lua_setfield(L, -2, "__index"); 81 | if (meths) { 82 | luaL_register(L, NULL, meths); 83 | } 84 | return 1; 85 | } 86 | 87 | uv_loop_t* luvL_event_loop(lua_State* L) { 88 | return luvL_state_self(L)->loop; 89 | } 90 | 91 | static void _sleep_cb(uv_timer_t* handle, int status) { 92 | luvL_state_ready((luv_state_t*)handle->data); 93 | free(handle); 94 | } 95 | static int luv_sleep(lua_State* L) { 96 | lua_Number timeout = luaL_checknumber(L, 1); 97 | luv_state_t* state = luvL_state_self(L); 98 | uv_timer_t* timer = (uv_timer_t*)malloc(sizeof(uv_timer_t)); 99 | timer->data = state; 100 | uv_timer_init(luvL_event_loop(L), timer); 101 | uv_timer_start(timer, _sleep_cb, (long)(timeout * 1000), 0L); 102 | return luvL_state_suspend(state); 103 | } 104 | 105 | static int luv_mem_free(lua_State* L) { 106 | lua_pushinteger(L, uv_get_free_memory()); 107 | return 1; 108 | } 109 | 110 | static int luv_mem_total(lua_State* L) { 111 | lua_pushinteger(L, uv_get_total_memory()); 112 | return 1; 113 | } 114 | 115 | static int luv_hrtime(lua_State* L) { 116 | lua_pushinteger(L, uv_hrtime()); 117 | return 1; 118 | } 119 | 120 | static int luv_self(lua_State* L) { 121 | lua_pushthread(L); 122 | lua_gettable(L, LUA_REGISTRYINDEX); 123 | return 1; 124 | } 125 | 126 | static int luv_cpu_info(lua_State* L) { 127 | int size, i; 128 | uv_cpu_info_t* info; 129 | uv_err_t err = uv_cpu_info(&info, &size); 130 | 131 | lua_settop(L, 0); 132 | 133 | if (err.code) { 134 | lua_pushboolean(L, 0); 135 | luaL_error(L, uv_strerror(err)); 136 | return 2; 137 | } 138 | 139 | lua_newtable(L); 140 | 141 | for (i = 0; i < size; i++) { 142 | lua_newtable(L); 143 | 144 | lua_pushstring(L, info[i].model); 145 | lua_setfield(L, -2, "model"); 146 | 147 | lua_pushinteger(L, (lua_Integer)info[i].speed); 148 | lua_setfield(L, -2, "speed"); 149 | 150 | lua_newtable(L); /* times */ 151 | 152 | lua_pushinteger(L, (lua_Integer)info[i].cpu_times.user); 153 | lua_setfield(L, -2, "user"); 154 | 155 | lua_pushinteger(L, (lua_Integer)info[i].cpu_times.nice); 156 | lua_setfield(L, -2, "nice"); 157 | 158 | lua_pushinteger(L, (lua_Integer)info[i].cpu_times.sys); 159 | lua_setfield(L, -2, "sys"); 160 | 161 | lua_pushinteger(L, (lua_Integer)info[i].cpu_times.idle); 162 | lua_setfield(L, -2, "idle"); 163 | 164 | lua_pushinteger(L, (lua_Integer)info[i].cpu_times.irq); 165 | lua_setfield(L, -2, "irq"); 166 | 167 | lua_setfield(L, -2, "times"); 168 | 169 | lua_rawseti(L, 1, i + 1); 170 | } 171 | 172 | uv_free_cpu_info(info, size); 173 | return 1; 174 | } 175 | 176 | static int luv_interface_addresses(lua_State* L) { 177 | int size, i; 178 | char buf[INET6_ADDRSTRLEN]; 179 | 180 | uv_interface_address_t* info; 181 | uv_err_t err = uv_interface_addresses(&info, &size); 182 | 183 | lua_settop(L, 0); 184 | 185 | if (err.code) { 186 | lua_pushboolean(L, 0); 187 | luaL_error(L, uv_strerror(err)); 188 | return 2; 189 | } 190 | 191 | lua_newtable(L); 192 | 193 | for (i = 0; i < size; i++) { 194 | uv_interface_address_t addr = info[i]; 195 | 196 | lua_newtable(L); 197 | 198 | lua_pushstring(L, addr.name); 199 | lua_setfield(L, -2, "name"); 200 | 201 | lua_pushboolean(L, addr.is_internal); 202 | lua_setfield(L, -2, "is_internal"); 203 | 204 | if (addr.address.address4.sin_family == PF_INET) { 205 | uv_ip4_name(&addr.address.address4, buf, sizeof(buf)); 206 | } 207 | else if (addr.address.address4.sin_family == PF_INET6) { 208 | uv_ip6_name(&addr.address.address6, buf, sizeof(buf)); 209 | } 210 | 211 | lua_pushstring(L, buf); 212 | lua_setfield(L, -2, "address"); 213 | 214 | lua_rawseti(L, -2, i + 1); 215 | } 216 | 217 | uv_free_interface_addresses(info, size); 218 | 219 | return 1; 220 | } 221 | 222 | luaL_Reg luv_funcs[] = { 223 | {"cpu_info", luv_cpu_info}, 224 | {"mem_free", luv_mem_free}, 225 | {"mem_total", luv_mem_total}, 226 | {"hrtime", luv_hrtime}, 227 | {"self", luv_self}, 228 | {"sleep", luv_sleep}, 229 | {"interface_addresses", luv_interface_addresses}, 230 | {NULL, NULL} 231 | }; 232 | 233 | #ifdef USE_ZMQ 234 | static const luv_const_reg_t luv_zmq_consts[] = { 235 | /* ctx options */ 236 | {"IO_THREADS", ZMQ_IO_THREADS}, 237 | {"MAX_SOCKETS", ZMQ_MAX_SOCKETS}, 238 | 239 | /* socket types */ 240 | {"REQ", ZMQ_REQ}, 241 | {"REP", ZMQ_REP}, 242 | {"DEALER", ZMQ_DEALER}, 243 | {"ROUTER", ZMQ_ROUTER}, 244 | {"PUB", ZMQ_PUB}, 245 | {"SUB", ZMQ_SUB}, 246 | {"PUSH", ZMQ_PUSH}, 247 | {"PULL", ZMQ_PULL}, 248 | {"PAIR", ZMQ_PAIR}, 249 | 250 | /* socket options */ 251 | {"SNDHWM", ZMQ_SNDHWM}, 252 | {"RCVHWM", ZMQ_RCVHWM}, 253 | {"AFFINITY", ZMQ_AFFINITY}, 254 | {"IDENTITY", ZMQ_IDENTITY}, 255 | {"SUBSCRIBE", ZMQ_SUBSCRIBE}, 256 | {"UNSUBSCRIBE", ZMQ_UNSUBSCRIBE}, 257 | {"RATE", ZMQ_RATE}, 258 | {"RECOVERY_IVL", ZMQ_RECOVERY_IVL}, 259 | {"SNDBUF", ZMQ_SNDBUF}, 260 | {"RCVBUF", ZMQ_RCVBUF}, 261 | {"RCVMORE", ZMQ_RCVMORE}, 262 | {"FD", ZMQ_FD}, 263 | {"EVENTS", ZMQ_EVENTS}, 264 | {"TYPE", ZMQ_TYPE}, 265 | {"LINGER", ZMQ_LINGER}, 266 | {"RECONNECT_IVL", ZMQ_RECONNECT_IVL}, 267 | {"BACKLOG", ZMQ_BACKLOG}, 268 | {"RECONNECT_IVL_MAX", ZMQ_RECONNECT_IVL_MAX}, 269 | {"RCVTIMEO", ZMQ_RCVTIMEO}, 270 | {"SNDTIMEO", ZMQ_SNDTIMEO}, 271 | {"IPV4ONLY", ZMQ_IPV4ONLY}, 272 | {"ROUTER_BEHAVIOR", ZMQ_ROUTER_BEHAVIOR}, 273 | {"TCP_KEEPALIVE", ZMQ_TCP_KEEPALIVE}, 274 | {"TCP_KEEPALIVE_IDLE",ZMQ_TCP_KEEPALIVE_IDLE}, 275 | {"TCP_KEEPALIVE_CNT", ZMQ_TCP_KEEPALIVE_CNT}, 276 | {"TCP_KEEPALIVE_INTVL",ZMQ_TCP_KEEPALIVE_INTVL}, 277 | {"TCP_ACCEPT_FILTER", ZMQ_TCP_ACCEPT_FILTER}, 278 | 279 | /* msg options */ 280 | {"MORE", ZMQ_MORE}, 281 | 282 | /* send/recv flags */ 283 | {"DONTWAIT", ZMQ_DONTWAIT}, 284 | {"SNDMORE", ZMQ_SNDMORE}, 285 | 286 | /* poll events */ 287 | {"POLLIN", ZMQ_POLLIN}, 288 | {"POLLOUT", ZMQ_POLLOUT}, 289 | {"POLLERR", ZMQ_POLLERR}, 290 | 291 | /* devices */ 292 | {"STREAMER", ZMQ_STREAMER}, 293 | {"FORWARDER", ZMQ_FORWARDER}, 294 | {"QUEUE", ZMQ_QUEUE}, 295 | {NULL, 0} 296 | }; 297 | #endif 298 | 299 | #ifdef __cplusplus 300 | extern "C" { 301 | #endif 302 | LUALIB_API int luaopen_luv(lua_State *L) { 303 | 304 | #ifndef WIN32 305 | signal(SIGPIPE, SIG_IGN); 306 | #endif 307 | 308 | int i; 309 | uv_loop_t* loop; 310 | luv_state_t* curr; 311 | luv_object_t* stdfh; 312 | 313 | lua_settop(L, 0); 314 | 315 | /* register decoders */ 316 | lua_pushcfunction(L, luvL_lib_decoder); 317 | lua_setfield(L, LUA_REGISTRYINDEX, "luv:lib:decoder"); 318 | 319 | #ifdef USE_ZMQ 320 | lua_pushcfunction(L, luvL_zmq_ctx_decoder); 321 | lua_setfield(L, LUA_REGISTRYINDEX, "luv:zmq:decoder"); 322 | #endif 323 | 324 | /* luv */ 325 | luvL_new_module(L, "luv", luv_funcs); 326 | 327 | /* luv.thread */ 328 | luvL_new_module(L, "luv_thread", luv_thread_funcs); 329 | lua_setfield(L, -2, "thread"); 330 | luvL_new_class(L, LUV_THREAD_T, luv_thread_meths); 331 | lua_pop(L, 1); 332 | 333 | if (!MAIN_INITIALIZED) { 334 | luvL_thread_init_main(L); 335 | lua_pop(L, 1); 336 | } 337 | 338 | /* luv.fiber */ 339 | luvL_new_module(L, "luv_fiber", luv_fiber_funcs); 340 | 341 | /* borrow coroutine.yield (fast on LJ2) */ 342 | lua_getglobal(L, "coroutine"); 343 | lua_getfield(L, -1, "yield"); 344 | lua_setfield(L, -3, "yield"); 345 | lua_pop(L, 1); /* coroutine */ 346 | 347 | lua_setfield(L, -2, "fiber"); 348 | 349 | luvL_new_class(L, LUV_FIBER_T, luv_fiber_meths); 350 | lua_pop(L, 1); 351 | 352 | /* luv.codec */ 353 | luvL_new_module(L, "luv_codec", luv_codec_funcs); 354 | lua_setfield(L, -2, "codec"); 355 | 356 | /* luv.timer */ 357 | luvL_new_module(L, "luv_timer", luv_timer_funcs); 358 | lua_setfield(L, -2, "timer"); 359 | luvL_new_class(L, LUV_TIMER_T, luv_timer_meths); 360 | lua_pop(L, 1); 361 | 362 | /* luv.idle */ 363 | luvL_new_module(L, "luv_idle", luv_idle_funcs); 364 | lua_setfield(L, -2, "idle"); 365 | luvL_new_class(L, LUV_IDLE_T, luv_idle_meths); 366 | lua_pop(L, 1); 367 | 368 | /* luv.fs */ 369 | luvL_new_module(L, "luv_fs", luv_fs_funcs); 370 | lua_setfield(L, -2, "fs"); 371 | luvL_new_class(L, LUV_FILE_T, luv_file_meths); 372 | lua_pop(L, 1); 373 | 374 | /* luv.pipe */ 375 | luvL_new_module(L, "luv_pipe", luv_pipe_funcs); 376 | lua_setfield(L, -2, "pipe"); 377 | luvL_new_class(L, LUV_PIPE_T, luv_stream_meths); 378 | luaL_register(L, NULL, luv_pipe_meths); 379 | lua_pop(L, 1); 380 | 381 | /* luv.std{in,out,err} */ 382 | if (!MAIN_INITIALIZED) { 383 | MAIN_INITIALIZED = 1; 384 | loop = luvL_event_loop(L); 385 | curr = luvL_state_self(L); 386 | 387 | const char* stdfhs[] = { "stdin", "stdout", "stderr" }; 388 | for (i = 0; i < 3; i++) { 389 | #ifdef WIN32 390 | const uv_file fh = GetStdHandle(i == 0 ? STD_INPUT_HANDLE 391 | : (i == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE)); 392 | #else 393 | const uv_file fh = i; 394 | #endif 395 | stdfh = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 396 | luaL_getmetatable(L, LUV_PIPE_T); 397 | lua_setmetatable(L, -2); 398 | luvL_object_init(curr, stdfh); 399 | uv_pipe_init(loop, &stdfh->h.pipe, 0); 400 | uv_pipe_open(&stdfh->h.pipe, fh); 401 | lua_pushvalue(L, -1); 402 | lua_setfield(L, LUA_REGISTRYINDEX, stdfhs[i]); 403 | lua_setfield(L, -2, stdfhs[i]); 404 | } 405 | } 406 | 407 | /* luv.net */ 408 | luvL_new_module(L, "luv_net", luv_net_funcs); 409 | lua_setfield(L, -2, "net"); 410 | luvL_new_class(L, LUV_NET_TCP_T, luv_stream_meths); 411 | luaL_register(L, NULL, luv_net_tcp_meths); 412 | lua_pop(L, 1); 413 | 414 | /* luv.process */ 415 | luvL_new_module(L, "luv_process", luv_process_funcs); 416 | lua_setfield(L, -2, "process"); 417 | luvL_new_class(L, LUV_PROCESS_T, luv_process_meths); 418 | lua_pop(L, 1); 419 | 420 | /* luv.zmq */ 421 | #ifdef USE_ZMQ 422 | luvL_new_module(L, "luv_zmq", luv_zmq_funcs); 423 | const luv_const_reg_t* c = luv_zmq_consts; 424 | for (; c->key; c++) { 425 | lua_pushinteger(L, c->val); 426 | lua_setfield(L, -2, c->key); 427 | } 428 | lua_setfield(L, -2, "zmq"); 429 | luvL_new_class(L, LUV_ZMQ_CTX_T, luv_zmq_ctx_meths); 430 | luvL_new_class(L, LUV_ZMQ_SOCKET_T, luv_zmq_socket_meths); 431 | lua_pop(L, 2); 432 | #endif 433 | 434 | lua_settop(L, 1); 435 | return 1; 436 | } 437 | 438 | #ifdef __cplusplus 439 | } 440 | #endif 441 | -------------------------------------------------------------------------------- /src/luv.h: -------------------------------------------------------------------------------- 1 | #ifndef LUV_H 2 | #define LUV_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | #include "lua.h" 14 | #include "lualib.h" 15 | #include "lauxlib.h" 16 | 17 | #ifdef __cplusplus 18 | } 19 | #endif 20 | 21 | #include "uv/include/uv.h" 22 | #include "ngx-queue.h" 23 | 24 | #ifdef USE_ZMQ 25 | #include "zmq/include/zmq.h" 26 | #include "zmq/include/zmq_utils.h" 27 | #endif 28 | 29 | #undef LUV_DEBUG 30 | 31 | #ifdef LUV_DEBUG 32 | # define TRACE(fmt, ...) do { \ 33 | fprintf(stderr, "%s: %d: %s: " fmt, \ 34 | __FILE__, __LINE__, __func__, ##__VA_ARGS__); \ 35 | } while (0) 36 | #else 37 | # define TRACE(fmt, ...) ((void)0) 38 | #endif /* LUV_DEBUG */ 39 | 40 | typedef union luv_handle_u { 41 | uv_handle_t handle; 42 | uv_stream_t stream; 43 | uv_tcp_t tcp; 44 | uv_pipe_t pipe; 45 | uv_prepare_t prepare; 46 | uv_check_t check; 47 | uv_idle_t idle; 48 | uv_async_t async; 49 | uv_timer_t timer; 50 | uv_fs_event_t fs_event; 51 | uv_fs_poll_t fs_poll; 52 | uv_poll_t poll; 53 | uv_process_t process; 54 | uv_tty_t tty; 55 | uv_udp_t udp; 56 | uv_file file; 57 | } luv_handle_t; 58 | 59 | typedef union luv_req_u { 60 | uv_req_t req; 61 | uv_write_t write; 62 | uv_connect_t connect; 63 | uv_shutdown_t shutdown; 64 | uv_fs_t fs; 65 | uv_work_t work; 66 | uv_udp_send_t udp_send; 67 | uv_getaddrinfo_t getaddrinfo; 68 | } luv_req_t; 69 | 70 | /* registry table for luv object refs */ 71 | #define LUV_REG_KEY "__LUV__" 72 | 73 | /* default buffer size for read operations */ 74 | #define LUV_BUF_SIZE 4096 75 | 76 | /* max path length */ 77 | #define LUV_MAX_PATH 1024 78 | 79 | /* metatables for various types */ 80 | #define LUV_NS_T "luv.ns" 81 | #define LUV_COND_T "luv.cond" 82 | #define LUV_FIBER_T "luv.fiber" 83 | #define LUV_THREAD_T "luv.thread" 84 | #define LUV_ASYNC_T "luv.async" 85 | #define LUV_TIMER_T "luv.timer" 86 | #define LUV_IDLE_T "luv.idle" 87 | #define LUV_FS_T "luv.fs" 88 | #define LUV_FS_POLL_T "luv.fs.poll" 89 | #define LUV_FILE_T "luv.file" 90 | #define LUV_PIPE_T "luv.pipe" 91 | #define LUV_TTY_T "luv.tty" 92 | #define LUV_PROCESS_T "luv.process" 93 | #define LUV_NET_TCP_T "luv.net.tcp" 94 | #define LUV_NET_UDP_T "luv.net.udp" 95 | #define LUV_ZMQ_CTX_T "luv.zmq.ctx" 96 | #define LUV_ZMQ_SOCKET_T "luv.zmq.socket" 97 | 98 | /* state flags */ 99 | #define LUV_FSTART (1 << 0) 100 | #define LUV_FREADY (1 << 1) 101 | #define LUV_FMAIN (1 << 2) 102 | #define LUV_FWAIT (1 << 3) 103 | #define LUV_FJOIN (1 << 4) 104 | #define LUV_FDEAD (1 << 5) 105 | 106 | /* ØMQ flags */ 107 | #define LUV_ZMQ_SCLOSED (1 << 0) 108 | #define LUV_ZMQ_XDUPCTX (1 << 1) 109 | #define LUV_ZMQ_WSEND (1 << 2) 110 | #define LUV_ZMQ_WRECV (1 << 3) 111 | 112 | /* luv states */ 113 | typedef struct luv_state_s luv_state_t; 114 | typedef struct luv_fiber_s luv_fiber_t; 115 | typedef struct luv_thread_s luv_thread_t; 116 | 117 | typedef enum { 118 | LUV_TFIBER, 119 | LUV_TTHREAD 120 | } luv_state_type; 121 | 122 | #define LUV_STATE_FIELDS \ 123 | ngx_queue_t rouse; \ 124 | ngx_queue_t queue; \ 125 | ngx_queue_t join; \ 126 | ngx_queue_t cond; \ 127 | uv_loop_t* loop; \ 128 | int type; \ 129 | int flags; \ 130 | luv_state_t* outer; \ 131 | lua_State* L; \ 132 | luv_req_t req; \ 133 | void* data 134 | 135 | struct luv_state_s { 136 | LUV_STATE_FIELDS; 137 | }; 138 | 139 | struct luv_thread_s { 140 | LUV_STATE_FIELDS; 141 | luv_state_t* curr; 142 | uv_thread_t tid; 143 | uv_async_t async; 144 | uv_check_t check; 145 | }; 146 | 147 | struct luv_fiber_s { 148 | LUV_STATE_FIELDS; 149 | }; 150 | 151 | union luv_any_state { 152 | luv_state_t state; 153 | luv_fiber_t fiber; 154 | luv_thread_t thread; 155 | }; 156 | 157 | /* luv objects */ 158 | #define LUV_OSTARTED (1 << 0) 159 | #define LUV_OSTOPPED (1 << 1) 160 | #define LUV_OWAITING (1 << 2) 161 | #define LUV_OCLOSING (1 << 3) 162 | #define LUV_OCLOSED (1 << 4) 163 | #define LUV_OSHUTDOWN (1 << 5) 164 | 165 | #define luvL_object_is_started(O) ((O)->flags & LUV_OSTARTED) 166 | #define luvL_object_is_stopped(O) ((O)->flags & LUV_OSTOPPED) 167 | #define luvL_object_is_waiting(O) ((O)->flags & LUV_OWAITING) 168 | #define luvL_object_is_closing(O) ((O)->flags & LUV_OCLOSING) 169 | #define luvL_object_is_shutdown(O) ((O)->flags & LUV_OSHUTDOWN) 170 | #define luvL_object_is_closed(O) ((O)->flags & LUV_OCLOSED) 171 | 172 | #define LUV_OBJECT_FIELDS \ 173 | ngx_queue_t rouse; \ 174 | ngx_queue_t queue; \ 175 | luv_state_t* state; \ 176 | int flags; \ 177 | int type; \ 178 | int count; \ 179 | int ref; \ 180 | void* data 181 | 182 | typedef struct luv_object_s { 183 | LUV_OBJECT_FIELDS; 184 | luv_handle_t h; 185 | uv_buf_t buf; 186 | } luv_object_t; 187 | 188 | typedef struct luv_chan_s { 189 | LUV_OBJECT_FIELDS; 190 | void* put; 191 | void* get; 192 | } luv_chan_t; 193 | 194 | union luv_any_object { 195 | luv_object_t object; 196 | luv_chan_t chan; 197 | }; 198 | 199 | int luvL_traceback(lua_State *L); 200 | 201 | uv_loop_t* luvL_event_loop(lua_State* L); 202 | 203 | int luvL_state_in_thread(luv_state_t* state); 204 | int luvL_state_is_thread(luv_state_t* state); 205 | int luvL_state_is_active(luv_state_t* state); 206 | 207 | void luvL_state_ready (luv_state_t* state); 208 | int luvL_state_yield (luv_state_t* state, int narg); 209 | int luvL_state_suspend(luv_state_t* state); 210 | int luvL_state_resume (luv_state_t* state, int narg); 211 | 212 | void luvL_fiber_ready (luv_fiber_t* fiber); 213 | int luvL_fiber_yield (luv_fiber_t* fiber, int narg); 214 | int luvL_fiber_suspend(luv_fiber_t* fiber); 215 | int luvL_fiber_resume (luv_fiber_t* fiber, int narg); 216 | 217 | int luvL_thread_loop (luv_thread_t* thread); 218 | int luvL_thread_once (luv_thread_t* thread); 219 | void luvL_thread_ready (luv_thread_t* thread); 220 | int luvL_thread_yield (luv_thread_t* thread, int narg); 221 | int luvL_thread_suspend(luv_thread_t* thread); 222 | int luvL_thread_resume (luv_thread_t* thread, int narg); 223 | void luvL_thread_enqueue(luv_thread_t* thread, luv_fiber_t* fiber); 224 | 225 | luv_state_t* luvL_state_self (lua_State* L); 226 | luv_thread_t* luvL_thread_self(lua_State* L); 227 | 228 | void luvL_thread_init_main(lua_State* L); 229 | 230 | luv_fiber_t* luvL_fiber_create (luv_state_t* outer, int narg); 231 | luv_thread_t* luvL_thread_create(luv_state_t* outer, int narg); 232 | 233 | void luvL_fiber_close (luv_fiber_t* self); 234 | 235 | int luvL_thread_loop (luv_thread_t* self); 236 | int luvL_thread_once (luv_thread_t* self); 237 | 238 | void luvL_object_init (luv_state_t* state, luv_object_t* self); 239 | void luvL_object_close(luv_object_t* self); 240 | 241 | int luvL_stream_stop (luv_object_t* self); 242 | void luvL_stream_free (luv_object_t* self); 243 | void luvL_stream_close(luv_object_t* self); 244 | 245 | typedef ngx_queue_t luv_cond_t; 246 | 247 | int luvL_cond_init (luv_cond_t* cond); 248 | int luvL_cond_wait (luv_cond_t* cond, luv_state_t* curr); 249 | int luvL_cond_signal (luv_cond_t* cond); 250 | int luvL_cond_broadcast (luv_cond_t* cond); 251 | 252 | int luvL_codec_encode(lua_State* L, int narg); 253 | int luvL_codec_decode(lua_State* L); 254 | 255 | int luvL_lib_decoder(lua_State* L); 256 | int luvL_zmq_ctx_decoder(lua_State* L); 257 | 258 | uv_buf_t luvL_alloc_cb (uv_handle_t* handle, size_t size); 259 | void luvL_connect_cb (uv_connect_t* conn, int status); 260 | 261 | int luvL_new_class (lua_State* L, const char* name, luaL_Reg* meths); 262 | int luvL_new_module(lua_State* L, const char* name, luaL_Reg* funcs); 263 | 264 | typedef struct luv_const_reg_s { 265 | const char* key; 266 | int val; 267 | } luv_const_reg_t; 268 | 269 | extern luaL_Reg luv_thread_funcs[32]; 270 | extern luaL_Reg luv_thread_meths[32]; 271 | 272 | extern luaL_Reg luv_fiber_funcs[32]; 273 | extern luaL_Reg luv_fiber_meths[32]; 274 | 275 | extern luaL_Reg luv_cond_funcs[32]; 276 | extern luaL_Reg luv_cond_meths[32]; 277 | 278 | extern luaL_Reg luv_codec_funcs[32]; 279 | 280 | extern luaL_Reg luv_timer_funcs[32]; 281 | extern luaL_Reg luv_timer_meths[32]; 282 | 283 | extern luaL_Reg luv_idle_funcs[32]; 284 | extern luaL_Reg luv_idle_meths[32]; 285 | 286 | extern luaL_Reg luv_fs_funcs[32]; 287 | extern luaL_Reg luv_file_meths[32]; 288 | 289 | extern luaL_Reg luv_stream_meths[32]; 290 | 291 | extern luaL_Reg luv_net_funcs[32]; 292 | extern luaL_Reg luv_net_tcp_meths[32]; 293 | extern luaL_Reg luv_net_udp_meths[32]; 294 | 295 | extern luaL_Reg luv_pipe_funcs[32]; 296 | extern luaL_Reg luv_pipe_meths[32]; 297 | 298 | extern luaL_Reg luv_process_funcs[32]; 299 | extern luaL_Reg luv_process_meths[32]; 300 | 301 | #ifdef USE_ZMQ 302 | extern luaL_Reg luv_zmq_funcs[32]; 303 | extern luaL_Reg luv_zmq_ctx_meths[32]; 304 | extern luaL_Reg luv_zmq_socket_meths[32]; 305 | #endif 306 | 307 | #ifdef WIN32 308 | # ifdef LUV_EXPORT 309 | # define LUALIB_API __declspec(dllexport) 310 | # else 311 | # define LUALIB_API __declspec(dllimport) 312 | # endif 313 | #else 314 | # define LUALIB_API LUA_API 315 | #endif 316 | 317 | #ifdef __cplusplus 318 | extern "C" { 319 | #endif 320 | 321 | LUALIB_API int luaopen_luv(lua_State *L); 322 | 323 | #ifdef __cplusplus 324 | } 325 | #endif 326 | 327 | #define container_of(ptr, type, member) \ 328 | ((type*) ((char*)(ptr) - offsetof(type, member))) 329 | 330 | /* lifted from luasys */ 331 | #define luv_boxpointer(L,u) \ 332 | (*(void**) (lua_newuserdata(L, sizeof(void*))) = (u)) 333 | #define luv_unboxpointer(L,i) \ 334 | (*(void**) (lua_touserdata(L, i))) 335 | 336 | /* lifted from luasys */ 337 | #define luv_boxinteger(L,n) \ 338 | (*(lua_Integer*) (lua_newuserdata(L, sizeof(lua_Integer))) = (lua_Integer) (n)) 339 | #define luv_unboxinteger(L,i) \ 340 | (*(lua_Integer*) (lua_touserdata(L, i))) 341 | 342 | #endif /* LUV_H */ 343 | -------------------------------------------------------------------------------- /src/luv_codec.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | #include 4 | #include 5 | 6 | #define LUV_CODEC_TREF 1 7 | #define LUV_CODEC_TVAL 2 8 | #define LUV_CODEC_TUSR 3 9 | 10 | /* TODO: make this buffer stuff generic */ 11 | typedef struct luv_buf_t { 12 | size_t size; 13 | uint8_t* head; 14 | uint8_t* base; 15 | } luv_buf_t; 16 | 17 | static int encode_table(lua_State* L, luv_buf_t *buf, int seen); 18 | static int decode_table(lua_State* L, luv_buf_t* buf, int seen); 19 | 20 | luv_buf_t* luvL_buf_new(size_t size) { 21 | if (!size) size = 128; 22 | luv_buf_t* buf = (luv_buf_t*)malloc(sizeof(luv_buf_t)); 23 | buf->base = (uint8_t*)malloc(size); 24 | buf->size = size; 25 | buf->head = buf->base; 26 | return buf; 27 | } 28 | 29 | void luvL_buf_close(luv_buf_t* buf) { 30 | free(buf->base); 31 | buf->head = NULL; 32 | buf->base = NULL; 33 | buf->size = 0; 34 | } 35 | 36 | void luvL_buf_need(luv_buf_t* buf, size_t len) { 37 | size_t size = buf->size; 38 | if (!size) { 39 | size = 128; 40 | buf->base = (uint8_t*)malloc(size); 41 | buf->size = size; 42 | buf->head = buf->base; 43 | } 44 | ptrdiff_t head = buf->head - buf->base; 45 | ptrdiff_t need = head + len; 46 | while (size < need) size *= 2; 47 | if (size > buf->size) { 48 | buf->base = (uint8_t*)realloc(buf->base, size); 49 | buf->size = size; 50 | buf->head = buf->base + head; 51 | } 52 | } 53 | void luvL_buf_init(luv_buf_t* buf, uint8_t* data, size_t len) { 54 | luvL_buf_need(buf, len); 55 | memcpy(buf->base, data, len); 56 | buf->head += len; 57 | } 58 | 59 | void luvL_buf_put(luv_buf_t* buf, uint8_t val) { 60 | luvL_buf_need(buf, 1); 61 | *(buf->head++) = val; 62 | } 63 | void luvL_buf_write(luv_buf_t* buf, uint8_t* data, size_t len) { 64 | luvL_buf_need(buf, len); 65 | memcpy(buf->head, data, len); 66 | buf->head += len; 67 | } 68 | void luvL_buf_write_uleb128(luv_buf_t* buf, uint32_t val) { 69 | luvL_buf_need(buf, 5); 70 | size_t n = 0; 71 | uint8_t* p = buf->head; 72 | for (; val >= 0x80; val >>= 7) { 73 | p[n++] = (uint8_t)((val & 0x7f) | 0x80); 74 | } 75 | p[n++] = (uint8_t)val; 76 | buf->head += n; 77 | } 78 | 79 | /* for lua_dump */ 80 | int luvL_writer(lua_State* L, const char* str, size_t len, void* buf) { 81 | (void)L; 82 | luvL_buf_write((luv_buf_t*)buf, (uint8_t*)str, len); 83 | return 0; 84 | } 85 | 86 | uint8_t luvL_buf_get(luv_buf_t* buf) { 87 | return *(buf->head++); 88 | } 89 | uint8_t* luvL_buf_read(luv_buf_t* buf, size_t len) { 90 | uint8_t* p = buf->head; 91 | buf->head += len; 92 | return p; 93 | } 94 | uint32_t luvL_buf_read_uleb128(luv_buf_t* buf) { 95 | const uint8_t* p = (const uint8_t*)buf->head; 96 | uint32_t v = *p++; 97 | if (v >= 0x80) { 98 | int sh = 0; 99 | v &= 0x7f; 100 | do { 101 | v |= ((*p & 0x7f) << (sh += 7)); 102 | } while (*p++ >= 0x80); 103 | } 104 | buf->head = (uint8_t*)p; 105 | return v; 106 | } 107 | uint8_t luvL_buf_peek(luv_buf_t* buf) { 108 | return *buf->head; 109 | } 110 | 111 | #define encoder_seen(L, idx, seen) do {\ 112 | int ref = lua_objlen(L, seen) + 1; \ 113 | lua_pushboolean(L, 1); \ 114 | lua_rawseti(L, seen, ref); \ 115 | lua_pushvalue(L, idx); \ 116 | lua_pushinteger(L, ref); \ 117 | lua_rawset(L, seen); \ 118 | } while (0) 119 | 120 | #define encoder_hook(L, buf, seen) do { \ 121 | luvL_buf_put(buf, LUV_CODEC_TUSR); \ 122 | lua_pushvalue(L, -2); \ 123 | lua_call(L, 1, 2); \ 124 | int cbt = lua_type(L, -2); \ 125 | if (!(cbt == LUA_TFUNCTION || cbt == LUA_TSTRING)) { \ 126 | luaL_error(L, "__codec must return either a function or a string"); \ 127 | } \ 128 | encode_value(L, buf, -2, seen); \ 129 | encode_value(L, buf, -1, seen); \ 130 | lua_pop(L, 2); \ 131 | } while (0) 132 | 133 | static void encode_value(lua_State* L, luv_buf_t* buf, int val, int seen) { 134 | size_t len; 135 | int val_type = lua_type(L, val); 136 | 137 | lua_pushvalue(L, val); 138 | 139 | luvL_buf_put(buf, (uint8_t)val_type); 140 | 141 | switch (val_type) { 142 | case LUA_TBOOLEAN: { 143 | int v = lua_toboolean(L, -1); 144 | luvL_buf_put(buf, (uint8_t)v); 145 | break; 146 | } 147 | case LUA_TSTRING: { 148 | const char *str_val = lua_tolstring(L, -1, &len); 149 | luvL_buf_write_uleb128(buf, (uint32_t)len); 150 | luvL_buf_write(buf, (uint8_t*)str_val, len); 151 | break; 152 | } 153 | case LUA_TNUMBER: { 154 | lua_Number v = lua_tonumber(L, -1); 155 | luvL_buf_write(buf, (uint8_t*)(void*)&v, sizeof v); 156 | break; 157 | } 158 | case LUA_TTABLE: { 159 | int tag, ref; 160 | lua_pushvalue(L, -1); 161 | lua_rawget(L, seen); 162 | if (!lua_isnil(L, -1)) { 163 | /* already seen */ 164 | ref = lua_tointeger(L, -1); 165 | tag = LUV_CODEC_TREF; 166 | luvL_buf_put(buf, tag); 167 | luvL_buf_write_uleb128(buf, (uint32_t)ref); 168 | lua_pop(L, 1); /* pop ref */ 169 | } 170 | else { 171 | lua_pop(L, 1); /* pop nil */ 172 | encoder_seen(L, -1, seen); 173 | if (luaL_getmetafield(L, -1, "__codec")) { 174 | encoder_hook(L, buf, seen); 175 | } 176 | else { 177 | tag = LUV_CODEC_TVAL; 178 | luvL_buf_put(buf, tag); 179 | encode_table(L, buf, seen); 180 | } 181 | } 182 | break; 183 | } 184 | case LUA_TFUNCTION: { 185 | int tag, ref; 186 | lua_pushvalue(L, -1); 187 | lua_rawget(L, seen); 188 | if (!lua_isnil(L, -1)) { 189 | ref = lua_tointeger(L, -1); 190 | tag = LUV_CODEC_TREF; 191 | luvL_buf_put(buf, tag); 192 | luvL_buf_write_uleb128(buf, (uint32_t)ref); 193 | lua_pop(L, 1); /* pop ref */ 194 | } 195 | else { 196 | int i; 197 | luv_buf_t b; b.base = NULL; b.head = NULL; b.size = 0; 198 | lua_Debug ar; 199 | 200 | lua_pop(L, 1); /* pop nil */ 201 | 202 | lua_pushvalue(L, -1); 203 | lua_getinfo(L, ">nuS", &ar); 204 | if (ar.what[0] != 'L') { 205 | luaL_error(L, "attempt to persist a C function '%s'", ar.name); 206 | } 207 | 208 | encoder_seen(L, -1, seen); 209 | 210 | tag = LUV_CODEC_TVAL; 211 | luvL_buf_put(buf, tag); 212 | 213 | lua_dump(L, (lua_Writer)luvL_writer, &b); 214 | 215 | len = (size_t)(b.head - b.base); 216 | luvL_buf_write_uleb128(buf, (uint32_t)len); 217 | luvL_buf_write(buf, b.base, len); 218 | luvL_buf_close(&b); 219 | 220 | lua_newtable(L); 221 | for (i = 1; i <= ar.nups; i++) { 222 | lua_getupvalue(L, -2, i); 223 | lua_rawseti(L, -2, i); 224 | } 225 | assert(lua_objlen(L, -1) == ar.nups); 226 | encode_table(L, buf, seen); 227 | lua_pop(L, 1); 228 | } 229 | 230 | break; 231 | } 232 | case LUA_TUSERDATA: 233 | if (luaL_getmetafield(L, -1, "__codec")) { 234 | encoder_hook(L, buf, seen); 235 | break; 236 | } 237 | else { 238 | luaL_error(L, "cannot encode userdata\n"); 239 | } 240 | case LUA_TNIL: 241 | /* type tag already written */ 242 | break; 243 | case LUA_TLIGHTUSERDATA: { 244 | void* ptr = lua_touserdata(L, -1); 245 | luvL_buf_write(buf, (uint8_t*)(void*)&ptr, sizeof(void*)); 246 | break; 247 | } 248 | case LUA_TTHREAD: 249 | default: 250 | luaL_error(L, "cannot encode a `%s'", lua_typename(L, val_type)); 251 | } 252 | lua_pop(L, 1); 253 | } 254 | 255 | static int encode_table(lua_State* L, luv_buf_t* buf, int seen) { 256 | lua_pushnil(L); 257 | while (lua_next(L, -2) != 0) { 258 | int top = lua_gettop(L); 259 | encode_value(L, buf, -2, seen); 260 | encode_value(L, buf, -1, seen); 261 | assert(lua_gettop(L) == top); 262 | lua_pop(L, 1); 263 | } 264 | 265 | /* sentinel */ 266 | lua_pushnil(L); 267 | encode_value(L, buf, -1, seen); 268 | lua_pop(L, 1); 269 | 270 | return 1; 271 | } 272 | 273 | static void find_decoder(lua_State* L, luv_buf_t* buf, int seen) { 274 | int i; 275 | int lookup[2] = { 276 | LUA_REGISTRYINDEX, 277 | LUA_GLOBALSINDEX 278 | }; 279 | for (i = 0; i < 2; i++) { 280 | lua_pushvalue(L, -1); 281 | lua_gettable(L, lookup[i]); 282 | if (lua_isnil(L, -1)) { 283 | lua_pop(L, 1); 284 | } 285 | else { 286 | lua_replace(L, -2); 287 | break; 288 | } 289 | } 290 | if (!lua_isfunction(L, -1)) { 291 | const char* key = lua_tostring(L, -1); 292 | luaL_error(L, "failed to find a valid decoder for `%s'", key); 293 | } 294 | } 295 | 296 | #define decoder_seen(L, idx, seen) do { \ 297 | int ref = lua_objlen(L, seen) + 1; \ 298 | lua_pushvalue(L, idx); \ 299 | lua_rawseti(L, seen, ref); \ 300 | } while (0) 301 | 302 | static void decode_value(lua_State* L, luv_buf_t* buf, int seen) { 303 | uint8_t val_type = luvL_buf_get(buf); 304 | size_t len; 305 | switch (val_type) { 306 | case LUA_TBOOLEAN: { 307 | int val = luvL_buf_get(buf); 308 | lua_pushboolean(L, val); 309 | break; 310 | } 311 | case LUA_TNUMBER: { 312 | uint8_t* ptr = luvL_buf_read(buf, sizeof(lua_Number)); 313 | lua_pushnumber(L, *(lua_Number*)(void*)ptr); 314 | break; 315 | } 316 | case LUA_TSTRING: { 317 | len = (size_t)luvL_buf_read_uleb128(buf); 318 | uint8_t* ptr = luvL_buf_read(buf, len); 319 | lua_pushlstring(L, (const char *)ptr, len); 320 | break; 321 | } 322 | case LUA_TTABLE: { 323 | uint8_t tag = luvL_buf_get(buf); 324 | uint32_t ref; 325 | if (tag == LUV_CODEC_TREF) { 326 | ref = luvL_buf_read_uleb128(buf); 327 | lua_rawgeti(L, seen, ref); 328 | } 329 | else { 330 | if (tag == LUV_CODEC_TUSR) { 331 | decode_value(L, buf, seen); /* hook */ 332 | if (lua_type(L, -1) == LUA_TSTRING) { 333 | find_decoder(L, buf, seen); 334 | } 335 | decode_value(L, buf, seen); /* any value */ 336 | lua_call(L, 1, 1); /* result */ 337 | decoder_seen(L, -1, seen); 338 | } 339 | else { 340 | lua_newtable(L); 341 | decoder_seen(L, -1, seen); 342 | decode_table(L, buf, seen); 343 | } 344 | } 345 | break; 346 | } 347 | case LUA_TFUNCTION: { 348 | size_t nups; 349 | uint8_t tag = luvL_buf_get(buf); 350 | if (tag == LUV_CODEC_TREF) { 351 | uint32_t ref = luvL_buf_read_uleb128(buf); 352 | lua_rawgeti(L, seen, ref); 353 | } 354 | else { 355 | size_t i; 356 | len = luvL_buf_read_uleb128(buf); 357 | const char* code = (char *)luvL_buf_read(buf, len); 358 | if (luaL_loadbuffer(L, code, len, "=chunk")) { 359 | luaL_error(L, "failed to load chunk\n"); 360 | } 361 | 362 | decoder_seen(L, -1, seen); 363 | lua_newtable(L); 364 | decode_table(L, buf, seen); 365 | nups = lua_objlen(L, -1); 366 | for (i=1; i <= nups; i++) { 367 | lua_rawgeti(L, -1, i); 368 | lua_setupvalue(L, -3, i); 369 | } 370 | lua_pop(L, 1); 371 | } 372 | break; 373 | } 374 | case LUA_TUSERDATA: { 375 | uint8_t tag = luvL_buf_get(buf); 376 | assert(tag == LUV_CODEC_TUSR); 377 | decode_value(L, buf, seen); /* hook */ 378 | if (lua_type(L, -1) == LUA_TSTRING) { 379 | find_decoder(L, buf, seen); 380 | } 381 | decode_value(L, buf, seen); /* any value */ 382 | luaL_checktype(L, -2, LUA_TFUNCTION); 383 | lua_call(L, 1, 1); /* result */ 384 | break; 385 | } 386 | case LUA_TLIGHTUSERDATA: { 387 | uint8_t* ptr = luvL_buf_read(buf, sizeof(void*)); 388 | lua_pushlightuserdata(L, *(void**)ptr); 389 | break; 390 | } 391 | case LUA_TNIL: 392 | lua_pushnil(L); 393 | break; 394 | case LUA_TTHREAD: 395 | default: 396 | luaL_error(L, "bad code"); 397 | } 398 | } 399 | 400 | static int decode_table(lua_State* L, luv_buf_t* buf, int seen) { 401 | for (;luvL_buf_peek(buf) != LUA_TNIL;) { 402 | decode_value(L, buf, seen); 403 | decode_value(L, buf, seen); 404 | lua_settable(L, -3); 405 | } 406 | 407 | /* sentinel */ 408 | decode_value(L, buf, seen); 409 | assert(lua_type(L, -1) == LUA_TNIL); 410 | lua_pop(L, 1); 411 | return 1; 412 | } 413 | 414 | int luvL_codec_encode(lua_State* L, int narg) { 415 | int i, base, seen; 416 | luv_buf_t buf; buf.base = NULL; buf.head = NULL; buf.size = 0; 417 | 418 | base = lua_gettop(L) - narg + 1; 419 | 420 | lua_newtable(L); 421 | lua_insert(L, base); /* seen */ 422 | seen = base++; 423 | 424 | luvL_buf_write_uleb128(&buf, narg); 425 | 426 | for (i = base; i < base + narg; i++) { 427 | encode_value(L, &buf, i, seen); 428 | } 429 | 430 | lua_remove(L, seen); 431 | lua_settop(L, seen); 432 | 433 | lua_pushlstring(L, (char *)buf.base, buf.head - buf.base); 434 | luvL_buf_close(&buf); 435 | 436 | return 1; 437 | } 438 | 439 | int luvL_codec_decode(lua_State* L) { 440 | size_t len; 441 | int nval, seen, i; 442 | int top = lua_gettop(L); 443 | 444 | luv_buf_t buf; buf.base = NULL; buf.head = NULL; buf.size = 0; 445 | 446 | const char* data = luaL_checklstring(L, 1, &len); 447 | luvL_buf_init(&buf, (uint8_t*)data, len); 448 | 449 | buf.head = buf.base; 450 | 451 | lua_newtable(L); 452 | seen = lua_gettop(L); 453 | nval = luvL_buf_read_uleb128(&buf); 454 | 455 | lua_checkstack(L, nval); 456 | 457 | for (i = 0; i < nval; i++) { 458 | decode_value(L, &buf, seen); 459 | } 460 | lua_remove(L, seen); 461 | 462 | assert(lua_gettop(L) == top + nval); 463 | return nval; 464 | } 465 | 466 | static int luv_codec_encode(lua_State* L) { 467 | return luvL_codec_encode(L, lua_gettop(L)); 468 | } 469 | static int luv_codec_decode(lua_State* L) { 470 | return luvL_codec_decode(L); 471 | } 472 | 473 | luaL_Reg luv_codec_funcs[] = { 474 | {"encode", luv_codec_encode}, 475 | {"decode", luv_codec_decode}, 476 | }; 477 | -------------------------------------------------------------------------------- /src/luv_cond.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | int luvL_cond_init(luv_cond_t* cond) { 4 | ngx_queue_init(cond); 5 | return 1; 6 | } 7 | int luvL_cond_wait(luv_cond_t* cond, luv_state_t* curr) { 8 | ngx_queue_insert_tail(cond, &curr->cond); 9 | TRACE("SUSPEND state %p\n", curr); 10 | return luvL_state_suspend(curr); 11 | } 12 | int luvL_cond_signal(luv_cond_t* cond) { 13 | ngx_queue_t* q; 14 | luv_state_t* s; 15 | if (!ngx_queue_empty(cond)) { 16 | q = ngx_queue_head(cond); 17 | s = ngx_queue_data(q, luv_state_t, cond); 18 | ngx_queue_remove(q); 19 | TRACE("READY state %p\n", s); 20 | luvL_state_ready(s); 21 | return 1; 22 | } 23 | return 0; 24 | } 25 | int luvL_cond_broadcast(luv_cond_t* cond) { 26 | ngx_queue_t* q; 27 | luv_state_t* s; 28 | int roused = 0; 29 | while (!ngx_queue_empty(cond)) { 30 | q = ngx_queue_head(cond); 31 | s = ngx_queue_data(q, luv_state_t, cond); 32 | ngx_queue_remove(q); 33 | TRACE("READY state %p\n", s); 34 | luvL_state_ready(s); 35 | ++roused; 36 | } 37 | return roused; 38 | } 39 | 40 | static int luv_new_cond(lua_State* L) { 41 | luv_cond_t* cond = (luv_cond_t*)lua_newuserdata(L, sizeof(luv_cond_t)); 42 | 43 | lua_pushvalue(L, 1); 44 | luaL_getmetatable(L, LUV_COND_T); 45 | lua_setmetatable(L, -2); 46 | 47 | luvL_cond_init(cond); 48 | return 1; 49 | } 50 | 51 | static int luv_cond_wait(lua_State *L) { 52 | luv_cond_t* cond = (luv_cond_t*)lua_touserdata(L, 1); 53 | luv_state_t* curr; 54 | if (!lua_isnoneornil(L, 2)) { 55 | curr = (luv_state_t*)luaL_checkudata(L, 2, LUV_FIBER_T); 56 | } 57 | else { 58 | curr = (luv_state_t*)luvL_state_self(L); 59 | } 60 | luvL_cond_wait(cond, curr); 61 | return 1; 62 | } 63 | static int luv_cond_signal(lua_State *L) { 64 | luv_cond_t* cond = (luv_cond_t*)lua_touserdata(L, 1); 65 | luvL_cond_signal(cond); 66 | return 1; 67 | } 68 | static int luv_cond_broadcast(lua_State *L) { 69 | luv_cond_t* cond = (luv_cond_t*)lua_touserdata(L, 1); 70 | luvL_cond_broadcast(cond); 71 | return 1; 72 | } 73 | 74 | static int luv_cond_free(lua_State *L) { 75 | luv_cond_t* cond = (luv_cond_t*)lua_touserdata(L, 1); 76 | (void)cond; 77 | return 0; 78 | } 79 | 80 | static int luv_cond_tostring(lua_State *L) { 81 | luv_cond_t* cond = (luv_cond_t*)luaL_checkudata(L, 1, LUV_COND_T); 82 | lua_pushfstring(L, "userdata<%s>: %p", LUV_COND_T, cond); 83 | return 1; 84 | } 85 | 86 | luaL_Reg luv_cond_funcs[] = { 87 | {"create", luv_new_cond} 88 | }; 89 | 90 | luaL_Reg luv_cond_meths[] = { 91 | {"wait", luv_cond_wait}, 92 | {"signal", luv_cond_signal}, 93 | {"broadcast", luv_cond_broadcast}, 94 | {"__gc", luv_cond_free}, 95 | {"__tostring",luv_cond_tostring}, 96 | {NULL, NULL} 97 | }; 98 | 99 | -------------------------------------------------------------------------------- /src/luv_fiber.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | void luvL_fiber_close(luv_fiber_t* fiber) { 4 | if (fiber->flags & LUV_FDEAD) return; 5 | 6 | lua_pushthread(fiber->L); 7 | lua_pushnil(fiber->L); 8 | lua_settable(fiber->L, LUA_REGISTRYINDEX); 9 | 10 | fiber->flags |= LUV_FDEAD; 11 | } 12 | 13 | void luvL_fiber_ready(luv_fiber_t* fiber) { 14 | if (!(fiber->flags & LUV_FREADY)) { 15 | TRACE("insert fiber %p into queue of %p\n", fiber, luvL_thread_self(fiber->L)); 16 | fiber->flags |= LUV_FREADY; 17 | luvL_thread_enqueue(luvL_thread_self(fiber->L), fiber); 18 | } 19 | } 20 | int luvL_fiber_yield(luv_fiber_t* self, int narg) { 21 | luvL_fiber_ready(self); 22 | return lua_yield(self->L, narg); 23 | } 24 | int luvL_fiber_suspend(luv_fiber_t* self) { 25 | TRACE("FIBER SUSPEND - READY? %i\n", self->flags & LUV_FREADY); 26 | if (self->flags & LUV_FREADY) { 27 | self->flags &= ~LUV_FREADY; 28 | if (!luvL_state_is_active((luv_state_t*)self)) { 29 | ngx_queue_remove(&self->queue); 30 | } 31 | TRACE("about to yield...\n"); 32 | return lua_yield(self->L, lua_gettop(self->L)); /* keep our stack */ 33 | } 34 | return 0; 35 | } 36 | int luvL_fiber_resume(luv_fiber_t* self, int narg) { 37 | luvL_fiber_ready(self); 38 | return lua_resume(self->L, narg); 39 | } 40 | 41 | luv_fiber_t* luvL_fiber_create(luv_state_t* outer, int narg) { 42 | TRACE("spawn fiber as child of: %p\n", outer); 43 | 44 | luv_fiber_t* self; 45 | lua_State* L = outer->L; 46 | 47 | int base = lua_gettop(L) - narg + 1; 48 | luaL_checktype(L, base, LUA_TFUNCTION); 49 | 50 | lua_State* L1 = lua_newthread(L); 51 | lua_insert(L, base); /* [thread, func, ...] */ 52 | 53 | lua_checkstack(L1, narg); 54 | lua_xmove(L, L1, narg); /* [thread] */ 55 | 56 | self = (luv_fiber_t*)lua_newuserdata(L, sizeof(luv_fiber_t)); 57 | luaL_getmetatable(L, LUV_FIBER_T); /* [thread, fiber, meta] */ 58 | lua_setmetatable(L, -2); /* [thread, fiber] */ 59 | 60 | lua_pushvalue(L, -1); /* [thread, fiber, fiber] */ 61 | lua_insert(L, base); /* [fiber, thread, fiber] */ 62 | lua_rawset(L, LUA_REGISTRYINDEX); /* [fiber] */ 63 | 64 | while (outer->type != LUV_TTHREAD) outer = outer->outer; 65 | 66 | self->type = LUV_TFIBER; 67 | self->outer = outer; 68 | self->L = L1; 69 | self->flags = 0; 70 | self->data = NULL; 71 | self->loop = outer->loop; 72 | 73 | /* fibers waiting for us to finish */ 74 | ngx_queue_init(&self->rouse); 75 | ngx_queue_init(&self->queue); 76 | 77 | return self; 78 | } 79 | 80 | /* Lua API */ 81 | static int luv_new_fiber(lua_State* L) { 82 | luv_state_t* outer = luvL_state_self(L); 83 | luvL_fiber_create(outer, lua_gettop(L)); 84 | assert(lua_gettop(L) == 1); 85 | return 1; 86 | } 87 | 88 | int luvL_state_xcopy(luv_state_t* a, luv_state_t* b) { 89 | int i, narg; 90 | narg = lua_gettop(a->L); 91 | lua_checkstack(a->L, 1); 92 | lua_checkstack(b->L, narg); 93 | for (i = 1; i <= narg; i++) { 94 | lua_pushvalue(a->L, i); 95 | lua_xmove(a->L, b->L, 1); 96 | } 97 | return narg; 98 | } 99 | 100 | static int luv_fiber_join(lua_State* L) { 101 | luv_fiber_t* self = (luv_fiber_t*)luaL_checkudata(L, 1, LUV_FIBER_T); 102 | luv_state_t* curr = (luv_state_t*)luvL_state_self(L); 103 | TRACE("joining fiber[%p], from [%p]\n", self, curr); 104 | assert((luv_state_t*)self != curr); 105 | if (self->flags & LUV_FDEAD) { 106 | /* seen join after termination */ 107 | TRACE("join after termination\n"); 108 | return luvL_state_xcopy((luv_state_t*)self, curr); 109 | } 110 | ngx_queue_insert_tail(&self->rouse, &curr->join); 111 | luvL_fiber_ready(self); 112 | TRACE("calling luvL_state_suspend on %p\n", curr); 113 | if (curr->type == LUV_TFIBER) { 114 | return luvL_state_suspend(curr); 115 | } 116 | else { 117 | luvL_state_suspend(curr); 118 | return luvL_state_xcopy((luv_state_t*)self, curr); 119 | } 120 | } 121 | 122 | static int luv_fiber_ready(lua_State* L) { 123 | luv_fiber_t* self = (luv_fiber_t*)lua_touserdata(L, 1); 124 | luvL_fiber_ready(self); 125 | return 1; 126 | } 127 | static int luv_fiber_free(lua_State* L) { 128 | luv_fiber_t* self = (luv_fiber_t*)lua_touserdata(L, 1); 129 | if (self->data) free(self->data); 130 | return 1; 131 | } 132 | static int luv_fiber_tostring(lua_State* L) { 133 | luv_fiber_t* self = (luv_fiber_t*)lua_touserdata(L, 1); 134 | lua_pushfstring(L, "userdata<%s>: %p", LUV_FIBER_T, self); 135 | return 1; 136 | } 137 | 138 | luaL_Reg luv_fiber_funcs[] = { 139 | {"create", luv_new_fiber}, 140 | {NULL, NULL} 141 | }; 142 | 143 | luaL_Reg luv_fiber_meths[] = { 144 | {"join", luv_fiber_join}, 145 | {"ready", luv_fiber_ready}, 146 | {"__gc", luv_fiber_free}, 147 | {"__tostring",luv_fiber_tostring}, 148 | {NULL, NULL} 149 | }; 150 | 151 | 152 | -------------------------------------------------------------------------------- /src/luv_fs.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #ifndef _WIN32 5 | #include 6 | #endif 7 | 8 | #include "luv.h" 9 | 10 | /* lifted from luvit */ 11 | static int luv_string_to_flags(lua_State* L, const char* str) { 12 | if (strcmp(str, "r") == 0) 13 | return O_RDONLY; 14 | if (strcmp(str, "r+") == 0) 15 | return O_RDWR; 16 | if (strcmp(str, "w") == 0) 17 | return O_CREAT | O_TRUNC | O_WRONLY; 18 | if (strcmp(str, "w+") == 0) 19 | return O_CREAT | O_TRUNC | O_RDWR; 20 | if (strcmp(str, "a") == 0) 21 | return O_APPEND | O_CREAT | O_WRONLY; 22 | if (strcmp(str, "a+") == 0) 23 | return O_APPEND | O_CREAT | O_RDWR; 24 | return luaL_error(L, "Unknown file open flag: '%s'", str); 25 | } 26 | 27 | /* lifted from luvit */ 28 | static void luv_push_stats_table(lua_State* L, struct stat* s) { 29 | lua_newtable(L); 30 | lua_pushinteger(L, s->st_dev); 31 | lua_setfield(L, -2, "dev"); 32 | lua_pushinteger(L, s->st_ino); 33 | lua_setfield(L, -2, "ino"); 34 | lua_pushinteger(L, s->st_mode); 35 | lua_setfield(L, -2, "mode"); 36 | lua_pushinteger(L, s->st_nlink); 37 | lua_setfield(L, -2, "nlink"); 38 | lua_pushinteger(L, s->st_uid); 39 | lua_setfield(L, -2, "uid"); 40 | lua_pushinteger(L, s->st_gid); 41 | lua_setfield(L, -2, "gid"); 42 | lua_pushinteger(L, s->st_rdev); 43 | lua_setfield(L, -2, "rdev"); 44 | lua_pushinteger(L, s->st_size); 45 | lua_setfield(L, -2, "size"); 46 | #ifdef __POSIX__ 47 | lua_pushinteger(L, s->st_blksize); 48 | lua_setfield(L, -2, "blksize"); 49 | lua_pushinteger(L, s->st_blocks); 50 | lua_setfield(L, -2, "blocks"); 51 | #endif 52 | lua_pushinteger(L, s->st_atime); 53 | lua_setfield(L, -2, "atime"); 54 | lua_pushinteger(L, s->st_mtime); 55 | lua_setfield(L, -2, "mtime"); 56 | lua_pushinteger(L, s->st_ctime); 57 | lua_setfield(L, -2, "ctime"); 58 | #ifndef _WIN32 59 | lua_pushboolean(L, S_ISREG(s->st_mode)); 60 | lua_setfield(L, -2, "is_file"); 61 | lua_pushboolean(L, S_ISDIR(s->st_mode)); 62 | lua_setfield(L, -2, "is_directory"); 63 | lua_pushboolean(L, S_ISCHR(s->st_mode)); 64 | lua_setfield(L, -2, "is_character_device"); 65 | lua_pushboolean(L, S_ISBLK(s->st_mode)); 66 | lua_setfield(L, -2, "is_block_device"); 67 | lua_pushboolean(L, S_ISFIFO(s->st_mode)); 68 | lua_setfield(L, -2, "is_fifo"); 69 | lua_pushboolean(L, S_ISLNK(s->st_mode)); 70 | lua_setfield(L, -2, "is_symbolic_link"); 71 | lua_pushboolean(L, S_ISSOCK(s->st_mode)); 72 | lua_setfield(L, -2, "is_socket"); 73 | #endif 74 | } 75 | 76 | /* the rest of this file is mostly stolen from luvit - key difference is that 77 | ** we don't run Lua callbacks, but instead suspend and resume Lua threads */ 78 | 79 | static void luv_fs_result(lua_State* L, uv_fs_t* req) { 80 | TRACE("enter fs result...\n"); 81 | if (req->result == -1) { 82 | lua_pushnil(L); 83 | lua_pushinteger(L, (uv_err_code)req->errorno); 84 | } 85 | else { 86 | switch (req->fs_type) { 87 | case UV_FS_RENAME: 88 | case UV_FS_UNLINK: 89 | case UV_FS_RMDIR: 90 | case UV_FS_MKDIR: 91 | case UV_FS_FSYNC: 92 | case UV_FS_FTRUNCATE: 93 | case UV_FS_FDATASYNC: 94 | case UV_FS_LINK: 95 | case UV_FS_SYMLINK: 96 | case UV_FS_CHMOD: 97 | case UV_FS_FCHMOD: 98 | case UV_FS_CHOWN: 99 | case UV_FS_FCHOWN: 100 | case UV_FS_UTIME: 101 | case UV_FS_FUTIME: 102 | case UV_FS_CLOSE: 103 | lua_pushinteger(L, req->result); 104 | break; 105 | 106 | case UV_FS_OPEN: 107 | { 108 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, -1, LUV_FILE_T); 109 | self->h.file = req->result; 110 | } 111 | break; 112 | 113 | case UV_FS_READ: 114 | lua_pushinteger(L, req->result); 115 | lua_pushlstring(L, (const char*)req->data, req->result); 116 | free(req->data); 117 | req->data = NULL; 118 | break; 119 | 120 | case UV_FS_WRITE: 121 | lua_pushinteger(L, req->result); 122 | break; 123 | 124 | case UV_FS_READLINK: 125 | lua_pushstring(L, (char*)req->ptr); 126 | break; 127 | 128 | case UV_FS_READDIR: 129 | { 130 | int i; 131 | char* namep = (char*)req->ptr; 132 | int count = req->result; 133 | lua_newtable(L); 134 | for (i = 1; i <= count; i++) { 135 | lua_pushstring(L, namep); 136 | lua_rawseti(L, -2, i); 137 | namep += strlen(namep) + 1; /* +1 for '\0' */ 138 | } 139 | } 140 | break; 141 | 142 | case UV_FS_STAT: 143 | case UV_FS_LSTAT: 144 | case UV_FS_FSTAT: 145 | luv_push_stats_table(L, (struct stat*)req->ptr); 146 | break; 147 | 148 | default: 149 | luaL_error(L, "Unhandled fs_type"); 150 | } 151 | } 152 | uv_fs_req_cleanup(req); 153 | } 154 | 155 | static void luv_fs_cb(uv_fs_t* req) { 156 | luv_state_t* state = container_of(req, luv_state_t, req); 157 | luv_fs_result(state->L, req); 158 | luvL_state_ready(state); 159 | } 160 | 161 | #define LUV_FS_CALL(L, func, misc, ...) do { \ 162 | luv_state_t* curr = luvL_state_self(L); \ 163 | uv_loop_t* loop = luvL_event_loop(L); \ 164 | uv_fs_t* req; \ 165 | uv_fs_cb cb; \ 166 | req = &curr->req.fs; \ 167 | if (curr->type == LUV_TTHREAD) { \ 168 | /* synchronous in main */ \ 169 | cb = NULL; \ 170 | } \ 171 | else { \ 172 | cb = luv_fs_cb; \ 173 | } \ 174 | req->data = misc; \ 175 | \ 176 | if (uv_fs_##func(loop, req, __VA_ARGS__, cb) < 0) { \ 177 | uv_err_t err = uv_last_error(loop); \ 178 | lua_settop(L, 0); \ 179 | lua_pushboolean(L, 0); \ 180 | lua_pushstring(L, uv_strerror(err)); \ 181 | } \ 182 | if (curr->type == LUV_TTHREAD) { \ 183 | luv_fs_result(L, req); \ 184 | return lua_gettop(L); \ 185 | } \ 186 | else { \ 187 | TRACE("suspending...\n"); \ 188 | return luvL_state_suspend(curr); \ 189 | } \ 190 | } while(0) 191 | 192 | static int luv_fs_open(lua_State* L) { 193 | luv_state_t* curr = luvL_state_self(L); 194 | const char* path = luaL_checkstring(L, 1); 195 | luv_object_t* self; 196 | 197 | int flags = luv_string_to_flags(L, luaL_checkstring(L, 2)); 198 | int mode = strtoul(luaL_checkstring(L, 3), NULL, 8); 199 | 200 | lua_settop(L, 0); 201 | 202 | self = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 203 | luaL_getmetatable(L, LUV_FILE_T); 204 | lua_setmetatable(L, -2); 205 | luvL_object_init(curr, self); 206 | 207 | self->h.file = -1; /* invalid file handle */ 208 | LUV_FS_CALL(L, open, NULL, path, flags, mode); 209 | } 210 | 211 | static int luv_fs_unlink(lua_State* L) { 212 | const char* path = luaL_checkstring(L, 1); 213 | lua_settop(L, 0); 214 | LUV_FS_CALL(L, unlink, NULL, path); 215 | } 216 | 217 | static int luv_fs_mkdir(lua_State* L) { 218 | const char* path = luaL_checkstring(L, 1); 219 | int mode = strtoul(luaL_checkstring(L, 2), NULL, 8); 220 | lua_settop(L, 0); 221 | LUV_FS_CALL(L, mkdir, NULL, path, mode); 222 | } 223 | 224 | static int luv_fs_rmdir(lua_State* L) { 225 | const char* path = luaL_checkstring(L, 1); 226 | lua_settop(L, 0); 227 | LUV_FS_CALL(L, rmdir, NULL, path); 228 | } 229 | 230 | static int luv_fs_readdir(lua_State* L) { 231 | const char* path = luaL_checkstring(L, 1); 232 | lua_settop(L, 0); 233 | LUV_FS_CALL(L, readdir, NULL, path, 0); 234 | } 235 | 236 | static int luv_fs_stat(lua_State* L) { 237 | const char* path = luaL_checkstring(L, 1); 238 | lua_settop(L, 0); 239 | LUV_FS_CALL(L, stat, NULL, path); 240 | } 241 | 242 | static int luv_fs_rename(lua_State* L) { 243 | const char* old_path = luaL_checkstring(L, 1); 244 | const char* new_path = luaL_checkstring(L, 2); 245 | lua_settop(L, 0); 246 | LUV_FS_CALL(L, rename, NULL, old_path, new_path); 247 | } 248 | 249 | static int luv_fs_sendfile(lua_State* L) { 250 | luv_object_t* o_file = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 251 | luv_object_t* i_file = (luv_object_t*)luaL_checkudata(L, 2, LUV_FILE_T); 252 | off_t ofs = luaL_checkint(L, 3); 253 | size_t len = luaL_checkint(L, 4); 254 | lua_settop(L, 2); 255 | LUV_FS_CALL(L, sendfile, NULL, o_file->h.file, i_file->h.file, ofs, len); 256 | } 257 | 258 | static int luv_fs_chmod(lua_State* L) { 259 | const char* path = luaL_checkstring(L, 1); 260 | int mode = strtoul(luaL_checkstring(L, 2), NULL, 8); 261 | lua_settop(L, 0); 262 | LUV_FS_CALL(L, chmod, NULL, path, mode); 263 | } 264 | 265 | static int luv_fs_utime(lua_State* L) { 266 | const char* path = luaL_checkstring(L, 1); 267 | double atime = luaL_checknumber(L, 2); 268 | double mtime = luaL_checknumber(L, 3); 269 | lua_settop(L, 0); 270 | LUV_FS_CALL(L, utime, NULL, path, atime, mtime); 271 | } 272 | 273 | static int luv_fs_lstat(lua_State* L) { 274 | const char* path = luaL_checkstring(L, 1); 275 | lua_settop(L, 0); 276 | LUV_FS_CALL(L, lstat, NULL, path); 277 | } 278 | 279 | static int luv_fs_link(lua_State* L) { 280 | const char* src_path = luaL_checkstring(L, 1); 281 | const char* dst_path = luaL_checkstring(L, 2); 282 | lua_settop(L, 0); 283 | LUV_FS_CALL(L, link, NULL, src_path, dst_path); 284 | } 285 | 286 | static int luv_fs_symlink(lua_State* L) { 287 | const char* src_path = luaL_checkstring(L, 1); 288 | const char* dst_path = luaL_checkstring(L, 2); 289 | int flags = luv_string_to_flags(L, luaL_checkstring(L, 3)); 290 | lua_settop(L, 0); 291 | LUV_FS_CALL(L, symlink, NULL, src_path, dst_path, flags); 292 | } 293 | 294 | static int luv_fs_readlink(lua_State* L) { 295 | const char* path = luaL_checkstring(L, 1); 296 | lua_settop(L, 0); 297 | LUV_FS_CALL(L, readlink, NULL, path); 298 | } 299 | 300 | static int luv_fs_chown(lua_State* L) { 301 | const char* path = luaL_checkstring(L, 1); 302 | int uid = luaL_checkint(L, 2); 303 | int gid = luaL_checkint(L, 3); 304 | lua_settop(L, 0); 305 | LUV_FS_CALL(L, chown, NULL, path, uid, gid); 306 | } 307 | 308 | static int luv_fs_cwd(lua_State* L) { 309 | char buffer[LUV_MAX_PATH]; 310 | uv_err_t err = uv_cwd(buffer, LUV_MAX_PATH); 311 | if (err.code) { 312 | return luaL_error(L, uv_strerror(err)); 313 | } 314 | lua_pushstring(L, buffer); 315 | return 1; 316 | } 317 | 318 | static int luv_fs_chdir(lua_State* L) { 319 | const char* dir = luaL_checkstring(L, 1); 320 | uv_err_t err = uv_chdir(dir); 321 | if (err.code) { 322 | return luaL_error(L, uv_strerror(err)); 323 | } 324 | return 0; 325 | } 326 | 327 | static int luv_fs_exepath(lua_State* L) { 328 | char buffer[LUV_MAX_PATH]; 329 | size_t len = LUV_MAX_PATH; 330 | uv_exepath(buffer, &len); 331 | lua_pushlstring(L, buffer, len); 332 | return 1; 333 | } 334 | 335 | 336 | /* file instance methods */ 337 | static int luv_file_stat(lua_State* L) { 338 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 339 | lua_settop(L, 0); 340 | LUV_FS_CALL(L, fstat, NULL, self->h.file); 341 | } 342 | 343 | static int luv_file_sync(lua_State* L) { 344 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 345 | lua_settop(L, 0); 346 | LUV_FS_CALL(L, fsync, NULL, self->h.file); 347 | } 348 | 349 | static int luv_file_datasync(lua_State* L) { 350 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 351 | lua_settop(L, 0); 352 | LUV_FS_CALL(L, fdatasync, NULL, self->h.file); 353 | } 354 | 355 | static int luv_file_truncate(lua_State* L) { 356 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 357 | off_t ofs = luaL_checkint(L, 2); 358 | lua_settop(L, 0); 359 | LUV_FS_CALL(L, ftruncate, NULL, self->h.file, ofs); 360 | } 361 | 362 | static int luv_file_utime(lua_State* L) { 363 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 364 | double atime = luaL_checknumber(L, 2); 365 | double mtime = luaL_checknumber(L, 3); 366 | lua_settop(L, 0); 367 | LUV_FS_CALL(L, futime, NULL, self->h.file, atime, mtime); 368 | } 369 | 370 | static int luv_file_chmod(lua_State* L) { 371 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 372 | int mode = strtoul(luaL_checkstring(L, 2), NULL, 8); 373 | lua_settop(L, 0); 374 | LUV_FS_CALL(L, fchmod, NULL, self->h.file, mode); 375 | } 376 | 377 | static int luv_file_chown(lua_State* L) { 378 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 379 | int uid = luaL_checkint(L, 2); 380 | int gid = luaL_checkint(L, 3); 381 | lua_settop(L, 0); 382 | LUV_FS_CALL(L, fchown, NULL, self->h.file, uid, gid); 383 | } 384 | 385 | static int luv_file_read(lua_State *L) { 386 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 387 | 388 | size_t len = luaL_optint(L, 2, LUV_BUF_SIZE); 389 | int64_t ofs = luaL_optint(L, 3, -1); 390 | void* buf = malloc(len); /* free from ctx->req.fs_req.data in cb */ 391 | 392 | lua_settop(L, 0); 393 | LUV_FS_CALL(L, read, buf, self->h.file, buf, len, ofs); 394 | } 395 | 396 | static int luv_file_write(lua_State *L) { 397 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 398 | 399 | size_t len; 400 | void* buf = (void*)luaL_checklstring(L, 2, &len); 401 | uint64_t ofs = luaL_optint(L, 3, 0); 402 | 403 | lua_settop(L, 0); 404 | LUV_FS_CALL(L, write, NULL, self->h.file, buf, len, ofs); 405 | } 406 | 407 | static int luv_file_close(lua_State *L) { 408 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 409 | lua_settop(L, 0); 410 | LUV_FS_CALL(L, close, NULL, self->h.file); 411 | } 412 | 413 | static int luv_file_free(lua_State *L) { 414 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 415 | if (self->data) free(self->data); 416 | return 0; 417 | } 418 | 419 | static int luv_file_tostring(lua_State *L) { 420 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_FILE_T); 421 | lua_pushfstring(L, "userdata<%s>: %p", LUV_FILE_T, self); 422 | return 1; 423 | } 424 | 425 | luaL_Reg luv_fs_funcs[] = { 426 | {"open", luv_fs_open}, 427 | {"unlink", luv_fs_unlink}, 428 | {"mkdir", luv_fs_mkdir}, 429 | {"rmdir", luv_fs_rmdir}, 430 | {"readdir", luv_fs_readdir}, 431 | {"stat", luv_fs_stat}, 432 | {"rename", luv_fs_rename}, 433 | {"sendfile", luv_fs_sendfile}, 434 | {"chmod", luv_fs_chmod}, 435 | {"chown", luv_fs_chown}, 436 | {"utime", luv_fs_utime}, 437 | {"lstat", luv_fs_lstat}, 438 | {"link", luv_fs_link}, 439 | {"symlink", luv_fs_symlink}, 440 | {"readlink", luv_fs_readlink}, 441 | {"cwd", luv_fs_cwd}, 442 | {"chdir", luv_fs_chdir}, 443 | {"exepath", luv_fs_exepath}, 444 | {NULL, NULL} 445 | }; 446 | 447 | luaL_Reg luv_file_meths[] = { 448 | {"read", luv_file_read}, 449 | {"write", luv_file_write}, 450 | {"close", luv_file_close}, 451 | {"stat", luv_file_stat}, 452 | {"sync", luv_file_sync}, 453 | {"utime", luv_file_utime}, 454 | {"chmod", luv_file_chmod}, 455 | {"chown", luv_file_chown}, 456 | {"datasync", luv_file_datasync}, 457 | {"truncate", luv_file_truncate}, 458 | {"__gc", luv_file_free}, 459 | {"__tostring",luv_file_tostring}, 460 | {NULL, NULL} 461 | }; 462 | 463 | 464 | -------------------------------------------------------------------------------- /src/luv_idle.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | static void _idle_cb(uv_idle_t* handle, int status) { 4 | luv_object_t* self = container_of(handle, luv_object_t, h); 5 | ngx_queue_t* q; 6 | luv_state_t* s; 7 | ngx_queue_foreach(q, &self->rouse) { 8 | s = ngx_queue_data(q, luv_state_t, cond); 9 | lua_settop(s->L, 0); 10 | lua_pushinteger(s->L, status); 11 | } 12 | luvL_cond_broadcast(&self->rouse); 13 | } 14 | 15 | static int luv_new_idle(lua_State* L) { 16 | luv_object_t* self = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 17 | luaL_getmetatable(L, LUV_IDLE_T); 18 | lua_setmetatable(L, -2); 19 | 20 | luv_state_t* curr = luvL_state_self(L); 21 | uv_idle_init(luvL_event_loop(L), &self->h.idle); 22 | luvL_object_init(curr, self); 23 | 24 | return 1; 25 | } 26 | 27 | /* methods */ 28 | static int luv_idle_start(lua_State* L) { 29 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_IDLE_T); 30 | int rv = uv_idle_start(&self->h.idle, _idle_cb); 31 | lua_pushinteger(L, rv); 32 | return 1; 33 | } 34 | 35 | static int luv_idle_stop(lua_State* L) { 36 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_IDLE_T); 37 | lua_pushinteger(L, uv_idle_stop(&self->h.idle)); 38 | return 1; 39 | } 40 | 41 | static int luv_idle_wait(lua_State *L) { 42 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_IDLE_T); 43 | luv_state_t* state = luvL_state_self(L); 44 | return luvL_cond_wait(&self->rouse, state); 45 | } 46 | 47 | static int luv_idle_free(lua_State *L) { 48 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 49 | luvL_object_close(self); 50 | return 1; 51 | } 52 | static int luv_idle_tostring(lua_State *L) { 53 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_IDLE_T); 54 | lua_pushfstring(L, "userdata<%s>: %p", LUV_IDLE_T, self); 55 | return 1; 56 | } 57 | 58 | luaL_Reg luv_idle_funcs[] = { 59 | {"create", luv_new_idle}, 60 | {NULL, NULL} 61 | }; 62 | 63 | luaL_Reg luv_idle_meths[] = { 64 | {"start", luv_idle_start}, 65 | {"stop", luv_idle_stop}, 66 | {"wait", luv_idle_wait}, 67 | {"__gc", luv_idle_free}, 68 | {"__tostring",luv_idle_tostring}, 69 | {NULL, NULL} 70 | }; 71 | -------------------------------------------------------------------------------- /src/luv_net.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | #include 3 | 4 | static int luv_new_tcp(lua_State* L) { 5 | luv_state_t* curr = luvL_state_self(L); 6 | luv_object_t* self = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 7 | luaL_getmetatable(L, LUV_NET_TCP_T); 8 | lua_setmetatable(L, -2); 9 | 10 | luvL_object_init(curr, self); 11 | 12 | uv_tcp_init(luvL_event_loop(L), &self->h.tcp); 13 | return 1; 14 | } 15 | 16 | static void _getaddrinfo_cb(uv_getaddrinfo_t* req, int s, struct addrinfo* ai) { 17 | luv_state_t* curr = container_of(req, luv_state_t, req); 18 | char host[INET6_ADDRSTRLEN]; 19 | int port = 0; 20 | 21 | if (ai->ai_family == PF_INET) { 22 | struct sockaddr_in* addr = (struct sockaddr_in*)ai->ai_addr; 23 | uv_ip4_name(addr, host, INET6_ADDRSTRLEN); 24 | port = addr->sin_port; 25 | } 26 | else if (ai->ai_family == PF_INET6) { 27 | struct sockaddr_in6* addr = (struct sockaddr_in6*)ai->ai_addr; 28 | uv_ip6_name(addr, host, INET6_ADDRSTRLEN); 29 | port = addr->sin6_port; 30 | } 31 | lua_settop(curr->L, 0); 32 | lua_pushstring(curr->L, host); 33 | lua_pushinteger(curr->L, port); 34 | 35 | uv_freeaddrinfo(ai); 36 | 37 | luvL_state_ready(curr); 38 | } 39 | 40 | static int luv_getaddrinfo(lua_State* L) { 41 | luv_state_t* curr = luvL_state_self(L); 42 | uv_loop_t* loop = luvL_event_loop(L); 43 | uv_getaddrinfo_t* req = &curr->req.getaddrinfo; 44 | 45 | const char* node = NULL; 46 | const char* service = NULL; 47 | struct addrinfo hints; 48 | 49 | if (!lua_isnoneornil(L, 1)) { 50 | node = luaL_checkstring(L, 1); 51 | } 52 | if (!lua_isnoneornil(L, 2)) { 53 | service = luaL_checkstring(L, 2); 54 | } 55 | if (node == NULL && service == NULL) { 56 | return luaL_error(L, "getaddrinfo: provide either node or service"); 57 | } 58 | 59 | hints.ai_family = PF_INET; 60 | hints.ai_socktype = SOCK_STREAM; 61 | hints.ai_protocol = IPPROTO_TCP; 62 | hints.ai_flags = 0; 63 | 64 | if (lua_istable(L, 3)) { 65 | lua_getfield(L, 3, "family"); 66 | if (!lua_isnil(L, -1)) { 67 | const char* s = lua_tostring(L, -1); 68 | if (strcmp(s, "INET") == 0) { 69 | hints.ai_family = PF_INET; 70 | } 71 | else if (strcmp(s, "INET6")) { 72 | hints.ai_family = PF_INET6; 73 | } 74 | else { 75 | return luaL_error(L, "unsupported family: %s", s); 76 | } 77 | } 78 | lua_pop(L, 1); 79 | 80 | lua_getfield(L, 3, "socktype"); 81 | if (!lua_isnil(L, -1)) { 82 | const char* s = lua_tostring(L, -1); 83 | if (strcmp(s, "STREAM")) { 84 | hints.ai_socktype = SOCK_STREAM; 85 | } 86 | else if (strcmp(s, "DGRAM")) { 87 | hints.ai_socktype = SOCK_DGRAM; 88 | } 89 | else { 90 | return luaL_error(L, "unsupported socktype: %s", s); 91 | } 92 | } 93 | lua_pop(L, 1); 94 | 95 | lua_getfield(L, 3, "protocol"); 96 | if (!lua_isnil(L, -1)) { 97 | const char* s = lua_tostring(L, -1); 98 | if (strcmp(s, "TCP")) { 99 | hints.ai_protocol = IPPROTO_TCP; 100 | } 101 | else if (strcmp(s, "UDP")) { 102 | hints.ai_protocol = IPPROTO_UDP; 103 | } 104 | else { 105 | return luaL_error(L, "unsupported protocol: %s", s); 106 | } 107 | } 108 | lua_pop(L, 1); 109 | } 110 | 111 | int rv = uv_getaddrinfo(loop, req, _getaddrinfo_cb, node, service, &hints); 112 | if (rv) { 113 | uv_err_t err = uv_last_error(loop); 114 | return luaL_error(L, uv_strerror(err)); 115 | } 116 | 117 | return luvL_state_suspend(curr); 118 | } 119 | 120 | static int luv_tcp_bind(lua_State* L) { 121 | luv_object_t *self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_TCP_T); 122 | 123 | struct sockaddr_in addr; 124 | const char* host; 125 | int port, rv; 126 | 127 | host = luaL_checkstring(L, 2); 128 | port = luaL_checkint(L, 3); 129 | addr = uv_ip4_addr(host, port); 130 | 131 | rv = uv_tcp_bind(&self->h.tcp, addr); 132 | lua_pushinteger(L, rv); 133 | 134 | return 1; 135 | } 136 | 137 | static int luv_tcp_connect(lua_State *L) { 138 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_TCP_T); 139 | luv_state_t* curr = luvL_state_self(L); 140 | 141 | struct sockaddr_in addr; 142 | const char* host; 143 | int port, rv; 144 | 145 | host = luaL_checkstring(L, 2); 146 | port = luaL_checkint(L, 3); 147 | addr = uv_ip4_addr(host, port); 148 | 149 | lua_settop(L, 2); 150 | 151 | rv = uv_tcp_connect(&curr->req.connect, &self->h.tcp, addr, luvL_connect_cb); 152 | if (rv) { 153 | uv_err_t err = uv_last_error(self->h.handle.loop); 154 | lua_settop(L, 0); 155 | lua_pushnil(L); 156 | lua_pushstring(L, uv_strerror(err)); 157 | return 2; 158 | } 159 | 160 | return luvL_state_suspend(curr); 161 | } 162 | 163 | static int luv_tcp_nodelay(lua_State* L) { 164 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_TCP_T); 165 | luaL_checktype(L, 2, LUA_TBOOLEAN); 166 | int enable = lua_toboolean(L, 2); 167 | lua_settop(L, 2); 168 | int rv = uv_tcp_nodelay(&self->h.tcp, enable); 169 | lua_pushinteger(L, rv); 170 | return 1; 171 | } 172 | static int luv_tcp_keepalive(lua_State* L) { 173 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_TCP_T); 174 | luaL_checktype(L, 2, LUA_TBOOLEAN); 175 | int enable = lua_toboolean(L, 2); 176 | unsigned int delay = 0; 177 | if (enable) { 178 | delay = luaL_checkint(L, 3); 179 | } 180 | int rv = uv_tcp_keepalive(&self->h.tcp, enable, delay); 181 | lua_settop(L, 1); 182 | lua_pushinteger(L, rv); 183 | return 1; 184 | } 185 | 186 | /* mostly stolen from Luvit */ 187 | static int luv_tcp_getsockname(lua_State* L) { 188 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_TCP_T); 189 | 190 | int port = 0; 191 | char ip[INET6_ADDRSTRLEN]; 192 | int family; 193 | 194 | struct sockaddr_storage addr; 195 | int len = sizeof(addr); 196 | 197 | if (uv_tcp_getsockname(&self->h.tcp, (struct sockaddr*)&addr, &len)) { 198 | uv_err_t err = uv_last_error(luvL_event_loop(L)); 199 | return luaL_error(L, "getsockname: %s", uv_strerror(err)); 200 | } 201 | 202 | family = addr.ss_family; 203 | if (family == AF_INET) { 204 | struct sockaddr_in* addrin = (struct sockaddr_in*)&addr; 205 | uv_inet_ntop(AF_INET, &(addrin->sin_addr), ip, INET6_ADDRSTRLEN); 206 | port = ntohs(addrin->sin_port); 207 | } 208 | else if (family == AF_INET6) { 209 | struct sockaddr_in6* addrin6 = (struct sockaddr_in6*)&addr; 210 | uv_inet_ntop(AF_INET6, &(addrin6->sin6_addr), ip, INET6_ADDRSTRLEN); 211 | port = ntohs(addrin6->sin6_port); 212 | } 213 | 214 | lua_newtable(L); 215 | lua_pushnumber(L, port); 216 | lua_setfield(L, -2, "port"); 217 | lua_pushnumber(L, family); 218 | lua_setfield(L, -2, "family"); 219 | lua_pushstring(L, ip); 220 | lua_setfield(L, -2, "address"); 221 | 222 | return 1; 223 | } 224 | 225 | /* mostly stolen from Luvit */ 226 | static int luv_tcp_getpeername(lua_State* L) { 227 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_TCP_T); 228 | 229 | int port = 0; 230 | char ip[INET6_ADDRSTRLEN]; 231 | int family; 232 | 233 | struct sockaddr_storage addr; 234 | int len = sizeof(addr); 235 | 236 | if (uv_tcp_getpeername(&self->h.tcp, (struct sockaddr*)&addr, &len)) { 237 | uv_err_t err = uv_last_error(luvL_event_loop(L)); 238 | return luaL_error(L, "getpeername: %s", uv_strerror(err)); 239 | } 240 | 241 | family = addr.ss_family; 242 | if (family == AF_INET) { 243 | struct sockaddr_in* addrin = (struct sockaddr_in*)&addr; 244 | uv_inet_ntop(AF_INET, &(addrin->sin_addr), ip, INET6_ADDRSTRLEN); 245 | port = ntohs(addrin->sin_port); 246 | } 247 | else if (family == AF_INET6) { 248 | struct sockaddr_in6* addrin6 = (struct sockaddr_in6*)&addr; 249 | uv_inet_ntop(AF_INET6, &(addrin6->sin6_addr), ip, INET6_ADDRSTRLEN); 250 | port = ntohs(addrin6->sin6_port); 251 | } 252 | 253 | lua_newtable(L); 254 | lua_pushnumber(L, port); 255 | lua_setfield(L, -2, "port"); 256 | lua_pushnumber(L, family); 257 | lua_setfield(L, -2, "family"); 258 | lua_pushstring(L, ip); 259 | lua_setfield(L, -2, "address"); 260 | 261 | return 1; 262 | } 263 | 264 | static int luv_tcp_tostring(lua_State *L) { 265 | luv_object_t *self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_TCP_T); 266 | lua_pushfstring(L, "userdata<%s>: %p", LUV_NET_TCP_T, self); 267 | return 1; 268 | } 269 | 270 | static int luv_new_udp(lua_State* L) { 271 | luv_state_t* curr = luvL_state_self(L); 272 | luv_object_t* self = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 273 | luaL_getmetatable(L, LUV_NET_UDP_T); 274 | lua_setmetatable(L, -2); 275 | luvL_object_init(curr, self); 276 | 277 | uv_udp_init(luvL_event_loop(L), &self->h.udp); 278 | return 1; 279 | } 280 | 281 | static int luv_udp_bind(lua_State* L) { 282 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_UDP_T); 283 | const char* host = luaL_checkstring(L, 2); 284 | int port = luaL_checkint(L, 3); 285 | 286 | int flags = 0; 287 | 288 | struct sockaddr_in address = uv_ip4_addr(host, port); 289 | 290 | if (uv_udp_bind(&self->h.udp, address, flags)) { 291 | uv_err_t err = uv_last_error(luvL_event_loop(L)); 292 | return luaL_error(L, uv_strerror(err)); 293 | } 294 | 295 | return 0; 296 | } 297 | 298 | static void _send_cb(uv_udp_send_t* req, int status) { 299 | luv_state_t* curr = container_of(req, luv_state_t, req); 300 | luvL_state_ready(curr); 301 | } 302 | 303 | static int luv_udp_send(lua_State* L) { 304 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_UDP_T); 305 | luv_state_t* curr = luvL_state_self(L); 306 | 307 | size_t len; 308 | 309 | const char* host = luaL_checkstring(L, 2); 310 | int port = luaL_checkint(L, 3); 311 | const char* mesg = luaL_checklstring(L, 4, &len); 312 | 313 | uv_buf_t buf = uv_buf_init((char*)mesg, len); 314 | struct sockaddr_in addr = uv_ip4_addr(host, port); 315 | 316 | if (uv_udp_send(&curr->req.udp_send, &self->h.udp, &buf, 1, addr, _send_cb)) { 317 | /* TODO: this shouldn't be fatal */ 318 | uv_err_t err = uv_last_error(luvL_event_loop(L)); 319 | return luaL_error(L, uv_strerror(err)); 320 | } 321 | 322 | return luvL_state_suspend(curr); 323 | } 324 | 325 | static void _recv_cb(uv_udp_t* handle, ssize_t nread, uv_buf_t buf, struct sockaddr* peer, unsigned flags) { 326 | luv_object_t* self = container_of(handle, luv_object_t, h); 327 | ngx_queue_t* q; 328 | luv_state_t* s; 329 | 330 | char host[INET6_ADDRSTRLEN]; 331 | int port = 0; 332 | 333 | ngx_queue_foreach(q, &self->rouse) { 334 | s = ngx_queue_data(q, luv_state_t, cond); 335 | 336 | lua_settop(s->L, 0); 337 | lua_pushlstring(s->L, buf.base, buf.len); 338 | 339 | if (peer->sa_family == PF_INET) { 340 | struct sockaddr_in* addr = (struct sockaddr_in*)peer; 341 | uv_ip4_name(addr, host, INET6_ADDRSTRLEN); 342 | port = addr->sin_port; 343 | } 344 | else if (peer->sa_family == PF_INET6) { 345 | struct sockaddr_in6* addr = (struct sockaddr_in6*)peer; 346 | uv_ip6_name(addr, host, INET6_ADDRSTRLEN); 347 | port = addr->sin6_port; 348 | } 349 | 350 | lua_pushstring(s->L, host); 351 | lua_pushinteger(s->L, port); 352 | /* [ mesg, host, port ] */ 353 | } 354 | luvL_cond_signal(&self->rouse); 355 | } 356 | 357 | static int luv_udp_recv(lua_State* L) { 358 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_UDP_T); 359 | if (!luvL_object_is_started(self)) { 360 | self->flags |= LUV_OSTARTED; 361 | uv_udp_recv_start(&self->h.udp, luvL_alloc_cb, _recv_cb); 362 | } 363 | return luvL_cond_wait(&self->rouse, luvL_state_self(L)); 364 | } 365 | 366 | static const char* LUV_UDP_MEMBERSHIP_OPTS[] = { "join", "leave", NULL }; 367 | 368 | int luv_udp_membership(lua_State* L) { 369 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_UDP_T); 370 | const char* iaddr = luaL_checkstring(L, 3); 371 | const char* maddr = luaL_checkstring(L, 2); 372 | 373 | int option = luaL_checkoption(L, 4, NULL, LUV_UDP_MEMBERSHIP_OPTS); 374 | uv_membership membership = option ? UV_LEAVE_GROUP : UV_JOIN_GROUP; 375 | 376 | if (uv_udp_set_membership(&self->h.udp, maddr, iaddr, membership)) { 377 | uv_err_t err = uv_last_error(luvL_event_loop(L)); 378 | return luaL_error(L, uv_strerror(err)); 379 | } 380 | 381 | return 0; 382 | } 383 | 384 | static int luv_udp_free(lua_State *L) { 385 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 386 | luvL_object_close(self); 387 | return 1; 388 | } 389 | static int luv_udp_tostring(lua_State *L) { 390 | luv_object_t *self = (luv_object_t*)luaL_checkudata(L, 1, LUV_NET_UDP_T); 391 | lua_pushfstring(L, "userdata<%s>: %p", LUV_NET_UDP_T, self); 392 | return 1; 393 | } 394 | 395 | luaL_Reg luv_net_funcs[] = { 396 | {"tcp", luv_new_tcp}, 397 | {"udp", luv_new_udp}, 398 | {"getaddrinfo", luv_getaddrinfo}, 399 | {NULL, NULL} 400 | }; 401 | 402 | luaL_Reg luv_net_tcp_meths[] = { 403 | {"bind", luv_tcp_bind}, 404 | {"connect", luv_tcp_connect}, 405 | {"getsockname", luv_tcp_getsockname}, 406 | {"getpeername", luv_tcp_getpeername}, 407 | {"keepalive", luv_tcp_keepalive}, 408 | {"nodelay", luv_tcp_nodelay}, 409 | {"__tostring", luv_tcp_tostring}, 410 | {NULL, NULL} 411 | }; 412 | 413 | luaL_Reg luv_net_udp_meths[] = { 414 | {"bind", luv_udp_bind}, 415 | {"send", luv_udp_send}, 416 | {"recv", luv_udp_recv}, 417 | {"membership",luv_udp_membership}, 418 | {"__gc", luv_udp_free}, 419 | {"__tostring",luv_udp_tostring}, 420 | {NULL, NULL} 421 | }; 422 | 423 | -------------------------------------------------------------------------------- /src/luv_object.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | void luvL_object_init(luv_state_t* state, luv_object_t* self) { 4 | ngx_queue_init(&self->rouse); 5 | ngx_queue_init(&self->queue); 6 | self->state = state; 7 | self->data = NULL; 8 | self->flags = 0; 9 | self->count = 0; 10 | self->buf.base = NULL; 11 | self->buf.len = 0; 12 | } 13 | 14 | void luvL_object_close_cb(uv_handle_t* handle) { 15 | luv_object_t* self = container_of(handle, luv_object_t, h); 16 | TRACE("object closed %p\n", self); 17 | self->flags |= LUV_OCLOSED; 18 | self->state = NULL; 19 | luvL_cond_signal(&self->rouse); 20 | } 21 | 22 | void luvL_object_close(luv_object_t* self) { 23 | if (!luvL_object_is_closing(self)) { 24 | TRACE("object closing %p, type: %i\n", self, self->h.handle.type); 25 | self->flags |= LUV_OCLOSING; 26 | uv_close(&self->h.handle, luvL_object_close_cb); 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/luv_pipe.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | static int luv_new_pipe(lua_State* L) { 4 | luv_object_t* self = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 5 | luaL_getmetatable(L, LUV_PIPE_T); 6 | lua_setmetatable(L, -2); 7 | int ipc = 0; 8 | if (!lua_isnoneornil(L, 2)) { 9 | luaL_checktype(L, 2, LUA_TBOOLEAN); 10 | ipc = lua_toboolean(L, 2); 11 | } 12 | uv_pipe_init(luvL_event_loop(L), &self->h.pipe, ipc); 13 | return 1; 14 | } 15 | 16 | static int luv_pipe_open(lua_State* L) { 17 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_PIPE_T); 18 | uv_file fh; 19 | if (lua_isuserdata(L, 2)) { 20 | luv_object_t* file = (luv_object_t*)luaL_checkudata(L, 2, LUV_FILE_T); 21 | fh = file->h.file; 22 | } 23 | else { 24 | fh = luaL_checkint(L, 2); 25 | } 26 | uv_pipe_open(&self->h.pipe, fh); 27 | return 0; 28 | } 29 | 30 | static int luv_pipe_bind(lua_State* L) { 31 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_PIPE_T); 32 | const char* path = luaL_checkstring(L, 2); 33 | 34 | if (uv_pipe_bind(&self->h.pipe, path)) { 35 | uv_err_t err = uv_last_error(self->h.pipe.loop); 36 | return luaL_error(L, uv_strerror(err)); 37 | } 38 | 39 | return 0; 40 | } 41 | 42 | static int luv_pipe_connect(lua_State* L) { 43 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_PIPE_T); 44 | const char* path = luaL_checkstring(L, 2); 45 | luv_state_t* curr = luvL_state_self(L); 46 | 47 | uv_pipe_connect(&curr->req.connect, &self->h.pipe, path, luvL_connect_cb); 48 | 49 | return 0; 50 | } 51 | 52 | static int luv_pipe_tostring(lua_State *L) { 53 | luv_object_t *self = (luv_object_t*)luaL_checkudata(L, 1, LUV_PIPE_T); 54 | lua_pushfstring(L, "userdata<%s>: %p", LUV_PIPE_T, self); 55 | return 1; 56 | } 57 | 58 | luaL_Reg luv_pipe_funcs[] = { 59 | {"create", luv_new_pipe}, 60 | {NULL, NULL} 61 | }; 62 | 63 | luaL_Reg luv_pipe_meths[] = { 64 | {"open", luv_pipe_open}, 65 | {"bind", luv_pipe_bind}, 66 | {"connect", luv_pipe_connect}, 67 | {"__tostring", luv_pipe_tostring}, 68 | {NULL, NULL} 69 | }; 70 | 71 | -------------------------------------------------------------------------------- /src/luv_process.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | void _exit_cb(uv_process_t* handle, int status, int sigterm) { 4 | TRACE("EXIT : status %i, sigterm %i\n", status, sigterm); 5 | luv_object_t* self = container_of(handle, luv_object_t, h); 6 | 7 | if (status == -1) { 8 | TRACE("ERROR: %s\n", uv_strerror(uv_last_error(self->state->loop))); 9 | } 10 | 11 | lua_State* L = self->state->L; 12 | lua_pushinteger(L, status); 13 | lua_pushinteger(L, sigterm); 14 | 15 | luvL_cond_signal(&self->rouse); 16 | } 17 | 18 | /* 19 | luv.process.spawn("cat", { 20 | "foo.txt", 21 | env = { HOME = "/home/cnorris" }, 22 | cwd = "/tmp", 23 | stdin = luv.stdout, 24 | stdout = luv.stdin, 25 | stderr = luv.stderr, 26 | detach = true, 27 | }) 28 | */ 29 | 30 | static int luv_new_process(lua_State* L) { 31 | const char* cmd = luaL_checkstring(L, 1); 32 | size_t argc; 33 | char** args; 34 | int i; 35 | char* cwd; 36 | char** env; 37 | uv_process_options_t opts; 38 | int rv; 39 | 40 | luaL_checktype(L, 2, LUA_TTABLE); /* options */ 41 | memset(&opts, 0, sizeof(uv_process_options_t)); 42 | 43 | argc = lua_objlen(L, 2); 44 | args = (char**)malloc((argc + 1) * sizeof(char*)); 45 | args[0] = (char*)cmd; 46 | for (i = 1; i <= argc; i++) { 47 | lua_rawgeti(L, -1, i); 48 | args[i] = (char*)lua_tostring(L, -1); 49 | lua_pop(L, 1); 50 | } 51 | 52 | args[argc + 1] = NULL; 53 | 54 | cwd = NULL; 55 | lua_getfield(L, 2, "cwd"); 56 | if (!lua_isnil(L, -1)) { 57 | cwd = (char*)lua_tostring(L, -1); 58 | } 59 | lua_pop(L, 1); 60 | 61 | env = NULL; 62 | lua_getfield(L, 2, "env"); 63 | if (!lua_isnil(L, -1)) { 64 | int i, len; 65 | const char* key; 66 | const char* val; 67 | len = 32; 68 | env = (char**)malloc(32 * sizeof(char*)); 69 | 70 | lua_pushnil(L); 71 | i = 0; 72 | while (lua_next(L, -2) != 0) { 73 | if (i >= len) { 74 | len *= 2; 75 | env = (char**)realloc(env, len * sizeof(char*)); 76 | } 77 | key = lua_tostring(L, -2); 78 | val = lua_tostring(L, -1); 79 | lua_pushfstring(L, "%s=%s", key, val); 80 | env[i++] = (char*)lua_tostring(L, -1); 81 | lua_pop(L, 2); 82 | } 83 | 84 | env[i] = NULL; 85 | } 86 | lua_pop(L, 1); 87 | 88 | opts.exit_cb = _exit_cb; 89 | opts.file = cmd; 90 | opts.args = args; 91 | opts.env = env; 92 | opts.cwd = cwd; 93 | 94 | uv_stdio_container_t stdio[3]; 95 | opts.stdio_count = 3; 96 | 97 | const char* stdfh_names[] = { "stdin", "stdout", "stderr" }; 98 | for (i = 0; i < 3; i++) { 99 | lua_getfield(L, 2, stdfh_names[i]); 100 | if (lua_isnil(L, -1)) { 101 | stdio[i].flags = UV_IGNORE; 102 | } 103 | else { 104 | luv_object_t* obj = (luv_object_t*)luaL_checkudata(L, -1, LUV_PIPE_T); 105 | stdio[i].flags = UV_INHERIT_STREAM; 106 | stdio[i].data.stream = &obj->h.stream; 107 | } 108 | lua_pop(L, 1); 109 | } 110 | 111 | opts.stdio = stdio; 112 | 113 | lua_getfield(L, 2, "detach"); 114 | if (lua_toboolean(L, -1)) { 115 | opts.flags |= UV_PROCESS_DETACHED; 116 | } 117 | else { 118 | opts.flags = 0; 119 | } 120 | lua_pop(L, 1); 121 | 122 | luv_state_t* curr = luvL_state_self(L); 123 | 124 | luv_object_t* self = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 125 | luaL_getmetatable(L, LUV_PROCESS_T); 126 | lua_setmetatable(L, -2); 127 | 128 | luvL_object_init(curr, self); 129 | 130 | lua_insert(L, 1); 131 | lua_settop(L, 1); 132 | 133 | rv = uv_spawn(luvL_event_loop(L), &self->h.process, opts); 134 | 135 | free(args); 136 | if (env) free(env); 137 | if (rv) { 138 | uv_err_t err = uv_last_error(luvL_event_loop(L)); 139 | return luaL_error(L, uv_strerror(err)); 140 | } 141 | 142 | if (opts.flags & UV_PROCESS_DETACHED) { 143 | uv_unref((uv_handle_t*)&self->h.process); 144 | return 1; 145 | } 146 | else { 147 | return luvL_cond_wait(&self->rouse, curr); 148 | } 149 | } 150 | 151 | static int luv_process_kill(lua_State* L) { 152 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_PROCESS_T); 153 | int signum = luaL_checkint(L, 2); 154 | 155 | if (uv_process_kill(&self->h.process, signum)) { 156 | uv_err_t err = uv_last_error(luvL_event_loop(L)); 157 | return luaL_error(L, uv_strerror(err)); 158 | } 159 | 160 | return 0; 161 | } 162 | 163 | static int luv_process_free(lua_State* L) { 164 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 165 | luvL_object_close(self); 166 | return 0; 167 | } 168 | static int luv_process_tostring(lua_State* L) { 169 | luv_object_t *self = (luv_object_t*)luaL_checkudata(L, 1, LUV_PROCESS_T); 170 | lua_pushfstring(L, "userdata<%s>: %p", LUV_PROCESS_T, self); 171 | return 1; 172 | } 173 | 174 | luaL_Reg luv_process_funcs[] = { 175 | {"spawn", luv_new_process}, 176 | {NULL, NULL} 177 | }; 178 | 179 | luaL_Reg luv_process_meths[] = { 180 | {"kill", luv_process_kill}, 181 | {"__gc", luv_process_free}, 182 | {"__tostring", luv_process_tostring}, 183 | {NULL, NULL} 184 | }; 185 | -------------------------------------------------------------------------------- /src/luv_state.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | int luvL_state_is_thread(luv_state_t* state) { 4 | return state->type == LUV_TTHREAD; 5 | } 6 | int luvL_state_in_thread(luv_state_t* state) { 7 | return state->type == LUV_TTHREAD; 8 | } 9 | 10 | luv_state_t* luvL_state_self(lua_State* L) { 11 | lua_pushthread(L); 12 | lua_rawget(L, LUA_REGISTRYINDEX); 13 | luv_state_t* self = (luv_state_t*)lua_touserdata(L, -1); 14 | lua_pop(L, 1); 15 | return self; 16 | } 17 | 18 | int luvL_state_is_active(luv_state_t* state) { 19 | return state == luvL_thread_self(state->L)->curr; 20 | } 21 | 22 | /* resume at the next iteration of the loop */ 23 | void luvL_state_ready(luv_state_t* state) { 24 | if (state->type == LUV_TTHREAD) { 25 | luvL_thread_ready((luv_thread_t*)state); 26 | } 27 | else { 28 | luvL_fiber_ready((luv_fiber_t*)state); 29 | } 30 | } 31 | 32 | /* yield a timeslice and allow passing values back to caller of resume */ 33 | int luvL_state_yield(luv_state_t* state, int narg) { 34 | assert(luvL_state_is_active(state)); 35 | if (state->type == LUV_TTHREAD) { 36 | return luvL_thread_yield((luv_thread_t*)state, narg); 37 | } 38 | else { 39 | return luvL_fiber_yield((luv_fiber_t*)state, narg); 40 | } 41 | } 42 | 43 | /* suspend execution of the current state until something wakes us up. */ 44 | int luvL_state_suspend(luv_state_t* state) { 45 | if (state->type == LUV_TTHREAD) { 46 | return luvL_thread_suspend((luv_thread_t*)state); 47 | } 48 | else { 49 | return luvL_fiber_suspend((luv_fiber_t*)state); 50 | } 51 | } 52 | 53 | /* transfer control directly to another state */ 54 | int luvL_state_resume(luv_state_t* state, int narg) { 55 | if (state->type == LUV_TTHREAD) { 56 | return luvL_thread_resume((luv_thread_t*)state, narg); 57 | } 58 | else { 59 | return luvL_fiber_resume((luv_fiber_t*)state, narg); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/luv_stream.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | /* used by udp and stream */ 4 | uv_buf_t luvL_alloc_cb(uv_handle_t* handle, size_t size) { 5 | luv_object_t* self = container_of(handle, luv_object_t, h); 6 | size = (size_t)self->buf.len; 7 | return uv_buf_init((char*)malloc(size), size); 8 | } 9 | 10 | /* used by tcp and pipe */ 11 | void luvL_connect_cb(uv_connect_t* req, int status) { 12 | luv_state_t* state = container_of(req, luv_state_t, req); 13 | luvL_state_ready(state); 14 | } 15 | 16 | static void _read_cb(uv_stream_t* stream, ssize_t len, uv_buf_t buf) { 17 | TRACE("got data\n"); 18 | luv_object_t* self = container_of(stream, luv_object_t, h); 19 | 20 | if (ngx_queue_empty(&self->rouse)) { 21 | TRACE("empty read queue, save buffer and stop read\n"); 22 | luvL_stream_stop(self); 23 | if (len >= 0) { 24 | self->buf = buf; 25 | self->count = len; 26 | } 27 | else { 28 | if (buf.base) { 29 | free(buf.base); 30 | buf.base = NULL; 31 | buf.len = 0; 32 | } 33 | } 34 | } 35 | else { 36 | TRACE("have states waiting...\n"); 37 | ngx_queue_t* q; 38 | luv_state_t* s; 39 | 40 | q = ngx_queue_head(&self->rouse); 41 | s = ngx_queue_data(q, luv_state_t, cond); 42 | ngx_queue_remove(q); 43 | 44 | TRACE("data - len: %i\n", (int)len); 45 | 46 | lua_settop(s->L, 0); 47 | if (len >= 0) { 48 | lua_pushinteger(s->L, len); 49 | lua_pushlstring(s->L, (char*)buf.base, len); 50 | if (len == 0) luvL_stream_stop(self); 51 | } 52 | else { 53 | uv_err_t err = uv_last_error(s->loop); 54 | if (err.code == UV_EOF) { 55 | TRACE("GOT EOF\n"); 56 | lua_settop(s->L, 0); 57 | lua_pushnil(s->L); 58 | } 59 | else { 60 | lua_settop(s->L, 0); 61 | lua_pushboolean(s->L, 0); 62 | lua_pushfstring(s->L, "read: %s", uv_strerror(err)); 63 | TRACE("READ ERROR, CLOSING STREAM\n"); 64 | luvL_stream_stop(self); 65 | luvL_object_close(self); 66 | } 67 | } 68 | if (buf.base) { 69 | free(buf.base); 70 | buf.len = 0; 71 | buf.base = NULL; 72 | } 73 | TRACE("wake up state: %p\n", s); 74 | luvL_state_ready(s); 75 | } 76 | } 77 | 78 | static void _write_cb(uv_write_t* req, int status) { 79 | luv_state_t* rouse = container_of(req, luv_state_t, req); 80 | lua_settop(rouse->L, 0); 81 | lua_pushinteger(rouse->L, status); 82 | TRACE("write_cb - wake up: %p\n", rouse); 83 | luvL_state_ready(rouse); 84 | } 85 | 86 | static void _shutdown_cb(uv_shutdown_t* req, int status) { 87 | luv_object_t* self = container_of(req->handle, luv_object_t, h); 88 | luv_state_t* state = container_of(req, luv_state_t, req); 89 | lua_settop(state->L, 0); 90 | lua_pushinteger(state->L, status); 91 | luvL_cond_signal(&self->rouse); 92 | } 93 | 94 | static void _listen_cb(uv_stream_t* server, int status) { 95 | TRACE("got client connection...\n"); 96 | luv_object_t* self = container_of(server, luv_object_t, h); 97 | if (luvL_object_is_waiting(self)) { 98 | ngx_queue_t* q = ngx_queue_head(&self->rouse); 99 | luv_state_t* s = ngx_queue_data(q, luv_state_t, cond); 100 | lua_State* L = s->L; 101 | 102 | TRACE("is waiting..., lua_State*: %p\n", L); 103 | luaL_checktype(L, 2, LUA_TUSERDATA); 104 | luv_object_t* conn = (luv_object_t*)lua_touserdata(L, 2); 105 | TRACE("got client conn: %p\n", conn); 106 | luvL_object_init(s, conn); 107 | int rv = uv_accept(&self->h.stream, &conn->h.stream); 108 | TRACE("accept returned ok\n"); 109 | if (rv) { 110 | uv_err_t err = uv_last_error(self->h.stream.loop); 111 | TRACE("ERROR: %s\n", uv_strerror(err)); 112 | lua_settop(L, 0); 113 | lua_pushnil(L); 114 | lua_pushstring(L, uv_strerror(err)); 115 | } 116 | self->flags &= ~LUV_OWAITING; 117 | luvL_cond_signal(&self->rouse); 118 | } 119 | else { 120 | TRACE("increment backlog count\n"); 121 | self->count++; 122 | } 123 | } 124 | 125 | static int luv_stream_listen(lua_State* L) { 126 | luaL_checktype(L, 1, LUA_TUSERDATA); 127 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 128 | int backlog = luaL_optinteger(L, 2, 128); 129 | if (uv_listen(&self->h.stream, backlog, _listen_cb)) { 130 | uv_err_t err = uv_last_error(self->h.stream.loop); 131 | TRACE("listen error\n"); 132 | return luaL_error(L, "listen: %s", uv_strerror(err)); 133 | } 134 | return 0; 135 | } 136 | 137 | static int luv_stream_accept(lua_State *L) { 138 | luaL_checktype(L, 1, LUA_TUSERDATA); 139 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 140 | luv_object_t* conn = (luv_object_t*)lua_touserdata(L, 2); 141 | 142 | luv_state_t* curr = luvL_state_self(L); 143 | 144 | if (self->count) { 145 | self->count--; 146 | int rv = uv_accept(&self->h.stream, &conn->h.stream); 147 | if (rv) { 148 | uv_err_t err = uv_last_error(self->h.stream.loop); 149 | lua_settop(L, 0); 150 | lua_pushnil(L); 151 | lua_pushstring(L, uv_strerror(err)); 152 | return 2; 153 | } 154 | return 1; 155 | } 156 | self->flags |= LUV_OWAITING; 157 | return luvL_cond_wait(&self->rouse, curr); 158 | } 159 | 160 | int luvL_stream_start(luv_object_t* self) { 161 | if (!luvL_object_is_started(self)) { 162 | self->flags |= LUV_OSTARTED; 163 | return uv_read_start(&self->h.stream, luvL_alloc_cb, _read_cb); 164 | } 165 | return 0; 166 | } 167 | int luvL_stream_stop(luv_object_t* self) { 168 | if (luvL_object_is_started(self)) { 169 | self->flags &= ~LUV_OSTARTED; 170 | return uv_read_stop(&self->h.stream); 171 | } 172 | return 0; 173 | } 174 | 175 | 176 | #define STREAM_ERROR(L,fmt,loop) do { \ 177 | uv_err_t err = uv_last_error(loop); \ 178 | lua_settop(L, 0); \ 179 | lua_pushboolean(L, 0); \ 180 | lua_pushfstring(L, fmt, uv_strerror(err)); \ 181 | TRACE("STREAM ERROR: %s\n", lua_tostring(L, -1)); \ 182 | } while (0) 183 | 184 | static int luv_stream_start(lua_State* L) { 185 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 186 | if (luvL_stream_start(self)) { 187 | luvL_stream_stop(self); 188 | luvL_object_close(self); 189 | STREAM_ERROR(L, "read start: %s", luvL_event_loop(L)); 190 | return 2; 191 | } 192 | lua_pushboolean(L, 1); 193 | return 1; 194 | } 195 | 196 | static int luv_stream_stop(lua_State* L) { 197 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 198 | if (luvL_stream_stop(self)) { 199 | STREAM_ERROR(L, "read stop: %s", luvL_event_loop(L)); 200 | return 2; 201 | } 202 | lua_pushboolean(L, 1); 203 | return 1; 204 | } 205 | 206 | static int luv_stream_read(lua_State* L) { 207 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 208 | luv_state_t* curr = luvL_state_self(L); 209 | int len = luaL_optinteger(L, 2, 4096); 210 | if (luvL_object_is_closing(self)) { 211 | TRACE("error: reading from closed stream\n"); 212 | lua_pushnil(L); 213 | lua_pushstring(L, "attempt to read from a closed stream"); 214 | return 2; 215 | } 216 | if (self->buf.base) { 217 | /* we have a buffer use that */ 218 | TRACE("have pending data\n"); 219 | lua_pushinteger(L, self->count); 220 | lua_pushlstring(L, (char*)self->buf.base, self->count); 221 | free(self->buf.base); 222 | self->buf.base = NULL; 223 | self->buf.len = 0; 224 | self->count = 0; 225 | return 2; 226 | } 227 | self->buf.len = len; 228 | if (!luvL_object_is_started(self)) { 229 | luvL_stream_start(self); 230 | } 231 | TRACE("read called... waiting\n"); 232 | return luvL_cond_wait(&self->rouse, curr); 233 | } 234 | 235 | static int luv_stream_write(lua_State* L) { 236 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 237 | 238 | size_t len; 239 | const char* chunk = luaL_checklstring(L, 2, &len); 240 | 241 | uv_buf_t buf = uv_buf_init((char*)chunk, len); 242 | 243 | luv_state_t* curr = luvL_state_self(L); 244 | uv_write_t* req = &curr->req.write; 245 | 246 | if (uv_write(req, &self->h.stream, &buf, 1, _write_cb)) { 247 | luvL_stream_stop(self); 248 | luvL_object_close(self); 249 | STREAM_ERROR(L, "write: %s", luvL_event_loop(L)); 250 | return 2; 251 | } 252 | 253 | lua_settop(curr->L, 1); 254 | return luvL_state_suspend(curr); 255 | } 256 | 257 | static int luv_stream_shutdown(lua_State* L) { 258 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 259 | if (!luvL_object_is_shutdown(self)) { 260 | self->flags |= LUV_OSHUTDOWN; 261 | luv_state_t* curr = luvL_state_self(L); 262 | uv_shutdown(&curr->req.shutdown, &self->h.stream, _shutdown_cb); 263 | return luvL_cond_wait(&self->rouse, curr); 264 | } 265 | return 1; 266 | } 267 | static int luv_stream_readable(lua_State* L) { 268 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 269 | lua_pushboolean(L, uv_is_readable(&self->h.stream)); 270 | return 1; 271 | } 272 | 273 | static int luv_stream_writable(lua_State* L) { 274 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 275 | lua_pushboolean(L, uv_is_writable(&self->h.stream)); 276 | return 1; 277 | } 278 | 279 | void luvL_stream_close(luv_object_t* self) { 280 | TRACE("close stream\n"); 281 | if (luvL_object_is_started(self)) { 282 | luvL_stream_stop(self); 283 | } 284 | luvL_object_close(self); 285 | if (self->buf.base) { 286 | free(self->buf.base); 287 | self->buf.base = NULL; 288 | self->buf.len = 0; 289 | } 290 | } 291 | 292 | static int luv_stream_close(lua_State* L) { 293 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 294 | luvL_stream_close(self); 295 | return luvL_cond_wait(&self->rouse, luvL_state_self(L)); 296 | } 297 | 298 | void luvL_stream_free(luv_object_t* self) { 299 | luvL_object_close(self); 300 | TRACE("free stream: %p\n", self); 301 | if (self->buf.base) { 302 | free(self->buf.base); 303 | self->buf.base = NULL; 304 | self->buf.len = 0; 305 | } 306 | } 307 | 308 | static int luv_stream_free(lua_State* L) { 309 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 310 | luvL_stream_free(self); 311 | return 1; 312 | } 313 | 314 | static int luv_stream_tostring(lua_State* L) { 315 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 316 | lua_pushfstring(L, "userdata: %p", self); 317 | return 1; 318 | } 319 | 320 | luaL_Reg luv_stream_meths[] = { 321 | {"read", luv_stream_read}, 322 | {"readable", luv_stream_readable}, 323 | {"write", luv_stream_write}, 324 | {"writable", luv_stream_writable}, 325 | {"start", luv_stream_start}, 326 | {"stop", luv_stream_stop}, 327 | {"listen", luv_stream_listen}, 328 | {"accept", luv_stream_accept}, 329 | {"shutdown", luv_stream_shutdown}, 330 | {"close", luv_stream_close}, 331 | {"__gc", luv_stream_free}, 332 | {"__tostring",luv_stream_tostring}, 333 | {NULL, NULL} 334 | }; 335 | 336 | 337 | -------------------------------------------------------------------------------- /src/luv_thread.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | void luvL_thread_ready(luv_thread_t* self) { 4 | if (!(self->flags & LUV_FREADY)) { 5 | TRACE("SET READY\n"); 6 | self->flags |= LUV_FREADY; 7 | uv_async_send(&self->async); 8 | } 9 | } 10 | 11 | int luvL_thread_yield(luv_thread_t* self, int narg) { 12 | TRACE("calling uv_run_once\n"); 13 | uv_run_once(self->loop); 14 | TRACE("done\n"); 15 | if (narg == LUA_MULTRET) { 16 | narg = lua_gettop(self->L); 17 | } 18 | return narg; 19 | } 20 | 21 | int luvL_thread_suspend(luv_thread_t* self) { 22 | if (self->flags & LUV_FREADY) { 23 | self->flags &= ~LUV_FREADY; 24 | int active = 0; 25 | do { 26 | TRACE("loop top\n"); 27 | luvL_thread_loop(self); 28 | active = uv_run_once(self->loop); 29 | TRACE("uv_run_once returned, active: %i\n", active); 30 | if (self->flags & LUV_FREADY) { 31 | TRACE("main ready, breaking\n"); 32 | break; 33 | } 34 | } 35 | while (active); 36 | TRACE("back in main\n"); 37 | /* nothing left to do, back in main */ 38 | self->flags |= LUV_FREADY; 39 | } 40 | return lua_gettop(self->L); 41 | } 42 | 43 | int luvL_thread_resume(luv_thread_t* self, int narg) { 44 | /* interrupt the uv_run_once loop in luvL_thread_schedule */ 45 | TRACE("resuming...\n"); 46 | luvL_thread_ready(self); 47 | if (narg) { 48 | /* pass arguments from current state to self */ 49 | luv_state_t* curr = self->curr; 50 | if (narg == LUA_MULTRET) { 51 | narg = lua_gettop(curr->L); 52 | } 53 | /* but only if it is not to ourselves */ 54 | if (curr != (luv_state_t*)self) { 55 | assert(lua_gettop(curr->L) >= narg); 56 | lua_checkstack(self->L, narg); 57 | lua_xmove(curr->L, self->L, narg); 58 | } 59 | } 60 | else { 61 | /* lua_settop(self->L, 0) ??? */ 62 | } 63 | return narg; 64 | } 65 | void luvL_thread_enqueue(luv_thread_t* self, luv_fiber_t* fiber) { 66 | int need_async = ngx_queue_empty(&self->rouse); 67 | ngx_queue_insert_tail(&self->rouse, &fiber->queue); 68 | if (need_async) { 69 | TRACE("need async\n"); 70 | /* interrupt the event loop (the sequence of these two calls matters) */ 71 | uv_async_send(&self->async); 72 | /* make sure we loop at least once more */ 73 | uv_ref((uv_handle_t*)&self->async); 74 | } 75 | } 76 | luv_thread_t* luvL_thread_self(lua_State* L) { 77 | luv_state_t* self = luvL_state_self(L); 78 | if (self->type == LUV_TTHREAD) { 79 | return (luv_thread_t*)self; 80 | } 81 | else { 82 | while (self->type != LUV_TTHREAD) self = self->outer; 83 | return (luv_thread_t*)self; 84 | } 85 | } 86 | 87 | int luvL_thread_once(luv_thread_t* self) { 88 | if (!ngx_queue_empty(&self->rouse)) { 89 | ngx_queue_t* q; 90 | luv_fiber_t* fiber; 91 | q = ngx_queue_head(&self->rouse); 92 | fiber = ngx_queue_data(q, luv_fiber_t, queue); 93 | ngx_queue_remove(q); 94 | TRACE("[%p] rouse fiber: %p\n", self, fiber); 95 | if (fiber->flags & LUV_FDEAD) { 96 | TRACE("[%p] fiber is dead: %p\n", self, fiber); 97 | luaL_error(self->L, "cannot resume a dead fiber"); 98 | } 99 | else { 100 | int stat, narg; 101 | narg = lua_gettop(fiber->L); 102 | 103 | if (!(fiber->flags & LUV_FSTART)) { 104 | /* first entry, ignore function arg */ 105 | fiber->flags |= LUV_FSTART; 106 | --narg; 107 | } 108 | 109 | self->curr = (luv_state_t*)fiber; 110 | TRACE("[%p] calling lua_resume on: %p\n", self, fiber); 111 | stat = lua_resume(fiber->L, narg); 112 | TRACE("resume returned\n"); 113 | self->curr = (luv_state_t*)self; 114 | 115 | switch (stat) { 116 | case LUA_YIELD: 117 | TRACE("[%p] seen LUA_YIELD\n", self); 118 | /* if called via coroutine.yield() then we're still in the queue */ 119 | if (fiber->flags & LUV_FREADY) { 120 | TRACE("%p is still ready, back in the queue\n", fiber); 121 | ngx_queue_insert_tail(&self->rouse, &fiber->queue); 122 | } 123 | break; 124 | case 0: { 125 | /* normal exit, wake up joining states */ 126 | int i, narg; 127 | narg = lua_gettop(fiber->L); 128 | TRACE("[%p] normal exit - fiber: %p, narg: %i\n", self, fiber, narg); 129 | ngx_queue_t* q; 130 | luv_state_t* s; 131 | while (!ngx_queue_empty(&fiber->rouse)) { 132 | q = ngx_queue_head(&fiber->rouse); 133 | s = ngx_queue_data(q, luv_state_t, join); 134 | ngx_queue_remove(q); 135 | TRACE("calling luvL_state_ready(%p)\n", s); 136 | luvL_state_ready(s); 137 | if (s->type == LUV_TFIBER) { 138 | lua_checkstack(fiber->L, 1); 139 | lua_checkstack(s->L, narg); 140 | for (i = 1; i <= narg; i++) { 141 | lua_pushvalue(fiber->L, i); 142 | lua_xmove(fiber->L, s->L, 1); 143 | } 144 | } 145 | } 146 | TRACE("closing fiber %p\n", fiber); 147 | luvL_fiber_close(fiber); 148 | break; 149 | } 150 | default: 151 | TRACE("ERROR: in fiber\n"); 152 | lua_pushvalue(fiber->L, -1); /* error message */ 153 | lua_xmove(fiber->L, self->L, 1); 154 | luvL_fiber_close(fiber); 155 | lua_error(self->L); 156 | } 157 | } 158 | } 159 | return !ngx_queue_empty(&self->rouse); 160 | } 161 | int luvL_thread_loop(luv_thread_t* self) { 162 | while (luvL_thread_once(self)); 163 | return 0; 164 | } 165 | 166 | static void _async_cb(uv_async_t* handle, int status) { 167 | TRACE("interrupt loop\n"); 168 | (void)handle; 169 | (void)status; 170 | } 171 | 172 | void luvL_thread_init_main(lua_State* L) { 173 | luv_thread_t* self = (luv_thread_t*)lua_newuserdata(L, sizeof(luv_thread_t)); 174 | luaL_getmetatable(L, LUV_THREAD_T); 175 | lua_setmetatable(L, -2); 176 | 177 | self->type = LUV_TTHREAD; 178 | self->flags = LUV_FREADY; 179 | self->loop = uv_default_loop(); 180 | self->curr = (luv_state_t*)self; 181 | self->L = L; 182 | self->outer = (luv_state_t*)self; 183 | self->data = NULL; 184 | self->tid = (uv_thread_t)uv_thread_self(); 185 | 186 | ngx_queue_init(&self->rouse); 187 | 188 | uv_async_init(self->loop, &self->async, _async_cb); 189 | uv_unref((uv_handle_t*)&self->async); 190 | 191 | lua_pushthread(L); 192 | lua_pushvalue(L, -2); 193 | lua_rawset(L, LUA_REGISTRYINDEX); 194 | } 195 | 196 | static void _thread_enter(void* arg) { 197 | luv_thread_t* self = (luv_thread_t*)arg; 198 | 199 | luvL_codec_decode(self->L); 200 | lua_remove(self->L, 1); 201 | 202 | luaL_checktype(self->L, 1, LUA_TFUNCTION); 203 | lua_pushcfunction(self->L, luvL_traceback); 204 | lua_insert(self->L, 1); 205 | int nargs = lua_gettop(self->L) - 2; 206 | 207 | int rv = lua_pcall(self->L, nargs, LUA_MULTRET, 1); 208 | lua_remove(self->L, 1); /* traceback */ 209 | 210 | if (rv) { /* error */ 211 | lua_pushboolean(self->L, 0); 212 | lua_insert(self->L, 1); 213 | luvL_thread_ready(self); 214 | luaL_error(self->L, lua_tostring(self->L, -1)); 215 | } 216 | else { 217 | lua_pushboolean(self->L, 1); 218 | lua_insert(self->L, 1); 219 | } 220 | 221 | self->flags |= LUV_FDEAD; 222 | } 223 | 224 | luv_thread_t* luvL_thread_create(luv_state_t* outer, int narg) { 225 | lua_State* L = outer->L; 226 | int base; 227 | 228 | /* ..., func, arg1, ..., argN */ 229 | base = lua_gettop(L) - narg + 1; 230 | 231 | luv_thread_t* self = (luv_thread_t*)lua_newuserdata(L, sizeof(luv_thread_t)); 232 | luaL_getmetatable(L, LUV_THREAD_T); 233 | lua_setmetatable(L, -2); 234 | lua_insert(L, base++); 235 | 236 | self->type = LUV_TTHREAD; 237 | self->flags = LUV_FREADY; 238 | self->loop = uv_loop_new(); 239 | self->curr = (luv_state_t*)self; 240 | self->L = luaL_newstate(); 241 | self->outer = outer; 242 | self->data = NULL; 243 | 244 | ngx_queue_init(&self->rouse); 245 | 246 | uv_async_init(self->loop, &self->async, _async_cb); 247 | uv_unref((uv_handle_t*)&self->async); 248 | 249 | luaL_openlibs(self->L); 250 | luaopen_luv(self->L); 251 | 252 | lua_settop(self->L, 0); 253 | luvL_codec_encode(L, narg); 254 | luaL_checktype(L, -1, LUA_TSTRING); 255 | lua_xmove(L, self->L, 1); 256 | 257 | /* keep a reference for reverse lookup in child */ 258 | lua_pushthread(self->L); 259 | lua_pushlightuserdata(self->L, (void*)self); 260 | lua_rawset(self->L, LUA_REGISTRYINDEX); 261 | 262 | uv_thread_create(&self->tid, _thread_enter, self); 263 | 264 | /* inserted udata below function, so now just udata on top */ 265 | TRACE("HERE TOP: %i, base: %i\n", lua_gettop(L), base); 266 | lua_settop(L, base - 1); 267 | return self; 268 | } 269 | 270 | /* Lua API */ 271 | static int luv_new_thread(lua_State* L) { 272 | luv_state_t* outer = luvL_state_self(L); 273 | int narg = lua_gettop(L); 274 | luvL_thread_create(outer, narg); 275 | return 1; 276 | } 277 | static int luv_thread_join(lua_State* L) { 278 | luv_thread_t* self = (luv_thread_t*)luaL_checkudata(L, 1, LUV_THREAD_T); 279 | luv_thread_t* curr = luvL_thread_self(L); 280 | 281 | luvL_thread_ready(self); 282 | luvL_thread_suspend(curr); 283 | uv_thread_join(&self->tid); /* XXX: use async instead, this blocks hard */ 284 | 285 | lua_settop(L, 0); 286 | 287 | int nret = lua_gettop(self->L); 288 | luvL_codec_encode(self->L, nret); 289 | lua_xmove(self->L, L, 1); 290 | luvL_codec_decode(L); 291 | 292 | return nret; 293 | } 294 | static int luv_thread_free(lua_State* L) { 295 | luv_thread_t* self = lua_touserdata(L, 1); 296 | TRACE("free thread\n"); 297 | uv_loop_delete(self->loop); 298 | TRACE("ok\n"); 299 | return 1; 300 | } 301 | static int luv_thread_tostring(lua_State* L) { 302 | luv_thread_t* self = (luv_thread_t*)luaL_checkudata(L, 1, LUV_THREAD_T); 303 | lua_pushfstring(L, "userdata<%s>: %p", LUV_THREAD_T, self); 304 | return 1; 305 | } 306 | 307 | luaL_Reg luv_thread_funcs[] = { 308 | {"spawn", luv_new_thread}, 309 | {NULL, NULL} 310 | }; 311 | 312 | luaL_Reg luv_thread_meths[] = { 313 | {"join", luv_thread_join}, 314 | {"__gc", luv_thread_free}, 315 | {"__tostring",luv_thread_tostring}, 316 | {NULL, NULL} 317 | }; 318 | 319 | -------------------------------------------------------------------------------- /src/luv_timer.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | static void _timer_cb(uv_timer_t* handle, int status) { 4 | luv_object_t* self = container_of(handle, luv_object_t, h); 5 | ngx_queue_t* q; 6 | luv_state_t* s; 7 | ngx_queue_foreach(q, &self->rouse) { 8 | s = ngx_queue_data(q, luv_state_t, cond); 9 | TRACE("rouse %p\n", s); 10 | lua_settop(s->L, 0); 11 | lua_pushinteger(s->L, status); 12 | } 13 | luvL_cond_broadcast(&self->rouse); 14 | } 15 | 16 | static int luv_new_timer(lua_State* L) { 17 | luv_object_t* self = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 18 | luaL_getmetatable(L, LUV_TIMER_T); 19 | lua_setmetatable(L, -2); 20 | 21 | luv_state_t* curr = luvL_state_self(L); 22 | uv_timer_init(luvL_event_loop(L), &self->h.timer); 23 | luvL_object_init(curr, self); 24 | 25 | return 1; 26 | } 27 | 28 | /* methods */ 29 | static int luv_timer_start(lua_State* L) { 30 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_TIMER_T); 31 | int64_t timeout = luaL_optlong(L, 2, 0L); 32 | int64_t repeat = luaL_optlong(L, 3, 0L); 33 | int rv = uv_timer_start(&self->h.timer, _timer_cb, timeout, repeat); 34 | lua_pushinteger(L, rv); 35 | return 1; 36 | } 37 | 38 | static int luv_timer_again(lua_State* L) { 39 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_TIMER_T); 40 | lua_pushinteger(L, uv_timer_again(&self->h.timer)); 41 | return 1; 42 | } 43 | 44 | static int luv_timer_stop(lua_State* L) { 45 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_TIMER_T); 46 | lua_pushinteger(L, uv_timer_stop(&self->h.timer)); 47 | return 1; 48 | } 49 | 50 | static int luv_timer_wait(lua_State *L) { 51 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_TIMER_T); 52 | luv_state_t* state = luvL_state_self(L); 53 | return luvL_cond_wait(&self->rouse, state); 54 | } 55 | 56 | static int luv_timer_free(lua_State *L) { 57 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 58 | luvL_object_close(self); 59 | return 1; 60 | } 61 | static int luv_timer_tostring(lua_State *L) { 62 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_TIMER_T); 63 | lua_pushfstring(L, "userdata<%s>: %p", LUV_TIMER_T, self); 64 | return 1; 65 | } 66 | 67 | luaL_Reg luv_timer_funcs[] = { 68 | {"create", luv_new_timer}, 69 | {NULL, NULL} 70 | }; 71 | 72 | luaL_Reg luv_timer_meths[] = { 73 | {"start", luv_timer_start}, 74 | {"again", luv_timer_again}, 75 | {"stop", luv_timer_stop}, 76 | {"wait", luv_timer_wait}, 77 | {"__gc", luv_timer_free}, 78 | {"__tostring",luv_timer_tostring}, 79 | {NULL, NULL} 80 | }; 81 | 82 | -------------------------------------------------------------------------------- /src/luv_zmq.c: -------------------------------------------------------------------------------- 1 | #include "luv.h" 2 | 3 | int luvL_zmq_socket_readable(void* socket) { 4 | zmq_pollitem_t items[1]; 5 | items[0].socket = socket; 6 | items[0].events = ZMQ_POLLIN; 7 | return zmq_poll(items, 1, 0); 8 | } 9 | int luvL_zmq_socket_writable(void* socket) { 10 | zmq_pollitem_t items[1]; 11 | items[0].socket = socket; 12 | items[0].events = ZMQ_POLLOUT; 13 | return zmq_poll(items, 1, 0); 14 | } 15 | 16 | int luvL_zmq_socket_send(luv_object_t* self, luv_state_t* state) { 17 | size_t len; 18 | zmq_msg_t msg; 19 | 20 | const char* data = luaL_checklstring(state->L, 2, &len); 21 | if (zmq_msg_init_size(&msg, len)) { 22 | /* ENOMEM */ 23 | return luaL_error(state->L, strerror(errno)); 24 | } 25 | 26 | memcpy(zmq_msg_data(&msg), data, len); 27 | int rv = zmq_msg_send(&msg, self->data, ZMQ_DONTWAIT); 28 | zmq_msg_close(&msg); 29 | 30 | return rv; 31 | } 32 | 33 | int luvL_zmq_socket_recv(luv_object_t* self, luv_state_t* state) { 34 | zmq_msg_t msg; 35 | zmq_msg_init(&msg); 36 | 37 | int rv = zmq_msg_recv(&msg, self->data, ZMQ_DONTWAIT); 38 | if (rv < 0) { 39 | zmq_msg_close(&msg); 40 | } 41 | else { 42 | void* data = zmq_msg_data(&msg); 43 | size_t len = zmq_msg_size(&msg); 44 | lua_settop(state->L, 0); 45 | lua_pushlstring(state->L, (const char*)data, len); 46 | zmq_msg_close(&msg); 47 | } 48 | return rv; 49 | } 50 | 51 | static void _zmq_poll_cb(uv_poll_t* handle, int status, int events) { 52 | luv_object_t* self = container_of(handle, luv_object_t, h); 53 | 54 | if (self->flags & LUV_ZMQ_WRECV) { 55 | int readable = luvL_zmq_socket_readable(self->data); 56 | if (!readable) goto wsend; 57 | 58 | self->flags &= ~LUV_ZMQ_WRECV; 59 | 60 | ngx_queue_t* queue = ngx_queue_head(&self->rouse); 61 | luv_state_t* state = ngx_queue_data(queue, luv_state_t, cond); 62 | ngx_queue_remove(queue); 63 | 64 | if (readable < 0) { 65 | lua_settop(state->L, 0); 66 | lua_pushboolean(state->L, 0); 67 | lua_pushstring(state->L, strerror(errno)); 68 | } 69 | else if (readable > 0) { 70 | int rv = luvL_zmq_socket_recv(self, state); 71 | if (rv < 0) { 72 | if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { 73 | lua_settop(state->L, 0); 74 | lua_pushboolean(state->L, 0); 75 | lua_pushstring(state->L, strerror(errno)); 76 | } 77 | } 78 | } 79 | luvL_state_ready(state); 80 | return; 81 | } 82 | 83 | wsend: 84 | if (self->flags & LUV_ZMQ_WSEND) { 85 | int writable = luvL_zmq_socket_writable(self->data); 86 | if (!writable) return; 87 | 88 | self->flags &= ~LUV_ZMQ_WSEND; 89 | 90 | ngx_queue_t* queue = ngx_queue_head(&self->queue); 91 | luv_state_t* state = ngx_queue_data(queue, luv_state_t, cond); 92 | ngx_queue_remove(queue); 93 | 94 | if (writable < 0) { 95 | lua_settop(state->L, 0); 96 | lua_pushboolean(state->L, 0); 97 | lua_pushstring(state->L, zmq_strerror(zmq_errno())); 98 | } 99 | else if (writable > 0) { 100 | int rv = luvL_zmq_socket_send(self, state); 101 | if (rv < 0) { 102 | lua_settop(state->L, 0); 103 | lua_pushboolean(state->L, 0); 104 | lua_pushstring(state->L, zmq_strerror(zmq_errno())); 105 | } 106 | } 107 | luvL_state_ready(state); 108 | return; 109 | } 110 | } 111 | 112 | 113 | /* Lua API */ 114 | static int luv_new_zmq(lua_State* L) { 115 | luv_thread_t* thread = luvL_thread_self(L); 116 | int nthreads = luaL_optinteger(L, 2, 1); 117 | 118 | luv_object_t* self = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 119 | luaL_getmetatable(L, LUV_ZMQ_CTX_T); 120 | lua_setmetatable(L, -2); 121 | 122 | luvL_object_init((luv_state_t*)thread, self); 123 | 124 | self->data = zmq_ctx_new(); 125 | zmq_ctx_set(self->data, ZMQ_IO_THREADS, nthreads); 126 | 127 | return 1; 128 | } 129 | 130 | /* socket methods */ 131 | static int luv_zmq_ctx_socket(lua_State* L) { 132 | luv_object_t* ctx = (luv_object_t*)lua_touserdata(L, 1); 133 | int type = luaL_checkint(L, 2); 134 | 135 | luv_state_t* curr = luvL_state_self(L); 136 | luv_object_t* self = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 137 | luaL_getmetatable(L, LUV_ZMQ_SOCKET_T); 138 | lua_setmetatable(L, -2); 139 | 140 | luvL_object_init(curr, self); 141 | 142 | self->data = zmq_socket(ctx->data, type); 143 | 144 | uv_os_sock_t socket; 145 | size_t len = sizeof(uv_os_sock_t); 146 | zmq_getsockopt(self->data, ZMQ_FD, &socket, &len); 147 | 148 | uv_poll_init_socket(luvL_event_loop(L), &self->h.poll, socket); 149 | uv_poll_start(&self->h.poll, UV_READABLE, _zmq_poll_cb); 150 | 151 | return 1; 152 | } 153 | 154 | static int luv_zmq_socket_bind(lua_State* L) { 155 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_ZMQ_SOCKET_T); 156 | const char* addr = luaL_checkstring(L, 2); 157 | /* XXX: make this async? */ 158 | int rv = zmq_bind(self->data, addr); 159 | lua_pushinteger(L, rv); 160 | return 1; 161 | } 162 | static int luv_zmq_socket_connect(lua_State* L) { 163 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_ZMQ_SOCKET_T); 164 | const char* addr = luaL_checkstring(L, 2); 165 | /* XXX: make this async? */ 166 | int rv = zmq_connect(self->data, addr); 167 | lua_pushinteger(L, rv); 168 | return 1; 169 | } 170 | 171 | static int luv_zmq_socket_send(lua_State* L) { 172 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_ZMQ_SOCKET_T); 173 | luv_state_t* curr = luvL_state_self(L); 174 | int rv = luvL_zmq_socket_send(self, curr); 175 | if (rv < 0) { 176 | int err = zmq_errno(); 177 | if (err == EAGAIN || err == EWOULDBLOCK) { 178 | TRACE("EAGAIN during SEND, polling...\n"); 179 | self->flags |= LUV_ZMQ_WSEND; 180 | return luvL_cond_wait(&self->queue, curr); 181 | } 182 | else { 183 | lua_settop(L, 0); 184 | lua_pushboolean(L, 0); 185 | lua_pushstring(L, strerror(errno)); 186 | } 187 | } 188 | return 2; 189 | } 190 | static int luv_zmq_socket_recv(lua_State* L) { 191 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_ZMQ_SOCKET_T); 192 | luv_state_t* curr = luvL_state_self(L); 193 | int rv = luvL_zmq_socket_recv(self, curr); 194 | if (rv < 0) { 195 | int err = zmq_errno(); 196 | if (err == EAGAIN || err == EWOULDBLOCK) { 197 | TRACE("EAGAIN during RECV, polling..\n"); 198 | self->flags |= LUV_ZMQ_WRECV; 199 | return luvL_cond_wait(&self->rouse, curr); 200 | } 201 | else { 202 | lua_settop(L, 0); 203 | lua_pushboolean(L, 0); 204 | lua_pushstring(L, zmq_strerror(err)); 205 | return 2; 206 | } 207 | } 208 | return 1; 209 | } 210 | 211 | static int luv_zmq_socket_close(lua_State* L) { 212 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_ZMQ_SOCKET_T); 213 | if (!luvL_object_is_closing(self)) { 214 | if (zmq_close(self->data)) { 215 | /* TODO: linger and error handling */ 216 | } 217 | uv_poll_stop(&self->h.poll); 218 | luvL_object_close(self); 219 | } 220 | return 1; 221 | } 222 | 223 | static const char* LUV_ZMQ_SOCKOPTS[] = { 224 | "", /* 0 */ 225 | "", /* 1 */ 226 | "", /* 2 */ 227 | "", /* 3 */ 228 | "AFFINITY", /* 4 */ 229 | "IDENTITY", /* 5 */ 230 | "SUBSCRIBE", /* 6 */ 231 | "UNSUBSCRIBE", /* 7 */ 232 | "RATE", /* 8 */ 233 | "RECOVERY_IVL", /* 9 */ 234 | "", /* 3 */ 235 | "SNDBUF", /* 11 */ 236 | "RCVBUF", /* 12 */ 237 | "RCVMORE", /* 13 */ 238 | "FD", /* 14 */ 239 | "EVENTS", /* 15 */ 240 | "TYPE", /* 16 */ 241 | "LINGER", /* 17 */ 242 | "RECONNECT_IVL", /* 18 */ 243 | "BACKLOG", /* 19 */ 244 | "", /* 20 */ 245 | "RECONNECT_IVL_MAX", /* 21 */ 246 | "MAXMSGSIZE", /* 22 */ 247 | "SNDHWM", /* 23 */ 248 | "RCVHWM", /* 24 */ 249 | "MULTICAST_HOPS", /* 25 */ 250 | "", /* 26 */ 251 | "RCVTIMEO", /* 27 */ 252 | "SNDTIMEO", /* 28 */ 253 | "", /* 29 */ 254 | "", /* 30 */ 255 | "IPV4ONLY", /* 31 */ 256 | "LAST_ENDPOINT", /* 32 */ 257 | "ROUTER_BEHAVIOR", /* 33 */ 258 | "TCP_KEEPALIVE", /* 34 */ 259 | "TCP_KEEPALIVE_CNT", /* 35 */ 260 | "TCP_KEEPALIVE_IDLE", /* 36 */ 261 | "TCP_KEEPALIVE_INTVL", /* 37 */ 262 | "TCP_ACCEPT_FILTER", /* 38 */ 263 | NULL 264 | }; 265 | 266 | static int luv_zmq_socket_setsockopt(lua_State* L) { 267 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_ZMQ_SOCKET_T); 268 | int opt, rv; 269 | if (lua_type(L, 2) == LUA_TSTRING) { 270 | opt = luaL_checkoption(L, 2, NULL, LUV_ZMQ_SOCKOPTS); 271 | } 272 | else { 273 | opt = luaL_checkint(L, 2); 274 | } 275 | switch (opt) { 276 | case ZMQ_SNDHWM: 277 | case ZMQ_RCVHWM: 278 | case ZMQ_RATE: 279 | case ZMQ_RECOVERY_IVL: 280 | case ZMQ_SNDBUF: 281 | case ZMQ_RCVBUF: 282 | case ZMQ_LINGER: 283 | case ZMQ_RECONNECT_IVL: 284 | case ZMQ_RECONNECT_IVL_MAX: 285 | case ZMQ_BACKLOG: 286 | case ZMQ_MULTICAST_HOPS: 287 | case ZMQ_RCVTIMEO: 288 | case ZMQ_SNDTIMEO: 289 | case ZMQ_ROUTER_BEHAVIOR: 290 | case ZMQ_TCP_KEEPALIVE: 291 | case ZMQ_TCP_KEEPALIVE_CNT: 292 | case ZMQ_TCP_KEEPALIVE_IDLE: 293 | case ZMQ_TCP_KEEPALIVE_INTVL: 294 | { 295 | int val = lua_tointeger(L, 2); 296 | rv = zmq_setsockopt(self->data, opt, &val, sizeof(val)); 297 | break; 298 | } 299 | 300 | case ZMQ_AFFINITY: 301 | { 302 | uint64_t val = (uint64_t)lua_tointeger(L, 2); 303 | rv = zmq_setsockopt(self->data, opt, &val, sizeof(val)); 304 | break; 305 | } 306 | 307 | case ZMQ_MAXMSGSIZE: 308 | { 309 | int64_t val = (int64_t)lua_tointeger(L, 2); 310 | rv = zmq_setsockopt(self->data, opt, &val, sizeof(val)); 311 | break; 312 | } 313 | 314 | case ZMQ_IPV4ONLY: 315 | { 316 | int val = lua_toboolean(L, 2); 317 | rv = zmq_setsockopt(self->data, opt, &val, sizeof(val)); 318 | break; 319 | } 320 | 321 | case ZMQ_IDENTITY: 322 | case ZMQ_SUBSCRIBE: 323 | case ZMQ_UNSUBSCRIBE: 324 | case ZMQ_TCP_ACCEPT_FILTER: 325 | { 326 | size_t len; 327 | const char* val = lua_tolstring(L, 2, &len); 328 | rv = zmq_setsockopt(self->data, opt, &val, len); 329 | break; 330 | } 331 | 332 | case ZMQ_RCVMORE: 333 | case ZMQ_FD: 334 | case ZMQ_EVENTS: 335 | case ZMQ_TYPE: 336 | case ZMQ_LAST_ENDPOINT: 337 | return luaL_error(L, "readonly option"); 338 | default: 339 | return luaL_error(L, "invalid option"); 340 | } 341 | if (rv < 0) { 342 | lua_pushboolean(L, 0); 343 | lua_pushstring(L, zmq_strerror(zmq_errno())); 344 | return 2; 345 | } 346 | lua_pushboolean(L, 1); 347 | return 1; 348 | } 349 | static int luv_zmq_socket_getsockopt(lua_State* L) { 350 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_ZMQ_SOCKET_T); 351 | size_t len; 352 | int opt; 353 | if (lua_type(L, 2) == LUA_TSTRING) { 354 | opt = luaL_checkoption(L, 2, NULL, LUV_ZMQ_SOCKOPTS); 355 | } 356 | else { 357 | opt = luaL_checkint(L, 2); 358 | } 359 | switch (opt) { 360 | case ZMQ_TYPE: 361 | case ZMQ_RCVMORE: 362 | case ZMQ_SNDHWM: 363 | case ZMQ_RCVHWM: 364 | case ZMQ_RATE: 365 | case ZMQ_RECOVERY_IVL: 366 | case ZMQ_SNDBUF: 367 | case ZMQ_RCVBUF: 368 | case ZMQ_LINGER: 369 | case ZMQ_RECONNECT_IVL: 370 | case ZMQ_RECONNECT_IVL_MAX: 371 | case ZMQ_BACKLOG: 372 | case ZMQ_MULTICAST_HOPS: 373 | case ZMQ_RCVTIMEO: 374 | case ZMQ_SNDTIMEO: 375 | case ZMQ_ROUTER_BEHAVIOR: 376 | case ZMQ_TCP_KEEPALIVE: 377 | case ZMQ_TCP_KEEPALIVE_CNT: 378 | case ZMQ_TCP_KEEPALIVE_IDLE: 379 | case ZMQ_TCP_KEEPALIVE_INTVL: 380 | case ZMQ_EVENTS: 381 | { 382 | int val; 383 | len = sizeof(val); 384 | zmq_getsockopt(self->data, opt, &val, &len); 385 | lua_pushinteger(L, val); 386 | break; 387 | } 388 | 389 | case ZMQ_AFFINITY: 390 | { 391 | uint64_t val = (uint64_t)lua_tointeger(L, 2); 392 | len = sizeof(val); 393 | zmq_getsockopt(self->data, opt, &val, &len); 394 | lua_pushinteger(L, (lua_Integer)val); 395 | break; 396 | } 397 | 398 | case ZMQ_MAXMSGSIZE: 399 | { 400 | int64_t val = (int64_t)lua_tointeger(L, 2); 401 | len = sizeof(val); 402 | zmq_getsockopt(self->data, opt, &val, &len); 403 | lua_pushinteger(L, (lua_Integer)val); 404 | break; 405 | } 406 | 407 | case ZMQ_IPV4ONLY: 408 | { 409 | int val = lua_toboolean(L, 2); 410 | len = sizeof(val); 411 | zmq_getsockopt(self->data, opt, &val, &len); 412 | lua_pushboolean(L, val); 413 | break; 414 | } 415 | 416 | case ZMQ_IDENTITY: 417 | case ZMQ_LAST_ENDPOINT: 418 | { 419 | char val[1024]; 420 | len = sizeof(val); 421 | zmq_getsockopt(self->data, opt, val, &len); 422 | lua_pushlstring(L, val, len); 423 | break; 424 | } 425 | 426 | case ZMQ_FD: 427 | { 428 | uv_os_sock_t socket; 429 | len = sizeof(uv_os_sock_t); 430 | zmq_getsockopt(self->data, ZMQ_FD, &socket, &len); 431 | /* TODO: give these a metatable */ 432 | #ifdef _WIN32 433 | luv_boxpointer(L, (uv_os_sock_t)socket); 434 | #else 435 | luv_boxinteger(L, socket); 436 | #endif 437 | } 438 | 439 | case ZMQ_SUBSCRIBE: 440 | case ZMQ_UNSUBSCRIBE: 441 | case ZMQ_TCP_ACCEPT_FILTER: 442 | return luaL_error(L, "writeonly option"); 443 | default: 444 | return luaL_error(L, "invalid option"); 445 | } 446 | return 1; 447 | } 448 | 449 | static int luv_zmq_socket_tostring(lua_State* L) { 450 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 451 | lua_pushfstring(L, "userdata<%s>: %p", LUV_ZMQ_SOCKET_T, self); 452 | return 1; 453 | } 454 | static int luv_zmq_socket_free(lua_State* L) { 455 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 456 | if (!luvL_object_is_closing(self)) { 457 | zmq_close(self->data); 458 | uv_poll_stop(&self->h.poll); 459 | luvL_object_close(self); 460 | } 461 | return 1; 462 | } 463 | 464 | static int luv_zmq_ctx_encoder(lua_State* L) { 465 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_ZMQ_CTX_T); 466 | lua_pushstring(L, "luv:zmq:decoder"); 467 | lua_pushlightuserdata(L, self->data); 468 | return 2; 469 | } 470 | 471 | int luvL_zmq_ctx_decoder(lua_State* L) { 472 | TRACE("ZMQ ctx decode hook\n"); 473 | luv_state_t* curr = luvL_state_self(L); 474 | luaL_checktype(L, -1, LUA_TLIGHTUSERDATA); 475 | 476 | luv_object_t* copy = (luv_object_t*)lua_newuserdata(L, sizeof(luv_object_t)); 477 | luaL_getmetatable(L, LUV_ZMQ_CTX_T); 478 | lua_setmetatable(L, -2); 479 | 480 | luvL_object_init(curr, copy); 481 | 482 | copy->data = lua_touserdata(L, -2); 483 | copy->flags = LUV_ZMQ_XDUPCTX; 484 | 485 | return 1; 486 | } 487 | 488 | static int luv_zmq_ctx_tostring(lua_State* L) { 489 | luv_object_t* self = (luv_object_t*)lua_touserdata(L, 1); 490 | lua_pushfstring(L, "userdata<%s>: %p", LUV_ZMQ_CTX_T, self); 491 | return 1; 492 | } 493 | static int luv_zmq_ctx_free(lua_State* L) { 494 | luv_object_t* self = (luv_object_t*)luaL_checkudata(L, 1, LUV_ZMQ_CTX_T); 495 | if (!(self->flags & LUV_ZMQ_XDUPCTX)) { 496 | zmq_ctx_destroy(self->data); 497 | } 498 | self->data = NULL; 499 | return 1; 500 | } 501 | 502 | luaL_Reg luv_zmq_funcs[] = { 503 | {"create", luv_new_zmq}, 504 | {NULL, NULL} 505 | }; 506 | 507 | luaL_Reg luv_zmq_ctx_meths[] = { 508 | {"socket", luv_zmq_ctx_socket}, 509 | {"__codec", luv_zmq_ctx_encoder}, 510 | {"__gc", luv_zmq_ctx_free}, 511 | {"__tostring",luv_zmq_ctx_tostring}, 512 | {NULL, NULL} 513 | }; 514 | 515 | luaL_Reg luv_zmq_socket_meths[] = { 516 | {"bind", luv_zmq_socket_bind}, 517 | {"connect", luv_zmq_socket_connect}, 518 | {"send", luv_zmq_socket_send}, 519 | {"recv", luv_zmq_socket_recv}, 520 | {"close", luv_zmq_socket_close}, 521 | {"getsockopt",luv_zmq_socket_getsockopt}, 522 | {"setsockopt",luv_zmq_socket_setsockopt}, 523 | {"__gc", luv_zmq_socket_free}, 524 | {"__tostring",luv_zmq_socket_tostring}, 525 | {NULL, NULL} 526 | }; 527 | 528 | 529 | -------------------------------------------------------------------------------- /src/ngx-queue.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | */ 5 | 6 | 7 | #ifndef NGX_QUEUE_H_INCLUDED_ 8 | #define NGX_QUEUE_H_INCLUDED_ 9 | 10 | 11 | typedef struct ngx_queue_s ngx_queue_t; 12 | 13 | struct ngx_queue_s { 14 | ngx_queue_t *prev; 15 | ngx_queue_t *next; 16 | }; 17 | 18 | 19 | #define ngx_queue_init(q) \ 20 | (q)->prev = q; \ 21 | (q)->next = q 22 | 23 | 24 | #define ngx_queue_empty(h) \ 25 | (h == (h)->prev) 26 | 27 | 28 | #define ngx_queue_insert_head(h, x) \ 29 | (x)->next = (h)->next; \ 30 | (x)->next->prev = x; \ 31 | (x)->prev = h; \ 32 | (h)->next = x 33 | 34 | 35 | #define ngx_queue_insert_after ngx_queue_insert_head 36 | 37 | 38 | #define ngx_queue_insert_tail(h, x) \ 39 | (x)->prev = (h)->prev; \ 40 | (x)->prev->next = x; \ 41 | (x)->next = h; \ 42 | (h)->prev = x 43 | 44 | 45 | #define ngx_queue_head(h) \ 46 | (h)->next 47 | 48 | 49 | #define ngx_queue_last(h) \ 50 | (h)->prev 51 | 52 | 53 | #define ngx_queue_sentinel(h) \ 54 | (h) 55 | 56 | 57 | #define ngx_queue_next(q) \ 58 | (q)->next 59 | 60 | 61 | #define ngx_queue_prev(q) \ 62 | (q)->prev 63 | 64 | 65 | #if defined(NGX_DEBUG) 66 | 67 | #define ngx_queue_remove(x) \ 68 | (x)->next->prev = (x)->prev; \ 69 | (x)->prev->next = (x)->next; \ 70 | (x)->prev = NULL; \ 71 | (x)->next = NULL 72 | 73 | #else 74 | 75 | #define ngx_queue_remove(x) \ 76 | (x)->next->prev = (x)->prev; \ 77 | (x)->prev->next = (x)->next 78 | 79 | #endif 80 | 81 | 82 | #define ngx_queue_split(h, q, n) \ 83 | (n)->prev = (h)->prev; \ 84 | (n)->prev->next = n; \ 85 | (n)->next = q; \ 86 | (h)->prev = (q)->prev; \ 87 | (h)->prev->next = h; \ 88 | (q)->prev = n; 89 | 90 | 91 | #define ngx_queue_add(h, n) \ 92 | (h)->prev->next = (n)->next; \ 93 | (n)->next->prev = (h)->prev; \ 94 | (h)->prev = (n)->prev; \ 95 | (h)->prev->next = h; 96 | 97 | 98 | #define ngx_queue_data(q, type, link) \ 99 | (type *) ((unsigned char *) q - offsetof(type, link)) 100 | 101 | 102 | #define ngx_queue_foreach(q, h) \ 103 | for ((q) = ngx_queue_head(h); \ 104 | (q) != ngx_queue_sentinel(h); \ 105 | (q) = ngx_queue_next(q)) 106 | 107 | 108 | #endif /* NGX_QUEUE_H_INCLUDED_ */ 109 | --------------------------------------------------------------------------------