├── .gitignore ├── GNUmakefile ├── LICENSE ├── Makefile ├── PORTING.md ├── README.md ├── config.h.guess ├── debian ├── changelog ├── compat ├── control ├── copyright ├── liblua5.1-cqueues.files ├── liblua5.1-cqueues.install ├── liblua5.2-cqueues.files ├── liblua5.2-cqueues.install └── rules ├── doc ├── art │ ├── lua.pdf │ └── lua.ps ├── cqueues.pdf └── cqueues.tex ├── examples ├── chat.srv ├── condition.lua ├── dig.lua ├── echo.srv ├── getopt.lua ├── httpd.srv ├── notify.lua ├── peereid.lua ├── promise.lua ├── signal.pause ├── signal.wait ├── simple.get ├── socket.pair ├── socket.xchg └── thread.count ├── mk ├── changelog ├── errno.ls ├── luapath ├── macros.ls ├── runlua ├── vendor.cc └── vendor.os ├── regress ├── 102-getlookup-getsearch.lua ├── 125-thread.current-not-cleared-on-error.lua ├── 130-segfault-on-null-cstack.lua ├── 141-segfault-on-accept.lua ├── 145-assertion-on-dead.lua ├── 152-thread-integer-passing.lua ├── 153-dns-resolvers.lua ├── 157-interposed-methods-lost.lua ├── 174-cq-close.lua ├── 175-cq-close-segfault.lua ├── 194-bind-unix.lua ├── 20-listen-dgram.lua ├── 22-client-dtls.lua ├── 23-eventfd.lua ├── 30-starttls-completion.lua ├── 44-resolvers-gc.lua ├── 51-join-defunct-thread.lua ├── 59-catch-special-only.lua ├── 61-multiwriters.lua ├── 62-noname.lua ├── 63-timeouts-early.lua ├── 71-empty-cqueue.lua ├── 72-fill-hangs.lua ├── 73-starttls-buffering.lua ├── 75-sleep-1s-broken.lua ├── 82-localname-garbage.lua ├── 85-cancel-issues.lua ├── 87-alpn-disappears.lua ├── 93-remove-family-field.lua ├── GNUmakefile ├── Makefile ├── regress.lua └── regress.sh ├── src ├── GNUmakefile ├── Makefile ├── auxlib.lua ├── condition.lua ├── cqueues.c ├── cqueues.h ├── cqueues.lua ├── dns.c ├── dns.config.lua ├── dns.hints.lua ├── dns.hosts.lua ├── dns.lua ├── dns.packet.lua ├── dns.record.lua ├── dns.resolver.lua ├── dns.resolvers.lua ├── errno.c.m4 ├── errno.lua ├── lib │ ├── GNUmakefile │ ├── Makefile │ ├── dns.c │ ├── dns.h │ ├── fifo.h │ ├── kpoll.c │ ├── llrb.h │ ├── notify.c │ ├── notify.h │ ├── socket.c │ └── socket.h ├── notify.c ├── notify.lua ├── promise.lua ├── signal.c ├── signal.lua ├── socket.c ├── socket.debug.lua ├── socket.lua ├── thread.c └── thread.lua ├── util ├── Makefile └── tcp-urgent.c └── vendor └── compat53 ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── c-api ├── compat-5.3.c └── compat-5.3.h ├── compat53 ├── init.lua └── module.lua ├── lbitlib.c ├── lprefix.h ├── lstrlib.c ├── ltablib.c ├── lutf8lib.c ├── rockspecs ├── bit32-5.3.5-1.rockspec ├── bit32-scm-1.rockspec ├── compat53-0.1-1.rockspec ├── compat53-0.2-1.rockspec ├── compat53-0.3-1.rockspec ├── compat53-0.4-1.rockspec ├── compat53-0.5-1.rockspec ├── compat53-0.7-1.rockspec ├── compat53-0.8-1.rockspec └── compat53-scm-0.rockspec └── tests ├── test-bit32.lua ├── test.lua └── testmod.c /.gitignore: -------------------------------------------------------------------------------- 1 | doc/*.aux 2 | doc/*.idx 3 | doc/*.log 4 | doc/*.out 5 | doc/*.toc 6 | src/5.1/ 7 | src/5.2/ 8 | src/5.3/ 9 | src/errno.c 10 | src/lib/*.o 11 | src/lib/*.a 12 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | # non-recursive prologue 2 | sp := $(sp).x 3 | dirstack_$(sp) := $(d) 4 | d := $(abspath $(lastword $(MAKEFILE_LIST))/..) 5 | 6 | ifeq ($(origin GUARD_$(d)), undefined) 7 | GUARD_$(d) := 1 8 | 9 | 10 | all: # default target 11 | 12 | # 13 | # G N U M A K E F U N C T I O N S 14 | # 15 | KNOWN_APIS = 5.1 5.2 5.3 5.4 16 | 17 | # template for invoking luapath script 18 | LUAPATH := $(d)/mk/luapath 19 | LUAPATH_FN = $(shell env CC='$(subst ',\\',$(CC))' CPPFLAGS='$(subst ',\\',$(CPPFLAGS))' LDFLAGS='$(subst ',\\',$(LDFLAGS))' $(LUAPATH) -krxm3 -I '$(subst ',\\',$(DESTDIR)$(includedir))' -I/usr/include -I/usr/local/include -P '$(subst ',\\',$(DESTDIR)$(bindir))' -P '$(subst ',\\',$(bindir))' -L '$(subst ',\\',$(DESTDIR)$(libdir))' -L '$(subst ',\\',$(libdir))' -v$(1) $(2)) 20 | 21 | # check whether luapath can locate Lua $(1) headers 22 | HAVE_API_FN = $(and $(filter $(1),$(call LUAPATH_FN,$(1),version)),$(1)$(info enabling Lua $(1))) 23 | 24 | # check whether $(1) in LUA_APIS or $(LUA$(1:.=)_CPPFLAGS) is non-empty 25 | WITH_API_FN = $$(and $$(or $$(filter $(1),$$(LUA_APIS)),$$(LUA$(subst .,,$(1))_CPPFLAGS)),$(1)) 26 | 27 | # 28 | # E N V I R O N M E N T C O N F I G U R A T I O N 29 | # 30 | -include $(d)/.config 31 | 32 | prefix ?= /usr/local 33 | includedir ?= $(prefix)/include 34 | libdir ?= $(prefix)/lib 35 | datadir ?= $(prefix)/share 36 | bindir ?= $(prefix)/bin 37 | lua51cpath ?= $(libdir)/lua/5.1 38 | lua51path ?= $(datadir)/lua/5.1 39 | lua52cpath ?= $(libdir)/lua/5.2 40 | lua52path ?= $(datadir)/lua/5.2 41 | lua53cpath ?= $(libdir)/lua/5.3 42 | lua53path ?= $(datadir)/lua/5.3 43 | lua54cpath ?= $(libdir)/lua/5.4 44 | lua54path ?= $(datadir)/lua/5.4 45 | 46 | 47 | AR ?= ar 48 | RANLIB ?= ranlib 49 | M4 ?= m4 50 | MV ?= mv 51 | RM ?= rm 52 | CP ?= cp 53 | RMDIR ?= rmdir 54 | MKDIR ?= mkdir 55 | CHMOD ?= chmod 56 | INSTALL ?= install 57 | INSTALL_DATA ?= $(INSTALL) -m 644 58 | TOUCH ?= touch 59 | TEE ?= tee 60 | TEE_A ?= $(TEE) -a 61 | 62 | # see Lua Autodetection, below 63 | 64 | .PHONY: $(d)/config 65 | 66 | PRINT_$(d) = printf "%s = %s\n" '$(1)' '$(subst ',\\',$(2))' | $(TEE_A) '$(3)' 67 | 68 | LAZY_$(d) = \ 69 | prefix includedir libdir datadir bindir \ 70 | lua51cpath lua51path lua52cpath lua52path lua53cpath lua53path lua54cpath lua54path \ 71 | CC ALL_CPPFLAGS CPPFLAGS ALL_CFLAGS CFLAGS ALL_LDFLAGS LDFLAGS \ 72 | ALL_SOFLAGS SOFLAGS ALL_LIB LIBS \ 73 | $(foreach API,$(KNOWN_APIS),ALL_LUA$(subst .,,$(API))_CPPFLAGS) \ 74 | AR RANLIB M4 MV RM CP RMDIR MKDIR CHMOD INSTALL INSTALL_DATA TOUCH \ 75 | TEE TEE_A 76 | 77 | NONLAZY_$(d) = \ 78 | LUA_APIS \ 79 | $(foreach API,$(KNOWN_APIS),LUAC$(subst .,,$(API))) \ 80 | $(foreach API,$(KNOWN_APIS),$(and $(call WITH_API_FN,$(API)),LUA$(subst .,,$(API))_CPPFLAGS)) 81 | 82 | $(d)/config: 83 | $(TOUCH) $(@D)/.config.tmp 84 | @$(foreach V,$(LAZY_$(@D)), $(call PRINT_$(@D),$(V),$(value $(V)),$(@D)/.config.tmp);) 85 | @$(foreach V,$(NONLAZY_$(@D)), $(call PRINT_$(@D),$(V),$($(V)),$(@D)/.config.tmp);) 86 | $(MV) $(@D)/.config.tmp $(@D)/.config 87 | 88 | # add local targets if building from inside project tree 89 | ifneq "$(filter $(abspath $(d)/..)/%, $(abspath $(firstword $(MAKEFILE_LIST))))" "" 90 | .PHONY: config configure 91 | 92 | config configure: $(d)/config 93 | endif 94 | 95 | 96 | # 97 | # L U A A U T O D E T E C T I O N 98 | # 99 | 100 | # set LUA_APIS if empty or "?" 101 | ifeq ($(or $(strip $(LUA_APIS)),?),?) 102 | override LUA_APIS := $(call HAVE_API_FN,5.1) $(call HAVE_API_FN,5.2) $(call HAVE_API_FN,5.3) $(call HAVE_API_FN,5.4) 103 | endif 104 | 105 | define LUAXY_template 106 | 107 | # set luaXYcpath if empty or "?" 108 | ifeq ($$(or $$(strip $$(lua$(subst .,,$(1))cpath)),?),?) 109 | override lua$(subst .,,$(1))cpath := $$(or $$(call LUAPATH_FN,$(1),cdir),$$(libdir)/lua/$(1)) 110 | endif 111 | 112 | # set luaXYpath if empty or "?" 113 | ifeq ($$(or $$(strip $$(lua$(subst .,,$(1))path)),?),?) 114 | override lua$(subst .,,$(1))path = $$(or $$(call LUAPATH_FN,$(1),ldir),$$(datadir)/lua/$(1)) 115 | endif 116 | 117 | # set LUAXY_CPPFLAGS if undefined or "?" (NB: can be empty if path already in $(CPPFLAGS)) 118 | ifeq ($$(and $$(findstring undefined,$$(origin LUA$(subst .,,$(1))_CPPFLAGS)),?),?) 119 | override LUA$(subst .,,$(1))_CPPFLAGS = $$(and $$(call WITH_API_FN,$(1)),$$(call LUAPATH_FN,$(1),cppflags)) 120 | endif 121 | 122 | # set ALL_LUAXY_CPPFLAGS if empty or "?" 123 | ifeq ($$(or $$(strip $$(ALL_LUA$(subst .,,$(1))_CPPFLAGS)),?),?) 124 | override ALL_LUA$(subst .,,$(1))_CPPFLAGS = -DLUA_COMPAT_APIINTCASTS $$(LUA$(subst .,,$(1))_CPPFLAGS) 125 | endif 126 | 127 | # set LUAXYC if empty or "?" 128 | ifeq ($$(or $$(strip $$(LUAC$(subst .,,$(1)))),?),?) 129 | override LUAC$(subst .,,$(1)) = $$(or $$(call LUAPATH_FN,$(1),luac),true) 130 | endif 131 | 132 | endef # LUAXY_template 133 | 134 | $(eval $(call LUAXY_template,5.1)) 135 | $(eval $(call LUAXY_template,5.2)) 136 | $(eval $(call LUAXY_template,5.3)) 137 | $(eval $(call LUAXY_template,5.4)) 138 | 139 | # 140 | # A U T O D E T E C T C O M P I L A T I O N F L A G S 141 | # 142 | cc-option ?= $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \ 143 | > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi;) 144 | 145 | VENDOR_OS_$(d) := $(shell $(d)/mk/vendor.os) 146 | VENDOR_CC_$(d) := $(shell env CC="$(CC)" $(d)/mk/vendor.cc) 147 | 148 | # 149 | # ALL_CPPFLAGS 150 | # 151 | ifeq ($(origin ALL_CPPFLAGS), undefined) 152 | 153 | ifneq ($(VENDOR_OS_$(d)), OpenBSD) 154 | ALL_CPPFLAGS += -D_REENTRANT -D_THREAD_SAFE -D_GNU_SOURCE 155 | endif 156 | 157 | ifeq ($(VENDOR_OS_$(d)), SunOS) 158 | ALL_CPPFLAGS += -Usun -D_XPG4_2 -D__EXTENSIONS__ 159 | endif 160 | 161 | ALL_CPPFLAGS += $(CPPFLAGS) 162 | 163 | endif # ALL_CPPFLAGS 164 | 165 | # 166 | # ALL_CFLAGS 167 | # 168 | ifeq ($(origin ALL_CFLAGS), undefined) 169 | 170 | ifeq ($(VENDOR_CC_$(d)), gcc) 171 | ALL_CFLAGS += -O2 -std=gnu99 -fPIC 172 | ALL_CFLAGS += -g -Wall -Wextra $(call cc-option, -Wno-missing-field-initializers) $(call cc-option, -Wno-override-init) -Wno-unused 173 | endif 174 | 175 | ifeq ($(VENDOR_CC_$(d)), clang) 176 | ALL_CFLAGS += -O2 -std=gnu99 -fPIC 177 | ALL_CFLAGS += -g -Wall -Wextra -Wno-missing-field-initializers -Wno-initializer-overrides -Wno-unused -Wno-dollar-in-identifier-extension 178 | endif 179 | 180 | ifeq ($(VENDOR_CC_$(d)), sunpro) 181 | ALL_CFLAGS += -xcode=pic13 182 | ALL_CFLAGS += -g 183 | # 184 | # Solaris Studio supports anonymous unions just fine; but it complains 185 | # incessantly about them. 186 | # 187 | ALL_CFLAGS += -erroff=E_ANONYMOUS_UNION_DECL 188 | endif 189 | 190 | ifeq ($(VENDOR_OS_$(d)), Darwin) 191 | ALL_CFLAGS += -Wno-deprecated-declarations 192 | endif 193 | 194 | ALL_CFLAGS += $(CFLAGS) 195 | 196 | endif # ALL_CFLAGS 197 | 198 | # 199 | # ALL_SOFLAGS 200 | # 201 | ifeq ($(origin ALL_SOFLAGS), undefined) 202 | 203 | ifeq ($(VENDOR_OS_$(d)), Darwin) 204 | ALL_SOFLAGS += -bundle -undefined dynamic_lookup 205 | else 206 | ALL_SOFLAGS += -shared 207 | endif 208 | 209 | ALL_SOFLAGS += $(SOFLAGS) 210 | 211 | endif # ALL_SOFLAGS 212 | 213 | # 214 | # ALL_LDFLAGS 215 | # 216 | ifeq ($(origin ALL_LDFLAGS), undefined) 217 | 218 | ALL_LDFLAGS += -L$(DESTDIR)$(libdir) -L$(libdir) 219 | ALL_LDFLAGS += $(LDFLAGS) 220 | 221 | endif # ALL_LDFLAGS 222 | 223 | # 224 | # ALL_LIBS 225 | # 226 | ifeq ($(origin ALL_LIBS), undefined) 227 | 228 | # put $(LIBS) first as they're more likely to be higher-level dependencies 229 | ALL_LIBS += $(LIBS) 230 | ALL_LIBS += -lssl -lcrypto -lpthread 231 | 232 | # NetBSD, FreeBSD, OpenBSD (and presumably descendants) lack any libdl; 233 | # dlopen, et al are part of libc. 234 | ifneq ($(patsubst %BSD,BSD,$(VENDOR_OS_$(d))), BSD) 235 | ALL_LIBS += -ldl 236 | endif 237 | 238 | # This only seems to be necessary on Linux. Darwin and OpenBSD lack a librt. 239 | # On OpenBSD clock_gettime is part of libc. Others have librt, but linking 240 | # it in is unnecessary. 241 | ifeq ($(VENDOR_OS_$(d)), Linux) 242 | ALL_LIBS += -lrt 243 | endif 244 | 245 | ALL_LIBS += -lm 246 | 247 | endif # ALL_LIBS 248 | 249 | 250 | # 251 | # P R O J E C T R U L E S 252 | # 253 | include $(d)/src/GNUmakefile 254 | include $(d)/regress/GNUmakefile 255 | 256 | $(d)/config.h: $(d)/config.h.guess 257 | $(CP) $< $@ 258 | 259 | 260 | # 261 | # C L E A N R U L E S 262 | # 263 | .PHONY: $(d)/clean~ clean~ 264 | 265 | $(d)/clean~: 266 | $(RM) -f $(@D)/*~ 267 | 268 | clean~: $(d)/clean~ 269 | 270 | 271 | # 272 | # D E B I A N R U L E S 273 | # 274 | ifneq "$(filter $(abspath $(d))/%, $(abspath $(firstword $(MAKEFILE_LIST))))" "" 275 | 276 | DPKG_BUILDPACKAGE ?= dpkg-buildpackage 277 | FAKEROOT ?= fakeroot 278 | DPKG_BUILDPACKAGE_OPTIONS ?= -b -uc -us 279 | 280 | .PHONY: $(d)/debian $(d)/debian-clean debian deb debian-clean deb-clean 281 | 282 | $(d)/debian: 283 | cd $(@D) && $(DPKG_BUILDPACKAGE) -rfakeroot $(DPKG_BUILDPACKAGE_OPTIONS) 284 | 285 | $(d)/debian-clean: 286 | cd $(@D) && $(FAKEROOT) ./debian/rules clean 287 | 288 | debian deb: $(d)/debian 289 | 290 | debian-clean deb-clean: $(d)/debian-clean 291 | 292 | endif # debian guard 293 | 294 | 295 | # 296 | # R E D H A T R U L E S 297 | # 298 | ifneq "$(filter $(abspath $(d))/%, $(abspath $(firstword $(MAKEFILE_LIST))))" "" 299 | .PHONY: $(d)/redhat $(d)/redhat-clean redhat rpm redhat-clean rpm-clean 300 | 301 | redhat rpm: $(d)/redhat 302 | 303 | redhat-clean rpm-clean: $(d)/redhat-clean 304 | 305 | endif # redhat guard 306 | 307 | 308 | # 309 | # R E L E A S E T A R B A L L R U L E S 310 | # 311 | ifneq "$(filter $(abspath $(d))/%, $(abspath $(firstword $(MAKEFILE_LIST))))" "" 312 | 313 | CQUEUES_VERSION := $(shell $(d)/mk/changelog version) 314 | 315 | .PHONY: $(d)/cqueues-$(CQUEUES_VERSION).tgz release 316 | 317 | $(d)/cqueues-$(CQUEUES_VERSION).tgz: 318 | cd $(@D) && git archive --format=tar --prefix=$(basename $(@F))/ HEAD | gzip -c > $@ 319 | 320 | release: $(d)/cqueues-$(CQUEUES_VERSION).tgz 321 | 322 | endif # release guard 323 | 324 | 325 | endif # include guard 326 | 327 | # non-recursive epilogue 328 | d := $(dirstack_$(sp)) 329 | sp := $(basename $(sp)) 330 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012-2015 William Ahern 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to 5 | deal in the Software without restriction, including without limitation the 6 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 | sell copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 | IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | all: 4 | +gmake -f GNUmakefile all 5 | 6 | .DEFAULT: 7 | +gmake -f GNUmakefile $< 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## API Documentation 2 | 3 | Please refer to the PDF available at 4 | 5 | 6 | 7 | ## Build Dependancies 8 | 9 | The Makefile requires GNU Make. The source code should build with recent 10 | GCC, clang, or Solaris SunPro compilers. 11 | 12 | If you use your own Makefile, note that GCC and especially clang may emit 13 | copious warnings about initializers and unused parameters. These warnings 14 | are stupid. Use `-Wno-override-init` (GCC), `-Wno-initializer-overrides` 15 | (clang) and `-Wno-unused` to quiet these. For other warnings, patches 16 | welcome. 17 | 18 | M4 and awk are required to generate `errno.c`. It relies on `mk/errno.list` to 19 | enumerate the system error macro names. `mk/errno.list` is a small 20 | POSIX-compatible shell script. By default it processes GCC's `-dM` macro list 21 | (clang also supports this option). For SunPro it uses a slightly cruder 22 | method. 23 | 24 | Because the location of Lua include headers are unpredictable across 25 | systems, the build system by default relies on `mk/luapath` to locate the 26 | correct headers. `mk/luapath` uses various POSIX utilities. For more 27 | information [see the luapath project page](http://25thandclement.com/~william/projects/luapath.html). 28 | But see `LUA_APIS`, `LUA51_CPPFLAGS`, `LUA52_CPPFLAGS`, and 29 | `LUA53_CPPFLAGS`, below. 30 | 31 | `cqueues` should work on recent versions of Linux, OS X, Solaris, NetBSD, 32 | FreeBSD, OpenBSD, and derivatives. The regression suite is run on all 33 | supported platforms before rolling a release, and regularly during the 34 | development. In the future support may be added for AIX and the AIX 35 | `pollset` interface. Windows support is planned, though initially by relying 36 | on BSD `select`. 37 | 38 | ## Build Overview 39 | 40 | There is no separate `./configure` step at the moment. System introspection 41 | occurs during compile time. However, the `configure` Make target can be used 42 | to cache the build environment. 43 | 44 | 45 | ## Build Environment 46 | 47 | ### Lua APIs 48 | 49 | `cqueues` targets the three latest Lua APIs---5.1, 5.2, and 5.3---and all 50 | can be compiled simultaneously. Supported build targets are automatically 51 | detected by default. To override API autodetection specify `LUA_APIS`. For 52 | example, 53 | 54 | ``` 55 | $ make LUA_APIS="5.2 5.3" 56 | ``` 57 | 58 | 59 | ### Toolchain Flags 60 | 61 | All the common GNU-style compiler variables are supported, including `CC`, 62 | `CPPFLAGS`, `CFLAGS`, `LDFLAGS`, `SOFLAGS`, and `LIBS`. Note that you can 63 | specify the path to both Lua 5.1, 5.2, and 5.3 include headers at the same 64 | time in `CPPFLAGS`; the build system will work things out to ensure the 65 | correct headers are loaded at compile-time. To specify them explicitly 66 | provide 67 | 68 | - `LUA51_CPPFLAGS` - preprocessor flags for Lua 5.1 69 | - `LUA52_CPPFLAGS` - preprocessor flags for Lua 5.2 70 | - `LUA53_CPPFLAGS` - preprocessor flags for Lua 5.3 71 | 72 | To completely override all internally-defined flags, specify the 73 | `ALL_`-prefixed variant of any of the above. For example, specify 74 | `ALL_CPPFLAGS` to override the built-in optimization and warning flags. 75 | Note that object files are built using a command similar to 76 | 77 | ``` 78 | $ $(CC) $(ALL_LUA53_CPPFLAGS) $(ALL_CPPFLAGS) 79 | ``` 80 | 81 | where the Lua-specific flags remain separate from more general flags. 82 | 83 | 84 | ### Installation Paths 85 | 86 | All the common GNU-style installation path variables are supported, 87 | including `prefix`, `bindir`, `libdir`, `datadir`, `includedir`, and 88 | `DESTDIR`. These additional path variables are also allowed: 89 | 90 | - `lua51path` - install path for Lua 5.1 modules, e.g. `$(prefix)/share/lua/5.1` 91 | - `lua51cpath` - install path for Lua 5.1 C modules, e.g. `$(prefix)/lib/lua/5.1` 92 | - `lua52path` - install path for Lua 5.2 modules, e.g. `$(prefix)/share/lua/5.2` 93 | - `lua52cpath` - install path for Lua 5.2 C modules, e.g. `$(prefix)/lib/lua/5.2` 94 | - `lua53path` - install path for Lua 5.3 modules, e.g. `$(prefix)/share/lua/5.3` 95 | - `lua53cpath` - install path for Lua 5.3 C modules, e.g. `$(prefix)/lib/lua/5.3` 96 | 97 | 98 | ### Caching Environment 99 | 100 | Invoking the `configure` target will cache the Make environment and reload 101 | the variable values on subsequent invocations. Variables can be modified on 102 | an individual basis after this. 103 | 104 | 105 | ## Build Targets 106 | 107 | `cqueues` targets the Lua 5.1 (LuaJIT), 5.2, and 5.3 API. For various reasons 108 | the build system is capable of building all three modules simultaneously in 109 | a single Make invocation. Therefore, there are many seemingly superfluous 110 | target names, either out of necessity or for convenience. 111 | 112 | 113 | ### Compile Targets 114 | 115 | #### liblua5.1-cqueues 116 | 117 | Build Lua 5.1 cqueues modules 118 | 119 | #### liblua5.2-cqueues 120 | 121 | Build Lua 5.2 cqueues modules 122 | 123 | #### liblua5.3-cqueues 124 | 125 | Build Lua 5.3 cqueues modules 126 | 127 | #### all5.1 128 | 129 | Synonym for liblua5.1-cqueues 130 | 131 | 132 | #### all5.2 133 | 134 | Synonym for liblua5.2-cqueues 135 | 136 | 137 | #### all5.3 138 | 139 | Synonym for liblua5.3-cqueues 140 | 141 | #### all 142 | 143 | Invokes one or more of the above according to the definition of `LUA_APIS`. 144 | 145 | 146 | ### Install Targets 147 | 148 | #### liblua5.1-cqueues-install 149 | 150 | Install Lua 5.1 cqueues modules 151 | 152 | #### liblua5.2-cqueues-install 153 | 154 | Install Lua 5.2 cqueues modules 155 | 156 | #### liblua5.3-cqueues-install 157 | 158 | Install Lua 5.3 cqueues modules 159 | 160 | #### install5.1 161 | 162 | Invokes liblua5.1-cqueues-install 163 | 164 | #### install5.2 165 | 166 | Invokes liblua5.2-cqueues-install 167 | 168 | #### install5.3 169 | 170 | Invokes liblua5.3-cqueues-install 171 | 172 | #### install 173 | 174 | Invokes one of more of the above install targets according to `LUA_APIS`. 175 | 176 | 177 | ### Uninstall Targets 178 | 179 | #### liblua5.1-cqueues-uninstall 180 | 181 | Uninstall Lua 5.1 cqueues modules 182 | 183 | #### liblua5.2-cqueues-uninstall 184 | 185 | Uninstall Lua 5.2 cqueues modules 186 | 187 | #### liblua5.3-cqueues-uninstall 188 | 189 | Uninstall Lua 5.3 cqueues modules 190 | 191 | #### uninstall5.1 192 | 193 | Invokes liblua5.1-cqueues-uninstall 194 | 195 | #### uninstall5.2 196 | 197 | Invokes liblua5.2-cqueues-uninstall 198 | 199 | #### uninstall5.3 200 | 201 | Invokes liblua5.3-cqueues-uninstall 202 | 203 | #### uninstall 204 | 205 | Invokes one or more of the above uninstall targets according to `LUA_APIS`. 206 | 207 | 208 | ### Other Targets 209 | 210 | #### clean 211 | 212 | rm binary targets, object files, debugging symbols, etc 213 | 214 | #### clean~ 215 | 216 | clean + rm *~ 217 | 218 | #### debian 219 | 220 | Build debian packages liblua5.1-cqueues and liblua5.2-cqueues using 221 | the dpkg-buildpackage utility. The Make variables `DPKG_BUILDPACKAGE` 222 | and `DPKG_BUILDPACKAGE_OPTIONS` can be used to manipulate this 223 | process. 224 | 225 | 226 | 227 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: liblua-cqueues 2 | Section: unknown 3 | Priority: extra 4 | Maintainer: William Ahern 5 | Build-Depends: debhelper (>= 9), m4, dh-lua, libssl-dev 6 | Standards-Version: 3.9.2 7 | 8 | Package: liblua5.1-cqueues 9 | Architecture: any 10 | Depends: openssl, liblua5.1-0 11 | Description: Event, socket, signal, thread, and file change notification Lua 12 | library. 13 | 14 | Package: liblua5.2-cqueues 15 | Architecture: any 16 | Depends: openssl, liblua5.2-0 17 | Description: Event, socket, signal, thread, and file change notification Lua 18 | library. 19 | 20 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Author: William Ahern 2 | Download: http://25thandclement.com/~william/projects/cqueues.html 3 | 4 | Files: * 5 | Copyright: © 2012-2015, William Ahern 6 | License: MIT 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to 9 | deal in the Software without restriction, including without limitation the 10 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 | sell copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 23 | IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /debian/liblua5.1-cqueues.files: -------------------------------------------------------------------------------- 1 | usr/lib/lua/5.1/_cqueues.so 2 | usr/share/lua/5.1/cqueues* 3 | -------------------------------------------------------------------------------- /debian/liblua5.1-cqueues.install: -------------------------------------------------------------------------------- 1 | debian/tmp/usr/lib/lua/5.1/_cqueues.so usr/lib/lua/5.1 2 | debian/tmp/usr/share/lua/5.1/cqueues* usr/share/lua/5.1 3 | -------------------------------------------------------------------------------- /debian/liblua5.2-cqueues.files: -------------------------------------------------------------------------------- 1 | usr/lib/lua/5.2/_cqueues.so 2 | usr/share/lua/5.2/cqueues* 3 | -------------------------------------------------------------------------------- /debian/liblua5.2-cqueues.install: -------------------------------------------------------------------------------- 1 | debian/tmp/usr/lib/lua/5.2/_cqueues.so usr/lib/lua/5.2 2 | debian/tmp/usr/share/lua/5.2/cqueues* usr/share/lua/5.2 3 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | CFLAGS := -O3 -g -fstack-protector --param=ssp-buffer-size=4 4 | 5 | DESTDIR=debian/tmp 6 | prefix=/usr 7 | build=liblua5.1-cqueues liblua5.2-cqueues 8 | install=$(addsuffix -install,$(build)) 9 | 10 | %: 11 | dh $@ 12 | 13 | override_dh_auto_configure: 14 | true 15 | 16 | override_dh_auto_build: 17 | make DESTDIR=$(DESTDIR) prefix=$(prefix) $(build) 18 | 19 | override_dh_auto_install: 20 | make DESTDIR=$(DESTDIR) prefix=$(prefix) $(install) 21 | 22 | override_dh_auto_clean: 23 | make clean 24 | 25 | override_dh_auto_test: 26 | true 27 | -------------------------------------------------------------------------------- /doc/art/lua.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wahern/cqueues/8c0142577d3cb1f24917879997678bef0d084815/doc/art/lua.pdf -------------------------------------------------------------------------------- /doc/art/lua.ps: -------------------------------------------------------------------------------- 1 | %!PS-Adobe-2.0 EPSF-2.0 2 | %%Title: Lua logo 3 | %%Creator: lua@tecgraf.puc-rio.br 4 | %%CreationDate: Wed Nov 29 19:04:04 EDT 2000 5 | %%BoundingBox: -45 0 1035 1080 6 | %%Pages: 1 7 | %%EndComments 8 | %%EndProlog 9 | 10 | %------------------------------------------------------------------------------ 11 | % 12 | % Graphic design by Alexandre Nakonechnyj. 13 | % PostScript programming by the Lua team. 14 | % This code is hereby placed in the public domain. 15 | % 16 | % Permission is hereby granted, without written agreement and without license 17 | % or royalty fees, to use, copy, and distribute this logo for any purpose, 18 | % including commercial applications, subject to the following conditions: 19 | % 20 | % * The origin of this logo must not be misrepresented; you must not 21 | % claim that you drew the original logo. We recommend that you give credit 22 | % to the graphics designer in all printed matter that includes the logo. 23 | % 24 | % * The only modification you can make is to adapt the orbiting text to 25 | % your product name. 26 | % 27 | % * The logo can be used in any scale as long as the relative proportions 28 | % of its elements are maintained. 29 | % 30 | %------------------------------------------------------------------------------ 31 | 32 | /LABEL () def 33 | 34 | %-- DO NOT CHANGE ANYTHING BELOW THIS LINE ------------------------------------ 35 | 36 | /PLANETCOLOR {0 0 0.5 setrgbcolor} bind def 37 | /HOLECOLOR {1.0 setgray} bind def 38 | /ORBITCOLOR {0.5 setgray} bind def 39 | /LOGOFONT {/Helvetica 0.90} def 40 | /LABELFONT {/Helvetica 0.36} def 41 | 42 | %------------------------------------------------------------------------------ 43 | 44 | /MOONCOLOR {PLANETCOLOR} bind def 45 | /LOGOCOLOR {HOLECOLOR} bind def 46 | /LABELCOLOR {ORBITCOLOR} bind def 47 | 48 | /LABELANGLE 125 def 49 | /LOGO (Lua) def 50 | 51 | /DASHANGLE 10 def 52 | /HALFDASHANGLE DASHANGLE 2 div def 53 | 54 | % moon radius. planet radius is 1. 55 | /r 1 2 sqrt 2 div sub def 56 | 57 | /D {0 360 arc fill} bind def 58 | /F {exch findfont exch scalefont setfont} bind def 59 | 60 | % place it nicely on the paper 61 | /RESOLUTION 1024 def 62 | RESOLUTION 2 div dup translate 63 | RESOLUTION 2 div 2 sqrt div dup scale 64 | 65 | %-------------------------------------------------------------------- planet -- 66 | PLANETCOLOR 67 | 0 0 1 D 68 | 69 | %---------------------------------------------------------------------- hole -- 70 | HOLECOLOR 71 | 1 2 r mul sub dup r D 72 | 73 | %---------------------------------------------------------------------- moon -- 74 | MOONCOLOR 75 | 1 1 r D 76 | 77 | %---------------------------------------------------------------------- logo -- 78 | LOGOCOLOR 79 | LOGOFONT 80 | F 81 | LOGO stringwidth pop 2 div neg 82 | -0.5 moveto 83 | LOGO show 84 | 85 | %------------------------------------------------------------------------------ 86 | % based on code from Blue Book Program 10, on pages 167--169 87 | % available at ftp://ftp.adobe.com/pub/adobe/displaypostscript/bluebook.shar 88 | 89 | % str ptsize centerangle radius outsidecircletext -- 90 | /outsidecircletext { 91 | circtextdict begin 92 | /radius exch def 93 | /centerangle exch def 94 | /ptsize exch def 95 | /str exch def 96 | 97 | gsave 98 | str radius ptsize findhalfangle 99 | centerangle 100 | add rotate 101 | str 102 | { /charcode exch def 103 | ( ) dup 0 charcode put outsideplacechar 104 | } forall 105 | 106 | grestore 107 | end 108 | } def 109 | 110 | % string radius ptsize findhalfangle halfangle 111 | /findhalfangle { 112 | 4 div add 113 | exch 114 | stringwidth pop 2 div 115 | exch 116 | 2 mul 3.1415926535 mul div 360 mul 117 | } def 118 | 119 | /circtextdict 16 dict def 120 | circtextdict begin 121 | 122 | /outsideplacechar { 123 | /char exch def 124 | /halfangle char radius ptsize findhalfangle def 125 | gsave 126 | halfangle neg rotate 127 | radius 0 translate 128 | -90 rotate 129 | char stringwidth pop 2 div neg 0 moveto 130 | char show 131 | grestore 132 | halfangle 2 mul neg rotate 133 | } def 134 | 135 | end 136 | 137 | %--------------------------------------------------------------------- label -- 138 | LABELFONT 139 | F 140 | 141 | /LABELSIZE LABELFONT exch pop def 142 | /LABELRADIUS LABELSIZE 3 div 1 r add sub neg 1.02 mul def 143 | 144 | 145 | /HALFANGLE 146 | LABEL LABELRADIUS LABELSIZE findhalfangle 147 | HALFDASHANGLE div ceiling HALFDASHANGLE mul 148 | def 149 | 150 | /LABELANGLE 151 | 60 LABELANGLE HALFANGLE sub 152 | lt 153 | { 154 | HALFANGLE 155 | HALFANGLE DASHANGLE div floor DASHANGLE mul 156 | eq 157 | {LABELANGLE DASHANGLE div ceiling DASHANGLE mul} 158 | {LABELANGLE HALFDASHANGLE sub DASHANGLE div round DASHANGLE mul HALFDASHANGLE add} 159 | ifelse 160 | } 161 | {HALFANGLE 60 add} 162 | ifelse 163 | def 164 | 165 | LABELCOLOR 166 | LABEL 167 | LABELSIZE 168 | LABELANGLE 169 | LABELRADIUS 170 | outsidecircletext 171 | 172 | %--------------------------------------------------------------------- orbit -- 173 | ORBITCOLOR 174 | 0.03 setlinewidth 175 | [1 r add 3.1415926535 180 div HALFDASHANGLE mul mul] 0 setdash 176 | newpath 177 | 0 0 178 | 1 r add 179 | 3 copy 180 | 30 181 | LABELANGLE HALFANGLE add 182 | arcn 183 | stroke 184 | 60 185 | LABELANGLE HALFANGLE sub 186 | 2 copy 187 | lt {arc stroke} {4 {pop} repeat} ifelse 188 | 189 | %------------------------------------------------------------------ copyright -- 190 | /COPYRIGHT 191 | (Graphic design by A. Nakonechnyj. Copyright (c) 1998, All rights reserved.) 192 | def 193 | 194 | LABELCOLOR 195 | LOGOFONT 196 | 32 div 197 | F 198 | 2 sqrt 0.99 mul 199 | dup 200 | neg 201 | moveto 202 | COPYRIGHT 203 | 90 rotate 204 | %show 205 | 206 | %---------------------------------------------------------------------- done -- 207 | showpage 208 | 209 | %%Trailer 210 | %%EOF 211 | -------------------------------------------------------------------------------- /doc/cqueues.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wahern/cqueues/8c0142577d3cb1f24917879997678bef0d084815/doc/cqueues.pdf -------------------------------------------------------------------------------- /examples/chat.srv: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- 3 | -- Simple chat server that broadcasts user messages to everybody connected, 4 | -- and accepts simple commands. 5 | -- 6 | -- Provides simple example of combining sockets, condition variables, and a 7 | -- basic flow control methodology. 8 | -- 9 | -- Also provides an example of signal management and a dead-man switch using 10 | -- pre-emptive threads. 11 | -- 12 | local cqueues = require"cqueues" 13 | local errno = require"cqueues.errno" 14 | local socket = require"cqueues.socket" 15 | local signal = require"cqueues.signal" 16 | local thread = require"cqueues.thread" 17 | local condition = require"cqueues.condition" 18 | 19 | local EAGAIN = errno.EAGAIN 20 | local EPIPE = errno.EPIPE 21 | local poll = cqueues.poll 22 | local sleep = cqueues.sleep 23 | local monotime = cqueues.monotime 24 | 25 | 26 | local port = socket.listen("0.0.0.0", ... or 6667) -- IRC port :) 27 | local loop = cqueues.new() 28 | 29 | local MaxIdle = 300 30 | local Streams = { } -- socket -> user mapping 31 | local Users = { } -- nickname -> user mapping 32 | 33 | 34 | -- 35 | -- simple fifo module 36 | -- 37 | -- ------------------------------------------------------------------------ 38 | local fifo = {} 39 | 40 | function fifo.new() 41 | local self = { condvar = condition.new(), count = 0 } 42 | 43 | return setmetatable(self, { __index = fifo }) 44 | end -- fifo.new 45 | 46 | function fifo:put(msg) 47 | local tail = { data = msg } 48 | 49 | if self.tail then 50 | self.tail.next = tail 51 | self.tail = tail 52 | else 53 | self.head = tail 54 | self.tail = tail 55 | end 56 | 57 | self.count = self.count + 1 58 | 59 | self:signal() 60 | end -- fifo:put 61 | 62 | function fifo:get() 63 | if self.head then 64 | local head = self.head 65 | 66 | self.head = head.next 67 | 68 | if not self.head then 69 | self.tail = nil 70 | end 71 | 72 | assert(self.count > 0) 73 | self.count = self.count - 1 74 | 75 | return head.data 76 | end 77 | 78 | assert(self.count == 0) 79 | end -- fifo:get 80 | 81 | function fifo:signal() 82 | self.condvar:signal() 83 | end -- fifo:signal 84 | 85 | function fifo:getcv() 86 | return self.condvar 87 | end -- fifo:getcv 88 | 89 | 90 | -- 91 | -- simple chat session object 92 | -- 93 | -- ------------------------------------------------------------------------ 94 | local chat = {} 95 | 96 | function chat.new(con) 97 | local self = { } 98 | local _, ip, port = con:peername() 99 | 100 | self.socket = con 101 | self.from = string.format("[%s]:%d", ip, port) 102 | self.msgs = fifo.new() 103 | self.eof = false --> flag to tell loop to exit 104 | self.lastt = monotime() --> last transmission time 105 | 106 | return setmetatable(self, { __index = chat }) 107 | end -- chat.new 108 | 109 | function chat:greet() 110 | self.socket:write"# What's your nickname?\n" 111 | 112 | self.nick = assert(self.socket:read"*l") 113 | 114 | self.socket:write(string.format("# Hello %s\n", self.nick)) 115 | self.socket:write"# commands: /die /quit /who\n" 116 | 117 | return self.nick 118 | end -- chat:greet 119 | 120 | function chat:pollfd() 121 | return self.socket:pollfd() 122 | end -- chat:pollfd 123 | 124 | function chat:events() 125 | -- NOTE: this would need to be changed to something more 126 | -- sophisticated if we changed our flow control protocol in 127 | -- chat:loop. 128 | return self.socket:events() 129 | end -- chat:events 130 | 131 | function chat:tryrecv() 132 | repeat 133 | local msg, why = self.socket:recv"*L" 134 | 135 | if msg then 136 | local cmd = string.match(msg, "^/(%w+)") 137 | 138 | if cmd then 139 | cmd = string.lower(cmd) 140 | 141 | if cmd == "quit" then 142 | self.socket:write"# goodbye\n" 143 | self:exit() 144 | 145 | return true 146 | elseif cmd == "who" then 147 | for nick, user in pairs(Users) do 148 | self.socket:write(string.format("# %s from %s\n", nick, user.from)) 149 | end 150 | elseif cmd == "die" then 151 | signal.raise(signal.SIGTERM) 152 | end 153 | else 154 | for _, user in pairs(Users) do 155 | user:put(msg) 156 | end 157 | end 158 | 159 | self.lastt = monotime() 160 | elseif why ~= EAGAIN and why ~= EPIPE then 161 | return false, why 162 | end 163 | until not msg 164 | 165 | return true 166 | end -- chat:tryrecv 167 | 168 | function chat:trysend(flush) 169 | repeat 170 | local msg = self.msgs:get() 171 | 172 | if msg then 173 | self.socket:send(msg, 1, #msg) 174 | end 175 | until not msg 176 | 177 | -- 178 | -- Use a yielding flush (blocking our immediate coroutine) to the 179 | -- socket to simplify things. 180 | -- 181 | -- If not, we'll have to synthesis an event mask by combining the 182 | -- event states after both :recv and :send, which would normally be 183 | -- either "r" if send flushed, or "rw" if send didn't flush. But 184 | -- note that if using SSL, read attempts can trigger writes, and 185 | -- vice-versa, if the transport channel does a key exchange in the 186 | -- middle of the stream. We would then have to be careful about 187 | -- always adding the "r" event, because a malicious client could 188 | -- send data while OpenSSL's state machine is only trying to write 189 | -- data (never reading from the socket), which means we'd constantly 190 | -- be waking up. This is an issue lots of software is probably 191 | -- susceptible to, actually. Could make for a cool presentation at a 192 | -- hacker conference ;) 193 | -- 194 | -- Ultimately, if the client isn't reading our data fast enough, why 195 | -- should we read his? It would complicate our flow control logic, 196 | -- and make SSL event management a headache. 197 | -- 198 | if flush then 199 | local ok, why = self.socket:flush() 200 | 201 | if not ok and why ~= EAGAIN and why ~= EPIPE then 202 | return false, why 203 | end 204 | end 205 | 206 | return true 207 | end -- chat:trysend() 208 | 209 | function chat:loop() 210 | local nick = self:greet() 211 | 212 | Streams[self.socket] = self 213 | 214 | if Users[self.nick] then 215 | Users[self.nick]:put(string.format("# nick %s taken by %s\n", nick, self.from)) 216 | Users[self.nick]:exit() 217 | end 218 | 219 | Users[self.nick] = self 220 | 221 | repeat 222 | -- 223 | -- Do :tryrecv last so we poll on the proper events. 224 | -- :trysend won't return until it's flushed, so we don't 225 | -- care about send events. 226 | -- 227 | -- Loop because :tryrecv may have enqueued a message to 228 | -- ourselves. 229 | -- 230 | repeat 231 | assert(self:trysend(true)) 232 | assert(self:tryrecv()) 233 | until self.msgs.count == 0 or self.eof 234 | 235 | local eof, gone = self.socket:eof() 236 | 237 | if eof or gone then 238 | self:exit() 239 | end 240 | 241 | if monotime() - self.lastt > MaxIdle then 242 | self:exit() 243 | end 244 | 245 | if not self.eof then 246 | poll(self, self.msgs.condvar, 10) 247 | end 248 | until self.eof 249 | 250 | local _, gone = self.socket:eof() 251 | 252 | if not gone then 253 | self:trysend(false) --> send any goodbye messages 254 | end 255 | end -- chat:loop 256 | 257 | function chat:exit(force) 258 | self.eof = true 259 | 260 | self.msgs:signal() 261 | 262 | if force then 263 | self.socket:shutdown"rw" 264 | end 265 | end -- chat:exit 266 | 267 | function chat:close() 268 | Streams[self.socket] = nil 269 | 270 | if Users[self.nick] == self then 271 | Users[self.nick] = nil 272 | end 273 | 274 | self.socket:close() --> descriptors are a precious resource 275 | end -- chat:close 276 | 277 | function chat:put(msg) 278 | self.msgs:put(msg) 279 | end -- chat:put 280 | 281 | 282 | -- 283 | -- Our core block which spawns a new coroutine for each client. 284 | -- 285 | -- ------------------------------------------------------------------------ 286 | loop:wrap(function() 287 | for con in port:clients() do 288 | loop:wrap(function() 289 | local chat = chat.new(con) 290 | 291 | -- don't let buggy code take down whole server 292 | local ok, why = pcall(chat.loop, chat) 293 | 294 | if not ok then 295 | io.stderr:write(string.format("%s: %s\n", chat.from, why)) 296 | end 297 | 298 | chat:close() 299 | end) 300 | end 301 | end) 302 | 303 | 304 | -- 305 | -- Try to handle our signals cleanly by doing managed shutdown. 306 | -- 307 | -- Do signal management in the same POSIX thread as our client loop because 308 | -- the /die command raises SIGTERM, and raise(2) sends the signal to the 309 | -- issuing thread, regardless of signal masks. Linux signalfd and Solaris 310 | -- sigtimedwait won't detect signals sent to another thread. 311 | -- 312 | -- ------------------------------------------------------------------------ 313 | loop:wrap(function() 314 | signal.block(signal.SIGTERM, signal.SIGHUP, signal.SIGINT) 315 | 316 | local sigs = signal.listen(signal.SIGTERM, signal.SIGHUP, signal.SIGINT) 317 | sigs:wait() 318 | 319 | for _, user in pairs(Users) do 320 | user:put"# server exiting\n" 321 | user:exit() 322 | end 323 | 324 | local deadline = monotime() + 1 325 | 326 | while next(Users) and deadline > monotime() do 327 | sleep(0.05) 328 | end 329 | 330 | os.exit(true) 331 | end) 332 | 333 | 334 | -- 335 | -- Implement a dead-man switch in case a bug in our code causes the main 336 | -- loop to stall. 337 | -- 338 | -- ------------------------------------------------------------------------ 339 | loop:wrap(function() 340 | local thr, pipe = thread.start(function(pipe) 341 | local cqueues = require"cqueues" 342 | local sleep = cqueues.sleep 343 | local poll = cqueues.poll 344 | local loop = cqueues.new() 345 | 346 | loop:wrap(function() 347 | sleep(10) 348 | 349 | -- try to drain socket so we don't get stale alive 350 | -- tokens on successive iterations. 351 | while pipe:recv(-32) do 352 | poll(pipe, 10) 353 | end 354 | 355 | io.stderr:write"main thread unresponsive\n" 356 | 357 | os.exit(false) 358 | end) 359 | 360 | local ok, why = loop:loop() 361 | 362 | io.stderr:write(string.format("dead-man thread failed: %s\n", why or "unknown error")) 363 | 364 | os.exit(false) 365 | end) 366 | 367 | loop:wrap(function() 368 | while true do 369 | sleep(5) 370 | assert(pipe:write"!\n") 371 | end 372 | end) 373 | end) 374 | 375 | 376 | -- start our main loop 377 | assert(loop:loop()) 378 | -------------------------------------------------------------------------------- /examples/condition.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- 3 | -- Test for inter-controller signaling. If it works, the inner loops should 4 | -- terminate as soon as we signal the condition variable. Otherwise, they 5 | -- shouldn't terminate till each timeouts in turn. 6 | -- 7 | local cqueues = require"cqueues" 8 | local cond = require"cqueues.condition" 9 | local sleep = cqueues.sleep 10 | local monotime = cqueues.monotime 11 | 12 | local loop = cqueues.new() 13 | local cv = cond.new() 14 | 15 | local function printf(...) print(string.format(...)) end 16 | 17 | local success = true 18 | 19 | for i=1,5 do 20 | loop:wrap(function() 21 | local loop = cqueues.new() 22 | 23 | loop:wrap(function() 24 | local began = monotime() 25 | local okay = cv:wait(3) 26 | 27 | if not okay then 28 | success = false 29 | end 30 | 31 | printf("thread %d woke in %.1fs: %s", i, monotime() - began, (okay and "OK") or "FAIL") 32 | end) 33 | 34 | assert(loop:loop()) 35 | end) 36 | end 37 | 38 | 39 | loop:wrap(function() 40 | print"sleeping..." 41 | sleep(2) 42 | print"signaling..." 43 | cv:signal() 44 | end) 45 | 46 | assert(loop:loop()) 47 | 48 | print((success and "OK") or "FAIL") 49 | -------------------------------------------------------------------------------- /examples/dig.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local cqueues = require"cqueues" 4 | local resolver = require"cqueues.dns.resolver" 5 | local getopt = loadfile"getopt.lua"() 6 | 7 | local usage = string.gsub([[ 8 | dig -rsh [name] [type] 9 | -r recursive lookup beginning from root nameservers 10 | -s smart resolve NS, MX, etc to A records if not present 11 | -h print this usage message 12 | 13 | Report bugs to 14 | ]], "^[\t\n]", ""):gsub("\n[\t]", "\n") 15 | 16 | local args = {} 17 | local opts = {} 18 | 19 | for optnam, optval in getopt("rsh", ...) do 20 | if optnam then 21 | if optnam == "r" then 22 | opts.r = true 23 | elseif optnam == "s" then 24 | opts.s = true 25 | elseif optnam == "h" then 26 | io.stdout:write(usage) 27 | os.exit() 28 | else 29 | if optnam == "?" then 30 | io.stderr:write("invalid option: " .. optval .. "\n") 31 | end 32 | 33 | io.stderr:write(usage) 34 | os.exit(false) 35 | end 36 | else 37 | args[#args + 1] = optval 38 | end 39 | end 40 | 41 | local name = args[1] or "google.com" 42 | local type = args[2] or "A" 43 | 44 | assert(cqueues.new():wrap(function() 45 | local init = (opts.r and resolver.root) or resolver.stub 46 | local res = init{ smart = opts.s } 47 | 48 | print(tostring(assert(res:query(name, type)))) 49 | 50 | local st = res:stat() 51 | 52 | print(string.format(";; queries: %d", st.queries)) 53 | print(string.format(";; udp sent: %d in %d bytes", st.udp.sent.count, st.udp.sent.bytes)) 54 | print(string.format(";; udp rcvd: %d in %d bytes", st.udp.rcvd.count, st.udp.rcvd.bytes)) 55 | print(string.format(";; tcp sent: %d in %d bytes", st.tcp.sent.count, st.tcp.sent.bytes)) 56 | print(string.format(";; tcp rcvd: %d in %d bytes", st.tcp.rcvd.count, st.tcp.rcvd.bytes)) 57 | end):loop()) 58 | 59 | -------------------------------------------------------------------------------- /examples/echo.srv: -------------------------------------------------------------------------------- 1 | #!/usr/local/lua52/bin/lua 2 | -- 3 | -- echo.srv [bind-address [[bind-port] [wait-timeout]]] 4 | -- 5 | local cqueues = require("cqueues") 6 | local socket = require("cqueues.socket") 7 | local bind, port, wait = ... 8 | 9 | local srv = socket.listen(bind or "127.0.0.1", tonumber(port or 8000)) 10 | 11 | local loop = cqueues.new() 12 | 13 | loop:wrap(function() 14 | for con in srv:clients(wait) do 15 | loop:wrap(function() 16 | for ln in con:lines("*L") do 17 | con:write(ln) 18 | end 19 | 20 | con:shutdown("w") 21 | end) 22 | end 23 | end) 24 | 25 | while not loop:empty() do 26 | local ok, err = loop:step() 27 | if not ok then error("loop.step: " .. err) end 28 | end 29 | -------------------------------------------------------------------------------- /examples/getopt.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- getopt(":a:b", ...) -- works just like getopt(3). 3 | -- 4 | -- Send bug reports to william@25thandClement.com. 5 | -- 6 | local function getopt(optstring, ...) 7 | local opts = { } 8 | local args = { ... } 9 | 10 | for optc, optv in optstring:gmatch"(%a)(:?)" do 11 | opts[optc] = { hasarg = optv == ":" } 12 | end 13 | 14 | return coroutine.wrap(function() 15 | local yield = coroutine.yield 16 | local i = 1 17 | 18 | while i <= #args do 19 | local arg = args[i] 20 | 21 | i = i + 1 22 | 23 | if arg == "--" then 24 | break 25 | elseif arg:sub(1, 1) == "-" then 26 | for j = 2, #arg do 27 | local opt = arg:sub(j, j) 28 | 29 | if opts[opt] then 30 | if opts[opt].hasarg then 31 | if j == #arg then 32 | if args[i] then 33 | yield(opt, args[i]) 34 | i = i + 1 35 | elseif optstring:sub(1, 1) == ":" then 36 | yield(':', opt) 37 | else 38 | yield('?', opt) 39 | end 40 | else 41 | yield(opt, arg:sub(j + 1)) 42 | end 43 | 44 | break 45 | else 46 | yield(opt, false) 47 | end 48 | else 49 | yield('?', opt) 50 | end 51 | end 52 | else 53 | yield(false, arg) 54 | end 55 | end 56 | 57 | for i = i, #args do 58 | yield(false, args[i]) 59 | end 60 | end) 61 | end 62 | 63 | return getopt 64 | -------------------------------------------------------------------------------- /examples/httpd.srv: -------------------------------------------------------------------------------- 1 | #!/usr/local/lua52/bin/lua 2 | -- 3 | -- Very simple HTTP server. 4 | -- 5 | -- httpd.srv [bind-address [bind-port]] 6 | -- 7 | local cqueues = require"cqueues" 8 | local socket = require"cqueues.socket" 9 | local errno = require"cqueues.errno" 10 | local bind, port = ... 11 | local verbose = false --true 12 | 13 | 14 | local function fmt(...) 15 | return string.format(...) 16 | end 17 | 18 | local info 19 | 20 | if verbose then 21 | info = function(...) 22 | io.stderr:write(fmt(...), "\n") 23 | end 24 | else 25 | info = function() 26 | return true 27 | end 28 | end 29 | 30 | local function warn(...) 31 | io.stderr:write(fmt(...), "\n") 32 | end 33 | 34 | 35 | local mime = { 36 | html = "text/html", 37 | xml = "application/xml", 38 | css = "text/css", 39 | js = "text/javascript", 40 | txt = "text/plain", 41 | gif = "image/gif", 42 | png = "image/png", 43 | jpg = "image/jpeg", 44 | } 45 | 46 | 47 | local srv = socket.listen(bind or "127.0.0.1", tonumber(port or 8000)) 48 | 49 | local loop = cqueues.new() 50 | 51 | loop:wrap(function() 52 | local count = 0 53 | 54 | for con in srv:clients() do 55 | count = count + 1 56 | 57 | local id = count 58 | 59 | loop:wrap(function() 60 | local _, ip, port = con:peername() 61 | 62 | info("%s:%d: connected", ip, port) 63 | 64 | local ok, why = pcall(function() 65 | con:setmode("tl", "tf") 66 | 67 | local get, why = con:read"*l" 68 | 69 | if not get then 70 | warn("%s:%d: no request (%s)", ip, port, errno.strerror(why)) 71 | 72 | return 73 | end 74 | 75 | info("%s:%d: %s", ip, port, get) 76 | 77 | local hdr = {} 78 | 79 | for h in con:lines"*h" do 80 | local f, b = string.match(h, "^([^:%s]+)%s*:%s*(.*)$") 81 | hdr[f] = b 82 | end 83 | 84 | con:read"*l" -- discard header/body break 85 | 86 | local path = string.match(get, "^%w+%s+/?([^%s]+)") or "/dev/null" 87 | 88 | if not path then 89 | warn("%s:%d: no path specified", ip, port) 90 | 91 | return 92 | end 93 | 94 | local file = io.open(path, "r") 95 | 96 | if file then 97 | con:write"HTTP/1.0 200 OK\n" 98 | con:write"Connection: close\n" 99 | 100 | local ext = string.match(path, "(%w+)$") or "" 101 | local ctype = mime[string.lower(ext)] or "application/octet-stream" 102 | 103 | con:write("Content-Type: ", ctype, "\n\n") 104 | 105 | if not string.match(ctype, "^text/") then 106 | con:setmode(nil, "bf") 107 | end 108 | 109 | for blk in file:lines(1024) do 110 | con:write(blk) 111 | end 112 | 113 | file:close() 114 | else 115 | con:write"HTTP/1.0 404 Not Found\n" 116 | con:write"Connection: close\n\n" 117 | end 118 | 119 | local ok, why = con:flush() 120 | 121 | if not ok then 122 | warn("%s:%d: cannot flush (%s)", ip, port, errno.strerror(why)) 123 | end 124 | 125 | con:shutdown"w" 126 | 127 | local junk = con:read(1) -- wait for EOF 128 | 129 | if junk then 130 | warn("%s:%d: spurious data", ip, port) 131 | end 132 | 133 | con:shutdown"r" 134 | end) 135 | 136 | if ok then 137 | info("%s:%d: disconnected", ip, port) 138 | else 139 | warn("%s:%d: %s", ip, port, why) 140 | end 141 | 142 | con:close() 143 | end) 144 | end 145 | end) 146 | 147 | while not loop:empty() do 148 | local ok, err = loop:step() 149 | if not ok then error("loop.step: " .. err) end 150 | end 151 | -------------------------------------------------------------------------------- /examples/notify.lua: -------------------------------------------------------------------------------- 1 | #!/usr/local/lua52/bin/lua 2 | 3 | local cqueues = require("cqueues") 4 | local notify = require("cqueues.notify") 5 | local path = ... or "/tmp" 6 | 7 | 8 | -- 9 | -- list kernel capabilities 10 | -- 11 | -- local f = {} 12 | -- 13 | -- for flag in notify.flags(notify.FEATURES) do 14 | -- f[#f + 1] = notify[flag] 15 | -- end 16 | -- 17 | -- io.stderr:write("using ", table.concat(f, ", "), "\n") 18 | 19 | 20 | -- 21 | -- initialize our directory notifier 22 | -- 23 | local nfy = notify.opendir(path, notify.ALL) 24 | 25 | local function addall(name, ...) 26 | if name then 27 | nfy:add(name, notify.ALL) 28 | addall(...) 29 | end 30 | end 31 | 32 | addall(select(2, ...)) 33 | 34 | 35 | -- 36 | -- create controller and loop over file change notifications 37 | -- 38 | local cq = cqueues.new() 39 | 40 | cq:wrap(function() 41 | for flags, name in nfy:changes() do 42 | for flag in notify.flags(flags) do 43 | print(name, notify[flag]) 44 | end 45 | end 46 | end) 47 | 48 | 49 | while not cq:empty() do 50 | local okay, why = cq:step() 51 | 52 | if not okay then 53 | error("cqueue: " .. why) 54 | end 55 | end 56 | 57 | -------------------------------------------------------------------------------- /examples/peereid.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local cqueues = require"cqueues" 4 | local socket = require"cqueues.socket" 5 | 6 | local path = "./peereid.sock" 7 | 8 | local unix = socket.listen{ path = path, unlink = true } 9 | local loop = cqueues.new() 10 | 11 | loop:wrap(function() 12 | local con = unix:accept() 13 | local pid, uid, gid = con:peerpid(), con:peereid() 14 | 15 | print(string.format("pid:%s uid:%s gid:%s", pid, uid, gid)) 16 | end) 17 | 18 | loop:wrap(function() 19 | local con = socket.connect{ path = path } 20 | 21 | con:connect() 22 | end) 23 | 24 | -- make sure we delete the socket 25 | local ok, why = loop:loop() 26 | os.remove(path) 27 | assert(ok, why) 28 | -------------------------------------------------------------------------------- /examples/promise.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local cqueues = require"cqueues" 4 | local promise = require"cqueues.promise" 5 | local resolver = require"cqueues.dns.resolver" 6 | local auxlib = require"cqueues.auxlib" 7 | 8 | local host, type = ... 9 | 10 | assert(cqueues.new():wrap(function () 11 | -- use fully recursive resolver to make sure it takes awhile 12 | local pkt = promise.new(function (host, type) 13 | return resolver.root():query(host, type) 14 | end, host or "parliament.uk", type or "MX") 15 | 16 | print(auxlib.tostring(pkt)) 17 | end):loop()) 18 | -------------------------------------------------------------------------------- /examples/signal.pause: -------------------------------------------------------------------------------- 1 | #!/usr/local/lua52/bin/lua 2 | -- 3 | -- Solaris has no analog to *BSD EVFILT_SIGNAL or Linux signalfd. Rather 4 | -- than using tricks (signal handlers + pipes, or thread + sigwait + pipe) 5 | -- which create troublesome global state, especially on fork, the default 6 | -- signal listener implementation just uses a short timeout for Solaris. 7 | -- 8 | -- Most of the time signals are used for simple process management. The most 9 | -- portable solution for immediate signal response I can come up with is to 10 | -- use two signals. One signal will be used to interrupt the process, and 11 | -- another for queueing and retrieval. (Note that you can't do both. Neither 12 | -- blocked nor ignored signals guarantee a syscall interrupt.) We install a 13 | -- dummy handler for the interrupt signal, and block both signals. Then we 14 | -- use pselect (cqueue:pause()) to wait around for either I/O or our 15 | -- interrupt. When we wake up, we check for any queued signals and process 16 | -- any I/O. 17 | -- 18 | -- There's one more caveat, however. pselect is broken on OS X. Therefore, 19 | -- for the most portable solution we use both methods: polling a signal 20 | -- listener descriptor, and a loop using pselect(). 21 | -- 22 | -- If all is working well, then the following script should print "sent 23 | -- SIGTERM" and "rcvd SIGTERM" in rapid succession on all supported 24 | -- platforms. 25 | -- 26 | local cqueues = require("cqueues") 27 | local signal = require("cqueues.signal") 28 | 29 | local cq = cqueues.new() 30 | 31 | local sl = signal.listen(signal.SIGTERM, signal.SIGINT) 32 | 33 | signal.discard(signal.SIGINT) 34 | signal.block(signal.SIGTERM, signal.SIGINT) 35 | 36 | 37 | cq:wrap(function() 38 | -- Change the default Solaris timeout so we can confirm we're waking 39 | -- up from an interrupt, and not just timing out. 40 | sl:settimeout(30) 41 | 42 | while true do 43 | sl:wait() 44 | end 45 | end) 46 | 47 | 48 | cq:wrap(function() 49 | local i = 0 50 | 51 | while true do 52 | cqueues.sleep(2) 53 | 54 | i = i + 1 55 | 56 | if i > 2 then 57 | os.exit() 58 | end 59 | 60 | signal.raise(signal.SIGTERM) 61 | print("sent", signal.SIGTERM, signal[signal.SIGTERM]) 62 | signal.raise(signal.SIGINT) 63 | end 64 | end) 65 | 66 | 67 | while not cq:empty() do 68 | local signo = sl:wait(0.0) 69 | 70 | if signo then print("rcvd", signo, signal[signo]) end 71 | 72 | local ok, err = cq:step(0.0) 73 | if not ok then error(err) end 74 | cq:pause(signal.SIGINT) 75 | end 76 | 77 | -------------------------------------------------------------------------------- /examples/signal.wait: -------------------------------------------------------------------------------- 1 | #!/usr/local/lua52/bin/lua 2 | 3 | local cqueues = require("cqueues") 4 | local signal = require("cqueues.signal") 5 | 6 | local cq = cqueues.new() 7 | 8 | local sl = signal.listen(signal.SIGTERM, signal.SIGINT) 9 | 10 | --signal.ignore(signal.SIGTERM) 11 | signal.block(signal.SIGTERM, signal.SIGINT) 12 | 13 | 14 | cq:wrap(function() 15 | local signo 16 | 17 | while true do 18 | signo = sl:wait(0.5) 19 | 20 | print(signo, signal[signo]) 21 | 22 | if signo == signal.SIGINT then 23 | os.exit(true) 24 | end 25 | end 26 | end) 27 | 28 | 29 | cq:wrap(function() 30 | while true do 31 | cqueues.sleep(1) 32 | signal.raise(signal.SIGTERM) 33 | end 34 | end) 35 | 36 | 37 | while not cq:empty() do 38 | local ok, err = cq:step() 39 | if not ok then error(err) end 40 | end 41 | -------------------------------------------------------------------------------- /examples/simple.get: -------------------------------------------------------------------------------- 1 | #!/usr/local/lua52/bin/lua 2 | 3 | local cqueues = require("cqueues") 4 | local socket = require("cqueues.socket") 5 | local host, port = ... 6 | 7 | host = host or "google.com" 8 | port = tonumber(port or 80) 9 | 10 | local http = socket.connect(host, port) 11 | 12 | if port == 443 then 13 | http:starttls() 14 | end 15 | 16 | local cq = cqueues.new() 17 | 18 | cq:wrap(function() 19 | http:write("GET / HTTP/1.0\n") 20 | http:write(string.format("Host: %s:%d\n\n", host, port)) 21 | 22 | local status = http:read() 23 | print("!", status) 24 | 25 | for ln in http:lines"*h" do 26 | print("|", ln) 27 | end 28 | 29 | local empty = http:read"*L" 30 | 31 | if empty and empty:match"^[^\r\n]" then 32 | http:unget(empty) 33 | end 34 | 35 | print"~" 36 | 37 | for ln in http:lines"*L" do 38 | io.stdout:write(ln) 39 | end 40 | 41 | http:close() 42 | end) 43 | 44 | while not cq:empty() do 45 | local ok, err = cq:step() 46 | 47 | if not ok then 48 | error("cqueue: " .. err) 49 | end 50 | end 51 | 52 | -------------------------------------------------------------------------------- /examples/socket.pair: -------------------------------------------------------------------------------- 1 | #!/usr/local/lua52/bin/lua 2 | 3 | local cqueues = require("cqueues") 4 | local socket = require("cqueues.socket") 5 | 6 | local snd, rcv = socket.pair("stream") 7 | 8 | local cq = cqueues.new() 9 | 10 | 11 | cq:wrap(function() 12 | for i = 1, 10 do 13 | snd:write(tostring(i), "\n") 14 | end 15 | 16 | snd:shutdown("w") 17 | end) 18 | 19 | cq:wrap(function() 20 | for i in rcv:lines() do 21 | print(tostring(i)) 22 | end 23 | end) 24 | 25 | 26 | while not cq:empty() do 27 | local ok, err = cq:step() 28 | 29 | if not ok then 30 | error("cqueue: " .. err) 31 | end 32 | end 33 | 34 | -------------------------------------------------------------------------------- /examples/socket.xchg: -------------------------------------------------------------------------------- 1 | #!/usr/local/lua52/bin/lua 2 | 3 | local cqueues = require("cqueues") 4 | local socket = require("cqueues.socket") 5 | 6 | local snd, rcv = socket.pair("stream") 7 | 8 | local cq = cqueues.new() 9 | 10 | local NLOOP = 1 11 | 12 | cq:wrap(function() 13 | for i = 1, NLOOP do 14 | local con, s = socket.pair("stream") 15 | local ok, err = snd:sendfd(os.date(), s) 16 | 17 | if not ok then error(err) end 18 | 19 | for i = 1, 10 do 20 | con:pack(i, 32) 21 | end 22 | 23 | con:flush() 24 | con:shutdown"w" 25 | 26 | -- Handle OS X bug 27 | cq:wrap(function() 28 | con:read() 29 | end) 30 | end 31 | end) 32 | 33 | cq:wrap(function() 34 | for i = 1, NLOOP do 35 | local msg, con, err = rcv:recvfd(512) 36 | 37 | if not msg then error(err) end 38 | 39 | print(msg) 40 | 41 | for i in function() return con:unpack(32) end do 42 | print(tostring(i)) 43 | end 44 | 45 | con:shutdown"w" 46 | end 47 | end) 48 | 49 | 50 | while not cq:empty() do 51 | local ok, err = cq:step() 52 | 53 | if not ok then 54 | error("cqueue: " .. err) 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /examples/thread.count: -------------------------------------------------------------------------------- 1 | #!/usr/local/lua52/bin/lua 2 | 3 | local cqueues = require"cqueues" 4 | local thread = require"cqueues.thread" 5 | 6 | local thr, con = thread.start(function(con, i, j) 7 | local cqueues = require"cqueues" 8 | local cq = cqueues.new() 9 | 10 | cq:wrap(function() 11 | for n = tonumber(i), tonumber(j) do 12 | io.stderr:write("sent ", n, "\n") 13 | con:write(n, "\n") 14 | cqueues.sleep(0.1) 15 | end 16 | end) 17 | 18 | while not cq:empty() do 19 | local ok, why = cq:step() 20 | if not ok then error(why) end 21 | end 22 | end, 0, 9) 23 | 24 | 25 | local cq = cqueues.new() 26 | 27 | cq:wrap(function() 28 | for ln in con:lines() do 29 | io.stdout:write(ln, " rcvd", "\n") 30 | end 31 | 32 | local ok, why = thr:join() 33 | 34 | if ok then 35 | print(why or "OK") 36 | else 37 | error(require"cqueues.errno".strerror(why)) 38 | end 39 | end) 40 | 41 | while not cq:empty() do 42 | local ok, err = cq:step() 43 | 44 | if err then print(err) end 45 | end 46 | -------------------------------------------------------------------------------- /mk/changelog: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e # strict errors 3 | set -u # don't expand unbound variables 4 | set -f # disable pathname expansion 5 | set -C # noclobber 6 | \unalias -a # no command surprises 7 | export LC_ALL=C # no locale headaches 8 | unset IFS # no field splitting surprises 9 | 10 | 11 | RELPATH=${0%/*} 12 | : RELPATH=${RELPATH:-.} 13 | 14 | CHANGELOG=${RELPATH}/../debian/changelog 15 | ROOTDIR=${RELPATH}/.. 16 | 17 | 18 | changelog() { 19 | if [ ! -f ${CHANGELOG} ]; then 20 | printf -- "${CHANGELOG}: No such file\n" >&2 21 | exit 1 22 | fi 23 | 24 | cat ${CHANGELOG} 25 | } 26 | 27 | 28 | usage() { 29 | cat <<-EOF 30 | usage: ${0##*/} [-h] version|author|commit 31 | -h print this usage message 32 | 33 | version most recent package version number 34 | author author of most recent log message 35 | commit Git hash of most recent commit 36 | 37 | Report bugs to 38 | EOF 39 | } 40 | 41 | while getopts h OPT; do 42 | case "${OPT}" in 43 | h) 44 | usage 45 | exit 0 46 | ;; 47 | *) 48 | usage >&2 49 | exit 1 50 | ;; 51 | esac 52 | done 53 | 54 | shift $(($OPTIND - 1)) 55 | 56 | 57 | case "${1:-version}" in 58 | version) 59 | changelog | sed -ne ' 60 | s/.*(\([1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]\).*).*/\1/ 61 | t Found 62 | d 63 | :Found 64 | p 65 | q 66 | ' 67 | ;; 68 | author) 69 | changelog | sed -ne ' 70 | s/.*<\([^>]*@[^>]*\)>.*/\1/ 71 | t Found 72 | d 73 | :Found 74 | p 75 | q 76 | ' 77 | ;; 78 | commit) 79 | GIT="$(command -v git)" 80 | test -n "${GIT}" -a -d ${ROOTDIR}/.git || exit 1 81 | 82 | cd ${ROOTDIR} 83 | ${GIT} show --pretty='%H' HEAD 2>/dev/null | sed -n '1p' 84 | ;; 85 | *) 86 | usage >&2 87 | exit 1 88 | ;; 89 | esac 90 | 91 | exit 0 92 | -------------------------------------------------------------------------------- /mk/errno.ls: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # List all EXXXX macros 4 | # 5 | 6 | set -e 7 | 8 | : ${CC:=cc} 9 | : ${xflag:=} 10 | : ${ELAST=no} 11 | 12 | 13 | usage() { 14 | cat <<-EOF 15 | usage: $(basename $0) -lxh 16 | -l print or compute ELAST value 17 | -x expand macros 18 | -h print this usage message 19 | 20 | Report bugs to 21 | EOF 22 | } 23 | 24 | while getopts lxh OPT; do 25 | case "${OPT}" in 26 | l) 27 | if [ -n "${xflag}" ]; then 28 | echo "$0: -l and -x are exclusive" >&2 29 | usage >&2 30 | exit 1 31 | fi 32 | 33 | ELAST=yes 34 | ;; 35 | x) 36 | if [ "${ELAST}" != "no" ]; then 37 | echo "$0: -l and -x are exclusive" >&2 38 | usage >&2 39 | exit 1 40 | fi 41 | 42 | xflag="-x" 43 | ;; 44 | h) 45 | usage 46 | exit 0 47 | ;; 48 | *) 49 | usage >&2 50 | exit 1 51 | ;; 52 | esac 53 | done 54 | 55 | 56 | posix() { 57 | cat <<-EOF | tr " " "\n" 58 | E2BIG EACCES EADDRINUSE EADDRNOTAVAIL EAFNOSUPPORT EAGAIN 59 | EWOULDBLOCK EALREADY EBADF EBADMSG EBUSY ECANCELED ECHILD 60 | ECONNABORTED ECONNREFUSED ECONNRESET EDEADLK EDESTADDRREQ 61 | EDOM EDQUOT EEXIST EFAULT EFBIG EHOSTUNREACH EIDRM EILSEQ 62 | EINPROGRESS EINTR EINVAL EIO EISCONN EISDIR ELOOP EMFILE 63 | EMLINK EMSGSIZE EMULTIHOP ENAMETOOLONG ENETDOWN ENETRESET 64 | ENETUNREACH ENFILE ENOBUFS ENODATA ENODEV ENOENT ENOEXEC 65 | ENOLCK ENOLINK ENOMEM ENOMSG ENOPROTOOPT ENOSPC ENOSR ENOSTR 66 | ENOSYS ENOTCONN ENOTDIR ENOTEMPTY ENOTRECOVERABLE ENOTSOCK 67 | ENOTSUP EOPNOTSUPP ENOTTY ENXIO EOPNOTSUPP ENOTSUP EOVERFLOW 68 | EOWNERDEAD EPERM EPIPE EPROTO EPROTONOSUPPORT EPROTOTYPE 69 | ERANGE EROFS ESPIPE ESRCH ESTALE ETIME ETIMEDOUT ETXTBSY 70 | EWOULDBLOCK EAGAIN EXDEV 71 | EOF 72 | } 73 | 74 | errno_h() { 75 | posix | env PATH="$(dirname $0):${PATH}" CC="${CC}" macros.ls -i "errno.h" -m "^E" -s - ${xflag} 76 | } 77 | 78 | 79 | if [ "${ELAST}" = "yes" ]; then 80 | xflag="" 81 | (echo "#include "; errno_h) | ${CC} -E - | \ 82 | sed -ne 's/^[ ]*\([0123456789][0123456789]*\).*$/\1/p' | sort -n | tail -1 83 | else 84 | errno_h 85 | fi 86 | -------------------------------------------------------------------------------- /mk/macros.ls: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # List specified macros 4 | # 5 | 6 | set -e 7 | 8 | : ${CC:=cc} 9 | : ${CPPFLAGS:=} 10 | : ${MACRO:=.} 11 | : ${VENDOR:=} 12 | : ${INCLUDE:=} 13 | : ${TMPFILE:=.$(basename $0).c} 14 | : ${PREPEND:=} 15 | : ${EXPAND:=no} 16 | 17 | NL=" 18 | " 19 | 20 | usage() { 21 | cat <<-EOF 22 | usage: $(basename $0) -m:v:i:t:xh 23 | -i INCLUDE include file (e.g. ) 24 | -m REGEX regular expression macro name filter 25 | -s PATH path to supplemental list, one per line 26 | -t TMPFILE compiler intermediate tmp file 27 | -v VENDOR compiler vendor name (e.g. gcc, clang, sunpro) 28 | -x expand macros 29 | -h print this usage message 30 | 31 | Report bugs to 32 | EOF 33 | } 34 | 35 | while getopts i:m:s:t:v:xh OPT; do 36 | case "${OPT}" in 37 | i) 38 | case "${OPTARG}" in 39 | \<*) 40 | INCLUDE="${INCLUDE}#include ${OPTARG}${NL}" 41 | ;; 42 | \"*) 43 | INCLUDE="${INCLUDE}#include ${OPTARG}${NL}" 44 | ;; 45 | *) 46 | INCLUDE="${INCLUDE}#include <${OPTARG}>${NL}" 47 | ;; 48 | esac 49 | ;; 50 | m) 51 | MACRO="${OPTARG}" 52 | ;; 53 | s) 54 | PREPEND="${OPTARG}" 55 | ;; 56 | t) 57 | TMPFILE="${OPTARG}" 58 | ;; 59 | v) 60 | VENDOR="${OPTARG}" 61 | ;; 62 | x) 63 | EXPAND=yes 64 | ;; 65 | h) 66 | usage 67 | exit 0 68 | ;; 69 | *) 70 | usage >&2 71 | exit 1 72 | ;; 73 | esac 74 | done 75 | 76 | 77 | vendor() { 78 | SCRIPT="$(dirname $0)/vendor.cc" 79 | 80 | if [ -n "${SCRIPT}" -a -x "${SCRIPT}" ]; then 81 | env CC="${CC}" ${SCRIPT} 82 | else 83 | ${CC} -E - <<-EOF | awk '/sunpro/||/clang/||/gcc/||/other/{ print $1; exit; }' 84 | #if defined __SUNPRO_C 85 | sunpro 86 | #elif defined __clang__ 87 | clang 88 | #elif defined __GNUC__ 89 | gcc 90 | #else 91 | other 92 | #endif 93 | EOF 94 | fi 95 | } 96 | 97 | 98 | filter() { 99 | awk "\$1~/^#define/ && \$2~/${MACRO}/{ print \$2 }" 100 | } 101 | 102 | 103 | macros() { 104 | if [ -n "${PREPEND}" ]; then 105 | cat "${PREPEND}" 106 | fi 107 | 108 | case "${VENDOR:-$(vendor)}" in 109 | *sunpro*) 110 | trap "rm -f ${TMPFILE}" EXIT 111 | echo "${INCLUDE}" >| ${TMPFILE} 112 | ${CC} ${CPPFLAGS} -xM ${TMPFILE} | awk '/\.h$/{ print $2 }' | sort -u | xargs cat | filter 113 | rm ${TMPFILE} 114 | ;; 115 | *) 116 | echo "${INCLUDE}" | ${CC} ${CPPFLAGS} -dM -E - | filter 117 | ;; 118 | esac 119 | } 120 | 121 | expand() { 122 | if [ "${EXPAND}" = "yes" ]; then 123 | (echo "${INCLUDE}"; awk '{ print "\"<<<< "$1" >>>>\" "$1 }') | ${CC} ${CPPFLAGS} -E - | awk '$1~/^"<<<>>>") + 5) }' 124 | else 125 | cat 126 | fi 127 | } 128 | 129 | macros | sort -u | expand 130 | -------------------------------------------------------------------------------- /mk/vendor.cc: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | : ${CC:=cc} 6 | 7 | ${CC} -E - <<-EOF | awk '/sunpro/||/clang/||/gcc/||/other/{ print $1; exit; }' 8 | #if defined __SUNPRO_C 9 | sunpro 10 | #elif defined __clang__ 11 | clang 12 | #elif defined __GNUC__ 13 | gcc 14 | #else 15 | other 16 | #endif 17 | EOF 18 | -------------------------------------------------------------------------------- /mk/vendor.os: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | uname -s 4 | -------------------------------------------------------------------------------- /regress/102-getlookup-getsearch.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | local config = require"cqueues.dns.config" 9 | local txt = io.tmpfile() 10 | 11 | assert(txt:write([[ 12 | search google.com yahoo.com wikipedia.org 13 | nameserver 8.8.8.8 14 | ]])) 15 | 16 | local resconf = config.new() 17 | resconf:loadfile(txt) 18 | 19 | for i,dn in ipairs(resconf:getsearch()) do 20 | info("search[%d]: %s", i, dn) 21 | end 22 | 23 | for i,how in ipairs(resconf:getlookup()) do 24 | info("lookup[%d]: %s", i, how) 25 | end 26 | 27 | say("OK") 28 | -------------------------------------------------------------------------------- /regress/125-thread.current-not-cleared-on-error.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | local cq = cqueues.new() 9 | local is_ok = false 10 | cq:wrap(function() 11 | cq:wrap(function() 12 | is_ok = true 13 | end) 14 | error("an error", 0) 15 | end) 16 | 17 | -- First step should signal an error 18 | local ok, err = cq:step() 19 | check(not ok and err == "an error", err) 20 | 21 | -- Second step should succeed 22 | check(cq:step()) 23 | check(is_ok, "second thread not run") 24 | say("OK") 25 | -------------------------------------------------------------------------------- /regress/130-segfault-on-null-cstack.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | runlua "$0" "$@" 5 | RC="$?" 6 | if [ ${RC} -eq 0 ]; then 7 | case ${REGRESS_VERBOSE} in 8 | 0) 9 | ;; 10 | 1) 11 | printf "\n%s: OK\n" "${REGRESS_PROGNAME}" 12 | ;; 13 | *) 14 | printf "%s: OK\n" "${REGRESS_PROGNAME}" 15 | ;; 16 | esac 17 | fi 18 | exit ${RC} 19 | ]] 20 | require"regress".export".*" 21 | 22 | -- 23 | -- Issue #130 -- Segfault due to passing NULL cstack to cstack_isrunning from 24 | -- condition variable __gc 25 | -- 26 | -- cqueue_destroy only cleaned up threads on the polling queue, not the 27 | -- pending queue. If a thread was waiting on a condition variable, but was 28 | -- made pending for another reason (e.g. timeout or other event), then the 29 | -- condition variable <-> controller association would still be installed, 30 | -- holding a reference to the controller. If the controller was garbage 31 | -- collected with the thread still on the pending queue and before the 32 | -- condition variable was collected (as it could be if both were destroyed 33 | -- in the same cycle, like when both survive until Lua VM destruction time), 34 | -- then the condition variable destructor cond__gc would attempt to access a 35 | -- pointer to a defunct controller object via the wakecb structure. In this 36 | -- particular case, one of the results was a NULL pointer dereference. 37 | -- 38 | -- The fix was to walk both the thread.polling _and_ thread.pending queues, 39 | -- calling thread_del. thread_del breaks any wakecb associations. 40 | -- 41 | 42 | -- stop GC to ensure everything destroyed on exit in same cycle 43 | info"stopping GC" 44 | collectgarbage"stop" 45 | 46 | 47 | -- instantiate first so cond__gc invoked after cqueue__gc (only required for 48 | -- 5.2 and 5.3). 49 | local cv1 = check(condition.new()) 50 | local cv2 = check(condition.new()) 51 | local cq = cqueues.new() 52 | 53 | local ready = false 54 | 55 | cq:wrap(function() 56 | info"starting thread 1" 57 | 58 | ready = true 59 | 60 | -- one condvar to wake us; another to ensure we still 61 | -- have a wakecb installed 62 | info"thread 1 polling" 63 | cqueues.poll(cv1, cv2) 64 | end) 65 | 66 | check(cq:step(), "thread 1 failed to start") 67 | 68 | cq:wrap(function() 69 | info"starting thread 2" 70 | 71 | -- put 1st thread in pending state 72 | info"putting thread 1 into pending state" 73 | check(ready, "thread 1 not ready") 74 | cv1:signal() 75 | 76 | -- short-circuit loop so 2nd thread is never run 77 | info"short-circuiting loop" 78 | error"oops" 79 | end) 80 | 81 | 82 | check(not cq:loop(), "loop was expected to fail") 83 | -------------------------------------------------------------------------------- /regress/141-segfault-on-accept.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | -- 7 | -- Issue #141 -- Lua/C accept method binding doesn't check whether the 8 | -- socket has already been closed, passing a NULL socket object to low-level 9 | -- so_accept routine. Fix was to use the lso_checkself utility routine like 10 | -- every other method, which will throw an error when passed a closed 11 | -- socket. 12 | -- 13 | require"regress".export".*" 14 | 15 | info"opening listening socket" 16 | local con = socket.listen("localhost", 0) 17 | 18 | info"calling close" 19 | con:close(); 20 | 21 | -- bug caused NULL dereference in so_clear, invoked from lso_accept 22 | info"calling accept" 23 | local ok = pcall(con.accept, con, 0); 24 | check(not ok, "con:accept didn't throw error as expected") 25 | 26 | say("OK") 27 | 28 | -------------------------------------------------------------------------------- /regress/145-assertion-on-dead.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | local co = coroutine.create(function() 9 | coroutine.yield() 10 | end) 11 | coroutine.resume(co) -- kick off coroutine 12 | coroutine.resume(co) -- resume a yield with no arguments 13 | 14 | local status = coroutine.status(co) 15 | check(status == "dead", "expected dead coroutine (got %q)", status) 16 | 17 | local cq = require"cqueues".new() 18 | cq:attach(co) 19 | cq:step() -- previously would trigger C assert and abort process 20 | 21 | say"OK" 22 | -------------------------------------------------------------------------------- /regress/152-thread-integer-passing.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua -r5.3- "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | local function runtest(testf, ...) 9 | -- check that the test runs locally 10 | info("running test in main thread") 11 | local err = packerror(testf(...)) 12 | if err then 13 | return nil, err:unpack() 14 | end 15 | 16 | -- check test when running from a thread 17 | info("running test in separate thread") 18 | local err, thr = packerror(thread.start(function (_, testf, ...) 19 | require"regress".export".*" 20 | 21 | testf = check(load(testf)) 22 | local err = packerror(testf(...)) 23 | check(not err, "%s", tostring(err)) 24 | end, string.dump(testf), ...)) 25 | if err then 26 | return nil, err:unpack() 27 | end 28 | 29 | local function joinresult(ok, ...) 30 | if not ok then 31 | return nil, ... 32 | elseif (...) then 33 | return fileresult(nil, ...) 34 | else 35 | return true 36 | end 37 | end 38 | 39 | return joinresult(fileresult(thr:join(5))) 40 | end 41 | 42 | local function checktest(...) 43 | local err = packerror(runtest(...)) 44 | check(not err, "%s", tostring(err)) 45 | return true 46 | end 47 | 48 | -- 49 | -- Assuming (among other things) ones' complement, two's complement, or 50 | -- sign-and-magnitude (the only 3 possibilities permissible in a C 51 | -- environment) and that lua_Integer has more value bits than lua_Number 52 | -- has significand bits, then math.maxinteger shouldn't be representable 53 | -- as a lua_Number. 54 | -- 55 | do 56 | local function tofloat(i) 57 | check("integer" == math.type(i), "number not an integer (%s)", tostring(i)) 58 | local n = i + 0.0 59 | check("float" == math.type(n), "number not a float (given %s, got %s)", tostring(i), tostring(n)) 60 | return n 61 | end 62 | 63 | check(math.maxinteger ~= tofloat(math.maxinteger), "math.maxinteger unexpectedly representable as a float") 64 | 65 | local function test(i, n) 66 | if not thread.self() then 67 | info("checking integer (%s) and float (%s) equivalence", tostring(i), tostring(n)) 68 | end 69 | 70 | check(i == math.maxinteger, "integer argument does not equal math.maxinteger (%s, %s)", tostring(i), tostring(math.maxinteger)) 71 | check(i ~= n, "integer argument unexpectedly equals floating point argument (%s, %s)", tostring(i), tostring(n)) 72 | 73 | return true 74 | end 75 | 76 | checktest(test, math.maxinteger, tofloat(math.maxinteger)) 77 | end 78 | 79 | -- 80 | -- Check that we didn't break floating point in other odd ways. 81 | -- 82 | do 83 | local function ftoa(n) 84 | return string.format("%a", n) 85 | end 86 | 87 | local function test(...) 88 | local t = pack(...) 89 | 90 | local function ftoa(n) 91 | return string.format("%a", n) 92 | end 93 | 94 | for i=1,t.n,2 do 95 | local n = t[i] 96 | local s = t[i + 1] 97 | 98 | if not thread.self() then 99 | info("checking float (%s) and string (%s)", tostring(n), tostring(s)) 100 | end 101 | 102 | check(ftoa(n) == s, "floating point test failed (%s, %q)", tostring(n), tostring(s)) 103 | check(tonumber(s) == n or math.abs(n) == math.huge, "floating point test failed (%s, %q)", tostring(n), tostring(s)) 104 | end 105 | 106 | return true 107 | end 108 | 109 | checktest(test, 1.1, ftoa(1.1), 1/3, ftoa(1/3), math.huge, ftoa(math.huge), -math.huge, ftoa(-math.huge)) 110 | end 111 | 112 | say"OK" 113 | -------------------------------------------------------------------------------- /regress/153-dns-resolvers.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | local main = cqueues.new() 9 | 10 | collectgarbage "stop" 11 | 12 | assert(main:wrap(function() 13 | local pool = dns.getpool() 14 | for _=1, pool.hiwat + 1 do 15 | check(pool:query("google.com", 'AAAA', 'IN', 1)) 16 | end 17 | end):loop()) 18 | 19 | say"OK" 20 | -------------------------------------------------------------------------------- /regress/157-interposed-methods-lost.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | -- load dns.config first so that subsequent loads of DNS modules overwrite 7 | -- the config metatable 8 | local config = require"cqueues.dns.config" 9 | local resolver = require"cqueues.dns.resolver" -- ensure config mt reloaded 10 | require"regress".export".*" 11 | 12 | -- config.new would fail because the :set method it depended upon is an 13 | -- interposed Lua function added to the userdata metatable. When subsequent 14 | -- modules which depended on the metatable definition were loaded, they 15 | -- called dnsL_loadall. dnsL_loadall defined all the internal metatables, 16 | -- which since commit 4d66661 had the effect of replacing any pre-existing 17 | -- __index field with a new table containing only the original C-defined 18 | -- methods. Previously cqs_newmetatable short-circuited when a metatable 19 | -- existed. The purpose of 4d66661 was to permit forced reloading of all Lua 20 | -- modules by clearing package.loaded; the original behavior resulted in 21 | -- modules interposing the same functions multiple times. 22 | info"creating resolver config object" 23 | local cfg = config.new{ 24 | nameserver = { "1.2.3.4" }, 25 | lookup = { "file", "bind", "cache" }, 26 | options = { edns0 = true }, 27 | } 28 | 29 | info("resolv.conf:") 30 | for ln in tostring(cfg):gmatch("[^\n]+") do 31 | info(" %s", ln) 32 | end 33 | 34 | say"OK" 35 | -------------------------------------------------------------------------------- /regress/174-cq-close.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | 9 | local mainloop = cqueues.new() 10 | local cv = condition.new() 11 | local cq = cqueues.new() 12 | 13 | local closed = false 14 | local polling, polled = false, false 15 | local tests = 0 16 | local okays = 0 17 | 18 | local function test(...) 19 | tests = tests + 1 20 | mainloop:wrap(function (f, ...) 21 | f(...) 22 | okays = okays + 1 23 | end, ...) 24 | end 25 | 26 | test(function () 27 | local function checkresult(ready) 28 | info"awoke on controller readiness" 29 | polling = false 30 | polled = true 31 | 32 | check(closed == true, "spurious wakeup") 33 | check(ready == cq, "cqueue not cancelled") 34 | check(cqueues.type(cq), "cqueue not closed") 35 | end 36 | 37 | cv:signal() 38 | polling = true 39 | info"polling controller" 40 | checkresult(cqueues.poll(cq, 3)) 41 | end) 42 | 43 | test(function () 44 | check(not polled, "polling thread unexpectedly finished") 45 | if not polling then 46 | info"waiting on polling thread" 47 | cv:wait(5) 48 | check(polling, "expected polling thread") 49 | end 50 | 51 | closed = true 52 | info"closing controller" 53 | cq:close() 54 | check(not pcall(cq.wrap, cq), "cqueue should not allow new threads when closed") -- previously would trigger a segfault, should now error 55 | 56 | info"closing controller again" 57 | cq:close() 58 | info("cq:close() was safely called a second time") 59 | end) 60 | 61 | ok, why = mainloop:loop() 62 | check(ok, "%s", why) 63 | check(tests == okays, "expected %d okays, got %d", tests, okays) 64 | 65 | say"OK" 66 | -------------------------------------------------------------------------------- /regress/175-cq-close-segfault.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | local cq = cqueues.new() 9 | local ok, why = cq:wrap(function () 10 | cq:close() 11 | end):loop() 12 | check(not ok, "expected loop to fail") 13 | check(tostring(why):find"cqueue running", "unexpected error (%s)", tostring(why)) 14 | 15 | say"OK" 16 | -------------------------------------------------------------------------------- /regress/194-bind-unix.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | -- 7 | -- Issue #194 -- AF_UNIX can't bind source and destination 8 | -- 9 | require"regress".export".*" 10 | 11 | assert(socket.connect { 12 | path = "foo"; 13 | bind = {path="bar"}; 14 | }) 15 | 16 | say("OK") 17 | 18 | -------------------------------------------------------------------------------- /regress/20-listen-dgram.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | local main = cqueues.new() 9 | 10 | main:wrap(function () 11 | local con = socket.listen{ host = "127.0.0.1", port = 0, type = socket.SOCK_DGRAM } 12 | 13 | check(con:listen()) 14 | end) 15 | 16 | check(main:loop()) 17 | 18 | say("OK") 19 | 20 | -------------------------------------------------------------------------------- /regress/22-client-dtls.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | 7 | require"regress".export".*" 8 | 9 | local context = require"openssl.ssl.context" 10 | 11 | local function exists(path) 12 | local fh = io.open(path, "r") 13 | 14 | if fh then 15 | fh:close() 16 | return true 17 | else 18 | return false 19 | end 20 | end 21 | 22 | -- return integer version of openssl(1) command-line tool at path 23 | local function openssl_version(path) 24 | local fh = io.popen(string.format("%s version", path), "r") 25 | local ln = (fh and fh:read()) or "" 26 | 27 | if fh then 28 | fh:close() 29 | end 30 | 31 | local M, m, p 32 | 33 | if ln:match"LibreSSL" then 34 | p, M, m = 0, ln:match("(%d+)%.(%d+)") 35 | else 36 | M, m, p = ln:match("(%d+)%.(%d+)%.(%d+)") 37 | 38 | end 39 | 40 | if M then 41 | return (tonumber(M) * 268435456) + (tonumber(m) * 1048576) + (tonumber(p) * 4096) 42 | end 43 | end 44 | 45 | -- find most recent version of openssl(1) command-line tool 46 | local function openssl_path() 47 | local paths = check(os.getenv"PATH", "no PATH in environment") 48 | local path = nil 49 | local version = 0 50 | 51 | for D in paths:gmatch("[^:]+") do 52 | local tmp_path = D .. "/openssl" 53 | local tmp_version = exists(tmp_path) and openssl_version(tmp_path) 54 | 55 | if tmp_version and tmp_version > version then 56 | info("found %s (%x)", tmp_path, tmp_version) 57 | path = tmp_path 58 | version = tmp_version 59 | end 60 | end 61 | 62 | return version > 0 and path 63 | end 64 | 65 | local function openssl_popen(path) 66 | local key, crt = genkey() 67 | local tmpname = os.tmpname() 68 | local tmpfile = check(io.open(tmpname, "w")) 69 | 70 | check(tmpfile:write(key:toPEM"private")) 71 | check(tmpfile:write(tostring(crt))) 72 | check(tmpfile:flush()) 73 | tmpfile:close() 74 | 75 | local perl_main = [[ 76 | use POSIX; 77 | use strict; 78 | 79 | my ($openssl, $key) = @ARGV; 80 | 81 | my $pid = fork; 82 | die "$!" unless defined $pid; 83 | 84 | # exec openssl in child 85 | exec $openssl, "s_server", "-quiet", "-dtls1", "-key", $key, "-cert", $key 86 | or die "$!" if $pid == 0; 87 | 88 | ; # wait for EOF 89 | 90 | unlink $key; 91 | 92 | while ($pid != (my $rpid = waitpid($pid, WNOHANG))) { 93 | die "$!" if $rpid == -1; 94 | #print STDERR "killing $pid\n"; 95 | kill 9, $pid; 96 | sleep 1; 97 | } 98 | 99 | #print STDERR "reaped $pid\n"; 100 | 101 | 1; 102 | 103 | EOF 104 | ]] 105 | 106 | local perl_begin = ([[ 107 | my @code; 108 | 109 | while () { 110 | last if m/^\s+EOF\s+$/; 111 | push @code, $_; 112 | } 113 | 114 | eval join("", @code) 115 | or die $@; 116 | ]]):gsub("%s+", " ") 117 | 118 | local function quote(txt) 119 | return string.format("'%s'", txt:gsub("'", "'\"'\"'")) 120 | end 121 | 122 | local perl_cmd = string.format("perl -e %s %s %s", quote(perl_begin), quote(path), quote(tmpname)) 123 | 124 | local fh = check(io.popen(perl_cmd, "w")) 125 | fh:write(perl_main) 126 | fh:flush() 127 | 128 | cqueues.sleep(1) -- wait for server to begin listening 129 | 130 | return fh 131 | end 132 | 133 | local main = cqueues.new() 134 | 135 | assert(main:wrap(function () 136 | -- spin up DTLS server using openssl(1) command-line utility 137 | local fh = openssl_popen(check(openssl_path(), "no openssl command-line utility found")) 138 | 139 | -- create client socket 140 | local con = socket.connect{ host="localhost", port=4433, type=socket.SOCK_DGRAM }; 141 | check(fileresult(con:starttls(context.new"DTLSv1", 3))) 142 | 143 | fh:close() 144 | end):loop()) 145 | 146 | say"OK" 147 | 148 | -------------------------------------------------------------------------------- /regress/23-eventfd.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | -- 7 | -- Simple test of controller alerts to check whether our eventfd support 8 | -- works and didn't break anything else. A controller triggers its alert 9 | -- event when adding a new coroutine and it's not currently looping. 10 | -- 11 | require"regress".export".*" 12 | 13 | local main = cqueues.new() 14 | local sub = cqueues.new() 15 | local sub_islooping = false 16 | local sub_alerted = false 17 | 18 | assert(main:wrap(function() 19 | sub_islooping = true 20 | assert(sub:loop()) 21 | end)) 22 | 23 | assert(main:step(0)) 24 | assert(sub_islooping) 25 | 26 | assert(main:wrap(function () 27 | assert(not sub_alerted) 28 | 29 | -- this should cause the main loop to resume the sub loop 30 | assert(sub:wrap(function() 31 | sub_alerted = true 32 | end)) 33 | end)) 34 | 35 | assert(main:loop(3)) 36 | check(sub_alerted, "sub loop never woke up") 37 | 38 | say"OK" 39 | -------------------------------------------------------------------------------- /regress/30-starttls-completion.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | -- 7 | -- Originally socket:starttls did not poll waiting for handshake completion, 8 | -- but instead simply placed the socket into TLS handshake mode and 9 | -- immediately returned. As part of a larger refactoring to improve the 10 | -- consistency of timeouts across the API, socket:starttls was changed to 11 | -- accept a timeout parameter and to poll for completion before returning. 12 | -- An explicit timeout of 0 seconds is necessary to return immediately. 13 | -- 14 | -- However, early TLS example code called socket:starttls outside of any 15 | -- event loop. So as to not break such code, if no timeout was provided 16 | -- socket:starttls attempted to detect whether it was running inside an 17 | -- event loop, and if not exhibited the original behavior of returning 18 | -- immediately rather than polling indefinitely. 19 | -- 20 | -- Since then, cqueues.poll has become capable of polling outside of an 21 | -- event loop, albeit with the side-effect of blocking the application. To 22 | -- improve consistency the backward's compatibility behavior has been 23 | -- removed. socket:starttls now unconditionally polls for completion of the 24 | -- handshake, up to any specified timeout. 25 | -- 26 | require"regress".export".*" 27 | 28 | local so = socket.connect("google.com", 443) 29 | local ok, why = auxlib.fileresult(so:starttls()) 30 | check(ok, "STARTTLS failed: %s", why) 31 | local ssl = check(so:checktls(), "no SSL object") 32 | local crt = ssl:getPeerCertificate() 33 | check(crt ~= nil, "bug not fixed") 34 | say("OK") 35 | -------------------------------------------------------------------------------- /regress/44-resolvers-gc.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua -j- "$0" "$@" 5 | ]] 6 | 7 | require"regress".export".*" 8 | 9 | local resolvers = require"cqueues.dns.resolvers" 10 | 11 | local function caughtleak() 12 | local pool = resolvers.stub() 13 | 14 | local gotleak 15 | 16 | pool:onleak(function () gotleak = true end) 17 | pool:get() 18 | 19 | for i=1,10 do 20 | collectgarbage"collect" 21 | end 22 | 23 | return gotleak 24 | end 25 | 26 | check(caughtleak(), "resolver leak not detected") 27 | 28 | if jit then 29 | _VERSION = "Lua 5.2" -- pretend that we support __gc on tables 30 | check(not caughtleak(), "expected not to catch leak") 31 | end 32 | 33 | say("OK") 34 | -------------------------------------------------------------------------------- /regress/51-join-defunct-thread.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua -t -j- "$0" "$@" 5 | ]] 6 | 7 | require"regress".export".*" 8 | 9 | check(jit, "LuaJIT required") 10 | check(errno.EOWNERDEAD, "EOWNERDEAD not defined") 11 | 12 | local thr, con = assert(thread.start(function (con) 13 | local errno = require"cqueues.errno" 14 | local ffi = require"ffi" 15 | 16 | require"regress".export".*" 17 | 18 | -- 19 | -- NOTE: On musl-libc the parent process deadlocks on flockfile as 20 | -- apparently the thread dies when writing to stderr. Log to our 21 | -- communications socket instead, which doesn't hold any locks. 22 | -- 23 | local function info(...) 24 | con:write(string.format(...), "\n") 25 | con:flush() 26 | end 27 | 28 | info"calling prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT)" 29 | 30 | local PR_SET_SECCOMP = 22 31 | local SECCOMP_MODE_STRICT = 1 32 | ffi.cdef"int prctl(int, unsigned long, unsigned long, unsigned long, unsigned long)" 33 | local ok, rv = pcall(function () return ffi.C.prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, 0, 0, 0) end) 34 | 35 | if not ok then 36 | local rv = string.match(rv, "[^:]+:[^:]+$") or rv 37 | info("prctl call failed: %s", rv) 38 | elseif rv ~= 0 then 39 | info("prctl call failed: %s", errno.strerror(ffi.errno())) 40 | else 41 | info"attempting to open /nonexistant" 42 | io.open"/nonexistant" -- should cause us to be killed 43 | info"prctl call failed: still able to open files" 44 | end 45 | 46 | info"calling pthread_exit" 47 | 48 | ffi.cdef"void pthread_exit(void *);" 49 | ffi.C.pthread_exit(nil) 50 | 51 | info"pthread_exit call failed: thread still running" 52 | end)) 53 | 54 | local main = check(cqueues.new()) 55 | 56 | -- read thread's log lines from pcall because socket:read will throw an 57 | -- error when the socket is closed asynchronously below. 58 | check(main:wrap(pcall, function () 59 | for ln in con:lines() do 60 | info("%s", ln) 61 | end 62 | end)) 63 | 64 | check(main:wrap(function () 65 | local ok, why, code = auxlib.fileresult(thr:join(5)) 66 | 67 | check(not ok, "thread unexpectedly joined (%s)", why or "no error") 68 | check(code == errno.EOWNERDEAD or code == errno.ETIMEDOUT, "unexpected error: %s", why) 69 | check(code == errno.EOWNERDEAD, "robust mutex strategy not supported on this system") 70 | 71 | con:close() 72 | end)) 73 | 74 | check(main:loop()) 75 | 76 | say"OK" 77 | -------------------------------------------------------------------------------- /regress/59-catch-special-only.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | 7 | require"regress".export".*" 8 | 9 | local cq = cqueues.new() 10 | 11 | local MAXSTACK = 1000000 + 10 12 | 13 | local co = coroutine.create(function(...) 14 | cq:wrap(function (...) 15 | for i=1,MAXSTACK do 16 | if 0 == i % math.floor(MAXSTACK / 100) then 17 | info("%d", i) 18 | end 19 | cqueues.poll(0) 20 | local n = coroutine.yield(i) 21 | check(n == i + 1) 22 | end 23 | end, ...) 24 | 25 | local ok, why, errno, thr = cq:loop() 26 | 27 | if not ok then 28 | io.stderr:write(debug.traceback(thr)) 29 | check(false) 30 | end 31 | 32 | return "done" 33 | end) 34 | 35 | for i=1,MAXSTACK do 36 | local ok, j = coroutine.resume(co, i) 37 | check(i == j) 38 | end 39 | 40 | check("done" == select(2, coroutine.resume(co, MAXSTACK + 1))) 41 | check(coroutine.status(co) == "dead") 42 | 43 | say("OK") -------------------------------------------------------------------------------- /regress/61-multiwriters.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | local main = cqueues.new() 9 | 10 | local _cache = {} 11 | local function megarep(s) 12 | if not _cache[s] then 13 | _cache[s] = string.rep(string.rep(s, 1024), 1024) 14 | end 15 | 16 | return _cache[s] 17 | end 18 | 19 | local function test(bufsiz) 20 | local loop = cqueues.new() 21 | 22 | local rd, wr = check(socket.pair()) 23 | 24 | wr:setvbuf("full", bufsiz) 25 | 26 | local sem = { count = 0, condvar = condition.new() } 27 | 28 | local function sem_get() 29 | while sem.count < 1 do 30 | sem.condvar:wait() 31 | end 32 | 33 | sem.count = sem.count - 1 34 | sem.condvar:signal() 35 | cqueues.sleep(0) 36 | end 37 | 38 | local function sem_put(n) 39 | sem.count = sem.count + (n or 1) 40 | sem.condvar:signal() 41 | cqueues.sleep(0) 42 | end 43 | 44 | for i=0,3 do 45 | loop:wrap(function () 46 | sem_get() 47 | 48 | local ch = string.char(string.byte"A" + i) 49 | 50 | for i=1,10 do 51 | check(wr:write(megarep(ch))) 52 | end 53 | 54 | check(wr:flush()) 55 | 56 | sem_put() 57 | end) 58 | end 59 | 60 | loop:wrap(function () 61 | sem_put(4) 62 | 63 | repeat 64 | sem.condvar:wait() 65 | until sem.count == 4 66 | 67 | wr:shutdown"rw" 68 | end) 69 | 70 | local interleaved = false 71 | 72 | loop:wrap(function () 73 | while true do 74 | local buf = rd:read(1024 * 1024) 75 | if buf == nil then break end 76 | local ch = string.sub(buf, 1, 1) 77 | local uniform = not buf:match(string.format("[^%s]", ch)) 78 | 79 | interleaved = interleaved or not uniform 80 | 81 | info("read %d bytes (interleaved:%s)", #buf, tostring(not uniform)) 82 | end 83 | end) 84 | 85 | check(loop:loop()) 86 | 87 | return interleaved 88 | end 89 | 90 | info"begin control test" 91 | check(test(4096) == true, "expected control test to interleave") 92 | info"control test OK" 93 | 94 | info"begin test case" 95 | check(test(-1) == false, "test case interleaved") 96 | info"test case OK" 97 | 98 | say("OK") 99 | -------------------------------------------------------------------------------- /regress/62-noname.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | local fileresult = auxlib.fileresult 9 | 10 | check(cqueues.new():wrap(function () 11 | local so, ok, why, error 12 | 13 | so, why, error = fileresult(socket.connect("nothing.nothing", 80)) 14 | info("socket.connect -> %s error:%d (%s)", tostring(so), error or 0, tostring(why)) 15 | check(so, "failed to create socket: %s", tostring(why)) 16 | 17 | so:onerror(function (so, op, why, lvl) return why end) 18 | 19 | ok, why, error = fileresult(so:connect(10)) 20 | info("socket:connect -> %s error:%d (%s)", tostring(ok), error or 0, tostring(why)) 21 | check(not ok and error and error ~= 0, "socket:connect shouldn't have succeeded") 22 | check(error ~= errno.ENOENT, "socket:connect shouldn't return ENOENT anymore") 23 | check(error < 0, "socket:connect should have returned DNS_ENONAME but got %d (%s)", error, tostring(why)) 24 | end):loop()) 25 | 26 | say"OK" 27 | -------------------------------------------------------------------------------- /regress/63-timeouts-early.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | -- 7 | -- Timeouts are stored as deadlines--absolute clock values of when we should 8 | -- resume the thread. Also, timeouts are stored and computed in floating 9 | -- point and converted to integers only when neeed. If the difference 10 | -- between the nearest deadline and the current time is less than the 11 | -- resolution of the system call timeout parameter (1 millisecond for 12 | -- epoll_wait), truncation will cause us to compute a timeout value of 0. 13 | -- The fix was to use ceil(3) to round up fractional seconds after shifting 14 | -- but before integer conversion. Additionally, subnormal floating point 15 | -- values are now also rounded up to the minimum resolution (e.g. 1 16 | -- millisecond for epoll_wait, 1 nanosecond for kevent). 17 | -- 18 | require"regress".export".*" 19 | 20 | local cqueues = require"cqueues" 21 | 22 | local cq = cqueues.new() 23 | 24 | cq:wrap(function () 25 | cqueues.sleep(0.1) 26 | end) 27 | 28 | local i = 0 29 | while not cq:empty() do 30 | assert(cq:step()) 31 | i = i + 1 32 | end 33 | 34 | check(i == 2, "loop waking up too early") 35 | 36 | say("OK") 37 | 38 | -------------------------------------------------------------------------------- /regress/71-empty-cqueue.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | -- 9 | -- Issue #71A -- After the addition of alerts, cqueue:loop waited indefinitely 10 | -- on an empty queue when the original, more desirable behavior was that it 11 | -- should immediately return. 12 | -- 13 | -- Issue #71B -- cqueue:step did not clear an alert, causing the cqueue to 14 | -- continually poll as ready even after calling cqueue:step. 15 | -- 16 | 17 | local function check_71A() 18 | info"testing issue 71A" 19 | 20 | -- run loop from top-level so we're not testing the nested :step logic 21 | local grace = 3 22 | local fh = check(io.popen(string.format([[ 23 | GRACE=%d 24 | run_and_wait() { 25 | . "${CQUEUES_SRCDIR}/regress/regress.sh" || exit 1; 26 | runlua - <<-EOF & 27 | require"regress".export".*" 28 | assert(cqueues.new():loop()) 29 | io.stdout:write"OK\n" 30 | EOF 31 | PID="$!" 32 | sleep ${GRACE} 33 | set +e # disable strict errors 34 | kill -9 "${PID}" 2>>/dev/null 35 | wait "${PID}" 36 | RC="$?" 37 | printf "RC=%%d\n" "${RC}" 38 | } 39 | exec %gms", timeout, ms) 13 | check(ms / 1000 >= timeout, "conversion lost time (%g > %dms)", timeout, ms) 14 | 15 | local ts = dbg.f2ts(timeout) 16 | info("%g -> { %d, %d }", timeout, ts.tv_sec, ts.tv_nsec) 17 | check(ts.tv_sec + (ts.tv_nsec * 1000000000) >= timeout, "conversion lost time (%g > { %d, %d })", timeout, ts.tv_sec, ts.tv_nsec) 18 | end 19 | 20 | local main = cqueues.new() 21 | 22 | local function checksleep(timeout, noloop) 23 | local start, elapsed 24 | 25 | info("sleeping for %gs (noloop:%s)", timeout, noloop and "yes" or "no") 26 | 27 | if noloop then 28 | local start = cqueues.monotime() 29 | cqueues.poll(timeout) 30 | elapsed = cqueues.monotime() - start 31 | else 32 | check(main:wrap(function () 33 | local start = cqueues.monotime() 34 | cqueues.poll(timeout) 35 | elapsed = cqueues.monotime() - start 36 | end):loop()) 37 | end 38 | 39 | info("%gs elapsed", elapsed) 40 | check(elapsed >= timeout, "sleep too short (%g < %g)", elapsed, timeout) 41 | end 42 | 43 | for _, noloop in ipairs{ false, true } do 44 | for _, timeout in ipairs{ 0.1, 1e-4, 1e-5, 1e-10, 1e-11, 0.9999, 0.999999999, 0.9999999999, 0.9, 1.0, 1.1, 2.0 } do 45 | if not noloop then checkconv(timeout) end 46 | checksleep(timeout, noloop) 47 | end 48 | end 49 | 50 | local INT_MAX = dbg.INT_MAX 51 | 52 | for _, timeout in ipairs{ INT_MAX + 1, INT_MAX * 2, INT_MAX + 0.9999 } do 53 | local ms, is_int_max = dbg.f2ms(timeout) 54 | info("%g -> %d", timeout, ms) 55 | check(is_int_max, "%g didn't clamp", timeout) 56 | end 57 | 58 | for _, timeout in ipairs{ 2^63 } do 59 | local ts, is_long_max = dbg.f2ts(timeout) 60 | info("%g -> { %g, %g }", timeout, ts.tv_sec, ts.tv_nsec) 61 | check(is_long_max, "%g didn't clamp", timeout) 62 | end 63 | 64 | say("OK") 65 | -------------------------------------------------------------------------------- /regress/82-localname-garbage.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | local function uname() 9 | return check(check(io.popen("uname -a", "r")):read"*a") 10 | end 11 | 12 | local function localpath(s) 13 | local family, pathname = check(fileresult(s:localname())) 14 | 15 | check(family == socket.AF_UNIX, "wrong address family (%s)", tostring(family)) 16 | 17 | return pathname 18 | end 19 | 20 | info"creating socket pair" 21 | local a, b = check(socket.pair(socket.AF_UNIX)) 22 | 23 | info"check that pathname is nil" 24 | check(localpath(a) == nil, "pathname of AF_UNIX socket pair not nil (%q)", localpath(a)) 25 | a:close() 26 | b:close() 27 | 28 | if uname():find"Linux" then 29 | local pathname = "\00082-localname-garbage.sock" 30 | 31 | info("creating abstract socket at %q", pathname) 32 | 33 | local srv = check(socket.listen{ path = pathname }) 34 | local a, b 35 | 36 | info("checking for abstract socket pathname") 37 | check(localpath(srv) == pathname, "bad pathname (%q)", localpath(srv)) 38 | 39 | local main = cqueues.new() 40 | 41 | main:wrap(function () 42 | info("accepting connection at %q", pathname) 43 | a = check(srv:accept()) 44 | end) 45 | 46 | main:wrap(function () 47 | info("connecting to %q", pathname) 48 | b = check(socket.connect{ path = pathname }) 49 | end) 50 | 51 | check(main:loop()) 52 | 53 | check(localpath(a) == pathname, "pathname of connected AF_UNIX socket wrong (%q)", localpath(a)) 54 | check(localpath(b) == nil, "pathname of connected AF_UNIX socket not nil (%q)", localpath(b)) 55 | end 56 | 57 | say("OK") 58 | 59 | -------------------------------------------------------------------------------- /regress/85-cancel-issues.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | -- 9 | -- Run our test. We don't have a way to force failure with versions of the 10 | -- openssl module that have been fixed. 11 | -- 12 | local main = check(cqueues.new()) 13 | local a, b = check(socket.pair(socket.SOCK_STREAM)) 14 | local cv = check(condition.new()) 15 | 16 | check(main:wrap(function () 17 | local subloop = check(cqueues.new()) 18 | 19 | subloop:wrap(function () 20 | local event = { pollfd = a:pollfd(), events = "rp" } 21 | 22 | check(cv:signal()) 23 | info"subloop: entering poll state" 24 | local ready, errmsg = fileresult(cqueues.poll(event)) 25 | info"subloop: awoke from poll" 26 | 27 | check(ready or not errmsg, "subloop: poll error (%s)", tostring(errmsg)) 28 | check(ready == event, "subloop: cancelled event did not poll ready") 29 | 30 | cv:signal() 31 | end) 32 | 33 | check(subloop:loop()) 34 | end)) 35 | 36 | check(main:wrap(function () 37 | info"main loop: waiting on subloop to poll" 38 | cv:wait() 39 | info"main loop: cancelling socket" 40 | cqueues.cancel(a) 41 | info"main loop: waiting on subloop signal" 42 | check(cv:wait(3), "main loop: timeout before cancelled event polled ready") 43 | end)) 44 | 45 | check(main:loop()) 46 | 47 | say"OK" 48 | -------------------------------------------------------------------------------- /regress/87-alpn-disappears.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | -- 9 | -- Acquire reference to internal openssl loader so we can reinvoke to 10 | -- trigger the bug. The bug is that ex_newstate overwrites the registry 11 | -- index where we stored our exdata state object from a previous invocation 12 | -- of ex_newstate, causing it to be garbage collected. 13 | -- 14 | -- When the state object is garbage collected, it invalidates all the 15 | -- outstanding data attached to any extant object. The state object is an 16 | -- interpreter-scoped singleton which should persist for the lifetime of the 17 | -- Lua interpreter. Garbage collection is supposed to signal that the Lua 18 | -- interpreter is being destroyed, and any data belonging to that 19 | -- interpreter and attached to OpenSSL objects is invalid. (NB: The parent 20 | -- application and any OpenSSL objects created from the interpreter can 21 | -- persist after our Lua interpreter. For example in a multithreaded 22 | -- application. Likewise, there can be multiple interpreters, each of which 23 | -- must have its own exdata state.) 24 | -- 25 | -- Because ex_newdata is run from initall, and initall is invoked whenever 26 | -- any openssl submodule is loaded, this bug can be triggered by loading a 27 | -- new submodule after installing exdata. Or, as here, it can be triggered 28 | -- by explicitly reinvoking the loader for a submodule. 29 | -- 30 | check(package.searchpath, "package.searchpath not defined") 31 | 32 | local file, why = package.searchpath("_openssl", package.cpath) 33 | check(file, "module _openssl required") 34 | local reinit = check(package.loadlib(file, "luaopen__openssl_compat")) 35 | 36 | local function ticklebug() 37 | reinit() 38 | 39 | for i=1,2 do 40 | collectgarbage"collect" 41 | end 42 | end 43 | 44 | -- 45 | -- Run our test. We don't have a way to force failure with versions of the 46 | -- openssl module that have been fixed. 47 | -- 48 | local main = check(cqueues.new()) 49 | 50 | check(main:wrap(function () 51 | local alpn_attempts, alpn_issued = 0, 0 52 | local cli_ctx, srv_ctx 53 | 54 | cli_ctx = getsslctx("TLSv1", false, false) 55 | check(cli_ctx.setAlpnProtos, "ALPN support not available") 56 | cli_ctx:setAlpnProtos{ "http2.0", "http" } 57 | 58 | srv_ctx = getsslctx("TLSv1", true) 59 | srv_ctx:setAlpnSelect(function (ssl, list, ...) 60 | info("onAlpnSelect: %s", table.concat(list, " ")) 61 | 62 | alpn_issued = alpn_issued + 1 63 | 64 | return list[1] 65 | end) 66 | 67 | for i=1,100 do 68 | local srv, cli = check(socket.pair(socket.SOCK_STREAM)) 69 | 70 | check(main:wrap(function () 71 | check(cli:starttls(cli_ctx)) 72 | end)) 73 | 74 | check(srv:starttls(srv_ctx)) 75 | alpn_attempts = alpn_attempts + 1 76 | 77 | ticklebug() 78 | end 79 | 80 | info("%d ALPN callbacks issued in %d SSL connections", alpn_issued, alpn_attempts) 81 | check(alpn_attempts == alpn_issued, "wrong number of ALPN callbacks") 82 | end)) 83 | 84 | check(main:loop()) 85 | 86 | say"OK" 87 | -------------------------------------------------------------------------------- /regress/93-remove-family-field.lua: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | _=[[ 3 | . "${0%%/*}/regress.sh" 4 | exec runlua "$0" "$@" 5 | ]] 6 | require"regress".export".*" 7 | 8 | -- 9 | -- Simple test to check whether we broke initializing a socket object with a 10 | -- pipe descriptor. 11 | -- 12 | info"creating FILE handle attached to printf invocation" 13 | local fh = check(io.popen("printf 'OK\\n'", "r")) 14 | 15 | info"creating socket object attached to pipe (via FILE handle)" 16 | local con = check(fileresult(socket.dup(fh))) 17 | 18 | info"reading printed string from pipe" 19 | local what = check(fileresult(con:read"*l")) 20 | 21 | check(what == "OK", "expected 'OK' but got '%q'", what) 22 | 23 | say("OK") 24 | -------------------------------------------------------------------------------- /regress/GNUmakefile: -------------------------------------------------------------------------------- 1 | # non-recursive prologue 2 | sp := $(sp).x 3 | dirstack_$(sp) := $(d) 4 | d := $(abspath $(lastword $(MAKEFILE_LIST))/..) 5 | 6 | ifeq ($(origin GUARD_$(d)), undefined) 7 | GUARD_$(d) := 1 8 | 9 | include $(d)/../GNUmakefile 10 | 11 | 12 | .PHONY: $(d)/check check 13 | 14 | $(d)/check: 15 | @for V in 5.1 5.2 5.3 5.4; do \ 16 | printf "Building $${V}... "; \ 17 | if (cd $(@D) && ./regress.sh -r"$${V}" build >/dev/null 2>&1); then \ 18 | printf "OK\n"; \ 19 | else \ 20 | printf "FAIL\n"; \ 21 | fi; \ 22 | done 23 | @cd $(@D); P=0; F=0; for T in ./[123456789]*.lua; do \ 24 | if "./$$T" -B; then P=$$(($$P + 1)); else F=$$(($$F + 1)); fi; \ 25 | done; \ 26 | printf "PASS=%d FAIL=%d\n" "$$P" "$$F"; \ 27 | test $$F -eq 0; 28 | 29 | check: $(d)/check 30 | 31 | 32 | .PHONY: $(d)/clean $(d)/clean~ 33 | 34 | $(d)/clean: 35 | $(RM) -fr $(@D)/.local 36 | 37 | $(d)/clean~: $(d)/clean 38 | $(RM) -f $(@D)/*~ 39 | 40 | clean: $(d)/clean 41 | 42 | clean~: $(d)/clean~ 43 | 44 | 45 | endif # include guard 46 | 47 | # non-recursive epilogue 48 | d := $(dirstack_$(sp)) 49 | sp := $(basename $(sp)) 50 | -------------------------------------------------------------------------------- /regress/Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | all: 4 | +gmake -f GNUmakefile all 5 | 6 | .DEFAULT: 7 | +gmake -f GNUmakefile $< 8 | -------------------------------------------------------------------------------- /regress/regress.lua: -------------------------------------------------------------------------------- 1 | local require = require -- may be overloaded by regress.require 2 | local auxlib = require"cqueues.auxlib" 3 | 4 | local regress = { 5 | cqueues = require"cqueues", 6 | dns = require "cqueues.dns", 7 | socket = require"cqueues.socket", 8 | thread = require"cqueues.thread", 9 | errno = require"cqueues.errno", 10 | condition = require"cqueues.condition", 11 | auxlib = auxlib, 12 | assert = auxlib.assert, 13 | fileresult = auxlib.fileresult, 14 | pack = table.pack or function (...) 15 | local t = { ... } 16 | t.n = select("#", ...) 17 | return t 18 | end, 19 | unpack = table.unpack or unpack, 20 | } 21 | 22 | local emit_progname = os.getenv"REGRESS_PROGNAME" or "regress" 23 | local emit_verbose = tonumber(os.getenv"REGRESS_VERBOSE" or 1) 24 | local emit_info = {} 25 | local emit_ll = 0 26 | 27 | local function emit(fmt, ...) 28 | local msg = string.format(fmt, ...) 29 | 30 | for txt, nl in msg:gmatch("([^\n]*)(\n?)") do 31 | if emit_ll == 0 and #txt > 0 then 32 | io.stderr:write(emit_progname, ": ") 33 | emit_ll = #emit_progname + 2 34 | end 35 | 36 | io.stderr:write(txt, nl) 37 | 38 | if nl == "\n" then 39 | emit_ll = 0 40 | else 41 | emit_ll = emit_ll + #txt 42 | end 43 | end 44 | end -- emit 45 | 46 | local function emitln(fmt, ...) 47 | if emit_ll > 0 then 48 | emit"\n" 49 | end 50 | 51 | emit(fmt .. "\n", ...) 52 | end -- emitln 53 | 54 | local function emitinfo() 55 | for _, txt in ipairs(emit_info) do 56 | emitln("%s", txt) 57 | end 58 | end -- emitinfo 59 | 60 | function regress.say(...) 61 | emitln(...) 62 | end -- say 63 | 64 | function regress.panic(...) 65 | emitinfo() 66 | emitln(...) 67 | os.exit(1) 68 | end -- panic 69 | 70 | function regress.info(...) 71 | if emit_verbose > 1 then 72 | emitln(...) 73 | else 74 | emit_info[#emit_info + 1] = string.format(...) 75 | 76 | if emit_verbose > 0 then 77 | if emit_ll > 78 then 78 | emit"\n." 79 | else 80 | emit"." 81 | end 82 | end 83 | end 84 | end -- info 85 | 86 | function regress.check(v, ...) 87 | if v then 88 | return v, ... 89 | else 90 | regress.panic(...) 91 | end 92 | end -- check 93 | 94 | function regress.export(...) 95 | for _, pat in ipairs{ ... } do 96 | for k, v in pairs(regress) do 97 | if string.match(k, pat) then 98 | _G[k] = v 99 | end 100 | end 101 | end 102 | 103 | return regress 104 | end -- export 105 | 106 | function regress.require(modname) 107 | local ok, module = pcall(require, modname) 108 | 109 | regress.check(ok, "module %s required", modname) 110 | 111 | return module 112 | end -- regress.require 113 | 114 | function regress.genkey(type) 115 | local pkey = regress.require"openssl.pkey" 116 | local x509 = regress.require"openssl.x509" 117 | local name = regress.require"openssl.x509.name" 118 | local altname = regress.require"openssl.x509.altname" 119 | local key 120 | 121 | type = string.upper(type or "RSA") 122 | 123 | if type == "EC" then 124 | key = regress.check(pkey.new{ type = "EC", curve = "prime192v1" }) 125 | else 126 | key = regress.check(pkey.new{ type = type, bits = 1024 }) 127 | end 128 | 129 | local dn = name.new() 130 | dn:add("C", "US") 131 | dn:add("ST", "California") 132 | dn:add("L", "San Francisco") 133 | dn:add("O", "Acme, Inc.") 134 | dn:add("CN", "acme.inc") 135 | 136 | local alt = altname.new() 137 | alt:add("DNS", "acme.inc") 138 | alt:add("DNS", "localhost") 139 | 140 | local crt = x509.new() 141 | crt:setVersion(3) 142 | crt:setSerial(47) 143 | crt:setSubject(dn) 144 | crt:setIssuer(crt:getSubject()) 145 | crt:setSubjectAlt(alt) 146 | 147 | local issued, expires = crt:getLifetime() 148 | crt:setLifetime(issued, expires + 60) 149 | 150 | crt:setBasicConstraints{ CA = true, pathLen = 2 } 151 | crt:setBasicConstraintsCritical(true) 152 | 153 | crt:setPublicKey(key) 154 | crt:sign(key) 155 | 156 | return key, crt 157 | end -- regress.genkey 158 | 159 | 160 | local function getsubtable(t, name, ...) 161 | name = name or false -- cannot be nil 162 | 163 | if not t[name] then 164 | t[name] = {} 165 | end 166 | 167 | if select('#', ...) > 0 then 168 | return getsubtable(t[name], ...) 169 | else 170 | return t[name] 171 | end 172 | end -- getsubtable 173 | 174 | function regress.newsslctx(protocol, accept, keytype) 175 | local context = regress.require"openssl.ssl.context" 176 | local ctx = context.new(protocol, accept) 177 | 178 | if keytype or keytype == nil then 179 | local key, crt = regress.genkey(keytype) 180 | 181 | ctx:setCertificate(crt) 182 | ctx:setPrivateKey(key) 183 | end 184 | 185 | return ctx 186 | end -- require.newsslctx 187 | 188 | local ctxcache = {} 189 | 190 | function regress.getsslctx(protocol, accept, keytype) 191 | local keycache = getsubtable(ctxcache, protocol, accept) 192 | 193 | if keytype == nil then 194 | keytype = "RSA" 195 | end 196 | 197 | local ctx = keycache[keytype] 198 | 199 | if not ctx then 200 | ctx = regress.newsslctx(protocol, accept, keytype) 201 | keycache[keytype] = ctx 202 | end 203 | 204 | return ctx 205 | end -- regress.getsslctx 206 | 207 | -- test 87-alpn-disappears relies on package.searchpath 208 | function regress.searchpath(name, paths, sep, rep) 209 | sep = (sep or "."):gsub("[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") 210 | rep = (rep or "/"):gsub("%%", "%%%%") 211 | 212 | local nofile = {} 213 | 214 | for path in paths:gmatch("([^;]+)") do 215 | path = path:gsub("%?", (name:gsub(sep, rep):gsub("%%", "%%%%"))) 216 | local fh = io.open(path, "rb") 217 | if fh then 218 | fh:close() 219 | return path 220 | end 221 | nofile[#nofile + 1] = string.format("no file '%s'", path) 222 | end 223 | 224 | return nil, table.concat(nofile, "\n") 225 | end -- regress.searchpath 226 | 227 | package.searchpath = package.searchpath or regress.searchpath 228 | 229 | 230 | local Error = {} 231 | Error.__index = Error 232 | 233 | function Error:__tostring() 234 | return self.text 235 | end 236 | 237 | function Error:unpack() 238 | return regress.unpack(self, 1, self.n) 239 | end 240 | 241 | function regress.packerror(v, ...) 242 | if not v then 243 | local err = regress.pack(select(2, auxlib.fileresult(nil, ...))) 244 | 245 | err.text = err[1] or "?" 246 | err.code = err[2] 247 | 248 | return setmetatable(err, Error) 249 | else 250 | return nil, v, ... 251 | end 252 | end 253 | 254 | return regress 255 | -------------------------------------------------------------------------------- /regress/regress.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e # strict error 3 | set -f # disable pathname expansion 4 | set -C # noclobber 5 | unset IFS 6 | 7 | : ${CQUEUES_SRCDIR:="$(cd "${0%%/*}/.." && pwd -L)"} 8 | PATH="${PATH:-$(command -p getconf PATH)}:${CQUEUES_SRCDIR}/mk" 9 | 10 | : ${REGRESS_VERBOSE:=1} 11 | : ${REGRESS_REBUILD:=1} 12 | : ${REGRESS_PROGNAME:=${0##*/}} 13 | REGRESS_PROGNAME="${REGRESS_PROGNAME%.lua}" 14 | 15 | export CQUEUES_SRCDIR REGRESS_VERBOSE REGRESS_REBUILD REGRESS_PROGNAME 16 | 17 | SHORTOPTS="Br:j:Jqvh" 18 | 19 | usage() { 20 | cat <<-EOF 21 | Usage: ${0##*/} [-${SHORTOPTS}] 22 | -B do not try to rebuild modules 23 | -r RANGE run specific Lua version 24 | -j RANGE run specific LuaJIT version 25 | -J exclude LuaJIT from candidate interpreters 26 | -q do not emit informational messages 27 | -v emit verbose informational messages 28 | -h print this usage message 29 | 30 | Report bugs to 31 | EOF 32 | } 33 | 34 | while getopts "${SHORTOPTS}" OPTC; do 35 | case "${OPTC}" in 36 | B) 37 | REGRESS_REBUILD=0 38 | ;; 39 | r) 40 | export RUNLUA_R="${OPTARG}" 41 | ;; 42 | j) 43 | export RUNLUA_J="${OPTARG}" 44 | ;; 45 | J) 46 | export RUNLUA_J="0-0" 47 | ;; 48 | q) 49 | REGRESS_VERBOSE=0 50 | ;; 51 | v) 52 | REGRESS_VERBOSE=2 53 | ;; 54 | h) 55 | usage 56 | exit 0 57 | ;; 58 | ?) 59 | usage >&2 60 | exit 1 61 | ;; 62 | esac 63 | done 64 | 65 | shift $((${OPTIND} - 1)) 66 | 67 | lua51path="${CQUEUES_SRCDIR}/regress/.local/share/5.1" 68 | lua51cpath="${CQUEUES_SRCDIR}/regress/.local/lib/5.1" 69 | lua52path="${CQUEUES_SRCDIR}/regress/.local/share/5.2" 70 | lua52cpath="${CQUEUES_SRCDIR}/regress/.local/lib/5.2" 71 | lua53path="${CQUEUES_SRCDIR}/regress/.local/share/5.3" 72 | lua53cpath="${CQUEUES_SRCDIR}/regress/.local/lib/5.3" 73 | lua54path="${CQUEUES_SRCDIR}/regress/.local/share/5.4" 74 | lua54cpath="${CQUEUES_SRCDIR}/regress/.local/lib/5.4" 75 | 76 | export LUA_PATH="${lua51path}/?.lua;${CQUEUES_SRCDIR}/regress/?.lua;${LUA_PATH:-;}" 77 | export LUA_CPATH="${lua51cpath}/?.so;${LUA_CPATH:-;}" 78 | export LUA_PATH_5_2="${lua52path}/?.lua;${CQUEUES_SRCDIR}/regress/?.lua;${LUA_PATH_5_2:-;}" 79 | export LUA_CPATH_5_2="${lua52cpath}/?.so;${LUA_CPATH_5_2:-;}" 80 | export LUA_PATH_5_3="${lua53path}/?.lua;${CQUEUES_SRCDIR}/regress/?.lua;${LUA_PATH_5_3:-;}" 81 | export LUA_CPATH_5_3="${lua53cpath}/?.so;${LUA_CPATH_5_3:-;}" 82 | export LUA_PATH_5_4="${lua54path}/?.lua;${CQUEUES_SRCDIR}/regress/?.lua;${LUA_PATH_5_4:-;}" 83 | export LUA_CPATH_5_4="${lua54cpath}/?.so;${LUA_CPATH_5_4:-;}" 84 | 85 | 86 | if [ "${0##*/}" = "regress.sh" ]; then 87 | case "${1:-build}" in 88 | build) 89 | LUA_API="$(runlua -e "print(_VERSION:match'%d.%d')")" 90 | unset MAKEFLAGS 91 | (cd "${CQUEUES_SRCDIR}" && make -s "install${LUA_API}" \ 92 | lua51path="${lua51path}" lua51cpath="${lua51cpath}" \ 93 | lua52path="${lua52path}" lua52cpath="${lua52cpath}" \ 94 | lua53path="${lua53path}" lua53cpath="${lua53cpath}" \ 95 | lua54path="${lua54path}" lua54cpath="${lua54cpath}") 96 | exit $? 97 | ;; 98 | *) 99 | printf "%s: %s: unknown command\n" "${0##*/}" "${1:-\'\'}" >&2 100 | exit 1 101 | ;; 102 | esac 103 | else 104 | if [ ${REGRESS_REBUILD} -eq 1 ]; then 105 | export REGRESS_REBUILD=0 # disable for recursive invocations 106 | 107 | (cd "${CQUEUES_SRCDIR}" && make -s install \ 108 | lua51path="${lua51path}" lua51cpath="${lua51cpath}" \ 109 | lua52path="${lua52path}" lua52cpath="${lua52cpath}" \ 110 | lua53path="${lua53path}" lua53cpath="${lua53cpath}" \ 111 | lua54path="${lua54path}" lua54cpath="${lua54cpath}") 112 | fi 113 | 114 | if [ ! -d "${CQUEUES_SRCDIR}/regress/.local/lib/5.3" ] || ! runlua -e 'require"_cqueues"' >>/dev/null 2>&1; then 115 | export RUNLUA_R="${RUNLUA_R:=5.1-5.2}" 116 | fi 117 | 118 | case "$(uname -s)" in 119 | OpenBSD) 120 | : ${RUNLUA_T:=1} 121 | export RUNLUA_T 122 | ;; 123 | esac 124 | fi 125 | -------------------------------------------------------------------------------- /src/GNUmakefile: -------------------------------------------------------------------------------- 1 | # non-recursive prologue 2 | sp := $(sp).x 3 | dirstack_$(sp) := $(d) 4 | d := $(abspath $(lastword $(MAKEFILE_LIST))/..) 5 | 6 | ifeq ($(origin GUARD_$(d)), undefined) 7 | GUARD_$(d) := 1 8 | 9 | 10 | # 11 | # E N V I R O N M E N T C O N F I G U R A T I O N 12 | # 13 | include $(d)/../GNUmakefile 14 | 15 | include $(d)/lib/GNUmakefile 16 | 17 | 18 | # 19 | # C O M P I L A T I O N F L A G S 20 | # 21 | VENDOR_$(d) = $(or $(CQUEUES_VENDOR),$(shell $(| $@.tmp 43 | mv $@.tmp $@ 44 | 45 | $(d)/config.h: $(abspath $(d)/..)/config.h 46 | $(CP) $< $@ 47 | 48 | define BUILD_$(d) 49 | 50 | $$(d)/$(1)/cqueues.so: $$(addprefix $$(d)/$(1)/, $$(OBJS_$(d))) $$(d)/lib/libnonlua.a 51 | $$(CC) -o $$@ $$^ $$(SOFLAGS_$$(abspath $$(@D)/..)) $$(LDFLAGS_$$(abspath $$(@D)/..)) $$(LIBS_$$(abspath $$(@D)/..)) 52 | 53 | $$(d)/$(1)/%.o: $$(d)/%.c $$(d)/cqueues.h $$(d)/../vendor/compat53/c-api/compat-5.3.h $$(d)/config.h 54 | $$(MKDIR) -p $$(@D) 55 | $$(CC) $$(CFLAGS_$$(" 251 | 252 | help: $(d)/help 253 | 254 | 255 | endif # include guard 256 | 257 | # non-recursive epilogue 258 | d := $(dirstack_$(sp)) 259 | sp := $(basename $(sp)) 260 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | all: 4 | +gmake -f GNUmakefile all 5 | 6 | .DEFAULT: 7 | +gmake -f GNUmakefile $< 8 | -------------------------------------------------------------------------------- /src/auxlib.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local cqueues = require"_cqueues" 3 | local errno = require"_cqueues.errno" 4 | local auxlib = require"_cqueues.auxlib" 5 | local coroutine = require"coroutine" 6 | 7 | -- 8 | -- auxlib.tostring 9 | -- 10 | -- Yieldable tostring. Implemented in C for Lua 5.2 and above. 11 | -- 12 | -- Lua 5.1 API doesn't allow us to implement a yieldable tostring 13 | -- routine. Fortunately, LuaJIT's tostring permits yielding. 14 | -- 15 | auxlib.tostring = auxlib.tostring or tostring 16 | 17 | -- 18 | -- auxlib.resume 19 | -- auxlib.wrap 20 | -- 21 | -- Wrappers for multilevel coroutine management to allow I/O polling 22 | -- of coroutine-wrapped code. The code checks for a special value 23 | -- returned by cqueues.poll, and will propogate a yield on I/O. 24 | -- Everything else should behave as usual. 25 | -- 26 | local _POLL = cqueues._POLL -- magic internal value 27 | local l_resume = coroutine.resume -- take reference to permit monkey patching 28 | local l_yield = coroutine.yield 29 | 30 | local function c_resume(co, ok, arg1, ...) 31 | if ok and arg1 == _POLL then 32 | return auxlib.resume(co, l_yield(_POLL, ...)) 33 | else 34 | return ok, arg1, ... 35 | end 36 | end -- c_resume 37 | 38 | function auxlib.resume(co, ...) 39 | return c_resume(co, l_resume(co, ...)) 40 | end -- auxlib.resume 41 | 42 | local function c_wrap(co, ok, ...) 43 | if ok then 44 | return ... 45 | else 46 | error((...), 0) 47 | end 48 | end -- c_wrap 49 | 50 | function auxlib.wrap(f) 51 | local co = coroutine.create(f) 52 | 53 | return function(...) 54 | return c_wrap(co, c_resume(co, l_resume(co, ...))) 55 | end 56 | end -- auxlib.wrap 57 | 58 | -- 59 | -- auxlib.assert 60 | -- 61 | -- If condition is false, locate and use the first non-false, 62 | -- non-nil value as the reason. If an integer value, attempt to 63 | -- convert to string using strerror. 64 | -- 65 | -- RATIONALE: Many routines return only an integer error number. 66 | -- Errors like EAGAIN are very common and constantly pushing a new 67 | -- string on the stack from C would be inefficient. 68 | -- 69 | -- Also, unlike the standard Lua idiom, the failure mode for some 70 | -- routines will return multiple false or nil values preceding the 71 | -- error number so user code doesn't need to use variable names like 72 | -- "value_or_error" for routines which can be expected to fail 73 | -- regularly in the normal course of operation and where simply 74 | -- using the assert idiom would create spaghetti code. 75 | -- 76 | local tostring = auxlib.tostring 77 | 78 | local function findwhy(v, ...) 79 | if v then 80 | if type(v) == "number" then 81 | -- return string and number for auxlib.fileresult 82 | return (errno.strerror(v) or tostring(v)), v 83 | else 84 | return tostring(v), ... 85 | end 86 | elseif select("#", ...) > 0 then 87 | return findwhy(...) 88 | else 89 | return 90 | end 91 | end 92 | 93 | function auxlib.assert(c, ...) 94 | if c then 95 | return c, ... 96 | end 97 | 98 | return error(findwhy(...), 2) 99 | end -- auxlib.assert 100 | 101 | -- 102 | -- auxlib.assert[23456789] 103 | -- 104 | -- Like auxlib.assert, but use error(message, level) where level is 105 | -- the numeric suffix of the assert function name. 106 | -- 107 | local function makeassert(level) 108 | return function (c, ...) 109 | if c then 110 | return c, ... 111 | end 112 | 113 | return error(findwhy(...), level) 114 | end 115 | end 116 | 117 | for n=2,9 do 118 | auxlib[string.format("assert%d", n)] = makeassert(n) 119 | end 120 | 121 | -- 122 | -- auxlib.fileresult 123 | -- 124 | function auxlib.fileresult(c, ...) 125 | if c then 126 | return c, ... 127 | else 128 | return c, findwhy(...) 129 | end 130 | end -- auxlib.fileresult 131 | 132 | auxlib.loader = loader 133 | 134 | return auxlib 135 | end -- loader 136 | 137 | return loader(loader, ...) 138 | -------------------------------------------------------------------------------- /src/condition.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local core = require"_cqueues.condition" 3 | 4 | local function check(self, why, ...) 5 | if self == why then 6 | return true, ... 7 | else 8 | return false, why, ... 9 | end 10 | end 11 | 12 | local wait; wait = core.interpose("wait", function (self, ...) 13 | return check(self, wait(self, ...)) 14 | end) 15 | 16 | return core 17 | end 18 | 19 | return loader(loader, ...) 20 | -------------------------------------------------------------------------------- /src/cqueues.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local core = require"_cqueues" 3 | local errno = require"_cqueues.errno" 4 | local monotime = core.monotime 5 | local running = core.running 6 | local strerror = errno.strerror 7 | local unpack = assert(table.unpack or unpack) -- 5.1 compat 8 | 9 | -- lazily load auxlib to prevent circular or unused dependencies 10 | local auxlib = setmetatable({}, { __index = function (t, k) 11 | local v = require"cqueues.auxlib"[k] 12 | rawset(t, k, v) 13 | return v 14 | end }) 15 | 16 | -- load deprecated APIs into shadow table to keep hidden unless used 17 | local notsupp = {} 18 | setmetatable(core, { __index = notsupp }) 19 | 20 | function core.new() 21 | local cq, err = core.create() 22 | if not cq then 23 | error("unable to initialize continuation queue: " .. err) 24 | end 25 | return cq 26 | end 27 | 28 | -- 29 | -- core.poll 30 | -- 31 | -- Wrap the cqueues yield protocol to support polling across 32 | -- multilevel resume/yield. Requires explicit or implicit use 33 | -- (through monkey patching of coroutine.resume and coroutine.wrap) 34 | -- of auxlib.resume or auxlib.wrap. 35 | -- 36 | -- Also supports polling from outside of a running event loop using 37 | -- a cheap hack. NOT RECOMMENDED. 38 | -- 39 | local _POLL = core._POLL 40 | local yield = coroutine.yield 41 | local poller 42 | 43 | function core.poll(...) 44 | if running() then 45 | return yield(_POLL, ...) 46 | else 47 | local tuple 48 | 49 | poller = poller or auxlib.assert3(core.new()) 50 | 51 | poller:wrap(function (...) 52 | tuple = { core.poll(...) } 53 | end, ...) 54 | 55 | -- NOTE: must step twice, once to call poll and 56 | -- again to wake up 57 | auxlib.assert3(poller:step()) 58 | auxlib.assert3(poller:step()) 59 | 60 | return unpack(tuple or {}) 61 | end 62 | end -- core.poll 63 | 64 | -- 65 | -- core.sleep 66 | -- 67 | -- Sleep primitive. 68 | -- 69 | function core.sleep(timeout) 70 | core.poll(timeout) 71 | end -- core.sleep 72 | 73 | -- 74 | -- core:step 75 | -- 76 | -- Wrap the low-level :step interface to make managing event loops 77 | -- slightly easier. 78 | -- 79 | local step; step = core.interpose("step", function (self, timeout) 80 | if core.running() then 81 | core.poll(self, timeout) 82 | 83 | return step(self, 0.0) 84 | else 85 | return step(self, timeout) 86 | end 87 | end) 88 | -- 89 | -- Lua 5.1 doesn't allow continuations in C 90 | -- so we have to emulate them in lua. 91 | -- 92 | if _VERSION == "Lua 5.1" then 93 | local function checkstep(self, ok, ...) 94 | if ok == "yielded" then 95 | return checkstep(self, self:step_resume(coroutine.yield(...))) 96 | else 97 | return ok, ... 98 | end 99 | end 100 | local step_; step_ = core.interpose("step", function (self, timeout) 101 | return checkstep(self, step_(self, timeout)) 102 | end) 103 | end -- core:step 104 | 105 | -- 106 | -- core:loop 107 | -- 108 | -- Step until an error is encountered. 109 | -- 110 | local function todeadline(timeout) 111 | -- special case 0 timeout to avoid monotime call in totimeout 112 | return timeout and (timeout > 0 and monotime() + timeout or 0) or nil 113 | end -- todeadline 114 | 115 | local function totimeout(deadline) 116 | if not deadline then 117 | return nil, false 118 | elseif deadline == 0 then 119 | return 0, true 120 | else 121 | local curtime = monotime() 122 | 123 | if curtime < deadline then 124 | return deadline - curtime, false 125 | else 126 | return 0, true 127 | end 128 | end 129 | end -- totimeout 130 | 131 | core.interpose("loop", function (self, timeout) 132 | local function checkstep(self, deadline, ok, ...) 133 | local timeout, expired = totimeout(deadline) 134 | 135 | if not ok then 136 | return false, ... 137 | elseif expired or self:empty() then 138 | return true 139 | else 140 | return checkstep(self, deadline, self:step(timeout)) 141 | end 142 | end 143 | 144 | local deadline = todeadline(timeout) 145 | return checkstep(self, deadline, self:step(timeout)) 146 | end) -- core:loop 147 | 148 | -- 149 | -- core:errors 150 | -- 151 | -- Return iterator over core:loop. 152 | -- 153 | core.interpose("errors", function (self, timeout) 154 | local deadline = todeadline(timeout) 155 | 156 | return function () 157 | local timeout = totimeout(deadline) 158 | return select(2, self:loop(timeout)) 159 | end 160 | end) -- core:errors 161 | 162 | -- 163 | -- core.assert 164 | -- 165 | -- DEPRECATED. See auxlib.assert. 166 | -- 167 | function notsupp.assert(...) 168 | return auxlib.assert(...) 169 | end -- notsupp.assert 170 | 171 | -- 172 | -- core.resume 173 | -- 174 | -- DEPRECATED. See auxlib.resume. 175 | -- 176 | function notsupp.resume(...) 177 | return auxlib.resume(...) 178 | end -- notsupp.resume 179 | 180 | -- 181 | -- core.wrap 182 | -- 183 | -- DEPRECATED. See auxlib.wrap. 184 | -- 185 | function notsupp.wrap(...) 186 | return auxlib.wrap(...) 187 | end -- notsupp.wrap 188 | 189 | core.loader = loader 190 | 191 | return core 192 | end -- loader 193 | 194 | return loader(loader, ...) 195 | -------------------------------------------------------------------------------- /src/dns.config.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local config = require"_cqueues.dns.config" 3 | 4 | config.loadfile = function (file, syntax) 5 | local cfg = config.new() 6 | 7 | cfg:loadfile(file, syntax) 8 | 9 | return cfg 10 | end 11 | 12 | config.loadpath = function (path, syntax) 13 | local cfg = config.new() 14 | 15 | cfg:loadpath(path, syntax) 16 | 17 | return cfg 18 | end 19 | 20 | local new = config.new; config.new = function(init) 21 | local cfg = new() 22 | 23 | if init then 24 | cfg:set(init) 25 | end 26 | 27 | return cfg 28 | end 29 | 30 | local stub = config.stub; config.stub = function(init) 31 | local cfg = stub() 32 | 33 | if init then 34 | cfg:set(init) 35 | end 36 | 37 | return cfg 38 | end 39 | 40 | local root = config.root; config.root = function(init) 41 | local cfg = root() 42 | 43 | if init then 44 | cfg:set(init) 45 | end 46 | 47 | return cfg 48 | end 49 | 50 | config.interpose("set", function (self, init) 51 | if init.nameserver then 52 | self:setns(init.nameserver) 53 | end 54 | 55 | if init.search then 56 | self:setsearch(init.search) 57 | end 58 | 59 | if init.lookup then 60 | self:setlookup(init.lookup) 61 | end 62 | 63 | local opts = init.options or init.opts or { } 64 | local copy = { 65 | "edns0", "ndots", "timeout", "attempts", 66 | "rotate", "recurse", "smart", "tcp" 67 | } 68 | 69 | for i, k in ipairs(copy) do 70 | if opts[k] == nil and init[k] ~= nil then 71 | opts[k] = init[k]; 72 | end 73 | end 74 | 75 | self:setopts(opts) 76 | 77 | if init.interface then 78 | self:setiface(init.interface) 79 | end 80 | end) 81 | 82 | config.interpose("get", function (self) 83 | return { 84 | nameserver = self:getns(), 85 | search = self:getsearch(), 86 | lookup = self:getlookup(), 87 | options = self:getopts(), 88 | interface = self:getiface(), 89 | } 90 | end) 91 | 92 | config.loader = loader 93 | 94 | return config 95 | end 96 | 97 | return loader(loader, ...) 98 | -------------------------------------------------------------------------------- /src/dns.hints.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local hints = require"_cqueues.dns.hints" 3 | 4 | return hints 5 | end 6 | 7 | return loader(loader, ...) 8 | -------------------------------------------------------------------------------- /src/dns.hosts.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local hosts = require"_cqueues.dns.hosts" 3 | 4 | hosts.loadfile = function (file) 5 | local hosts = hosts.new() 6 | 7 | hosts:loadfile(file) 8 | 9 | return hosts 10 | end 11 | 12 | hosts.loadpath = function (path) 13 | local hosts = hosts.new() 14 | 15 | hosts:loadpath(path) 16 | 17 | return hosts 18 | end 19 | 20 | hosts.stub = function () 21 | return hosts.loadpath"/etc/hosts" 22 | end 23 | 24 | hosts.root = function () 25 | return hosts.new() 26 | end 27 | 28 | return hosts 29 | end 30 | 31 | return loader(loader, ...) 32 | -------------------------------------------------------------------------------- /src/dns.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local dns = require"_cqueues.dns" 3 | local assert = require"cqueues".assert 4 | 5 | -- 6 | -- NOTE: defer loading dns.resolvers as it depends on us 7 | -- 8 | local pool = nil 9 | 10 | function dns.setpool(p) 11 | local o = pool 12 | 13 | assert(require"cqueues.dns.resolvers".type(p), "not dns resolver pool") 14 | 15 | pool = p 16 | 17 | return o 18 | end -- dns.setpool 19 | 20 | 21 | function dns.getpool() 22 | if not pool then 23 | dns.setpool(assert(require"cqueues.dns.resolvers".stub())) 24 | end 25 | 26 | return pool 27 | end -- dns.getpool 28 | 29 | 30 | function dns.query(...) 31 | return dns.getpool():query(...) 32 | end -- dns.query 33 | 34 | 35 | dns.loader = loader 36 | 37 | return dns 38 | end 39 | 40 | return loader(loader, ...) 41 | -------------------------------------------------------------------------------- /src/dns.packet.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local packet = require"_cqueues.dns.packet" 3 | local record = require"_cqueues.dns.record" -- dns.record depends on dns.packet 4 | 5 | local function toconst(id, map, what, lvl) 6 | local n 7 | 8 | if id == nil then 9 | return 10 | elseif type(id) == "number" then 11 | n = map[id] and id 12 | elseif type(id) == "string" then 13 | n = map[id] or map[string.upper(id)] 14 | end 15 | 16 | if not n then 17 | error((tostring(id) .. ": unknown DNS " .. what), lvl + 1) 18 | end 19 | 20 | return n 21 | end -- toconst 22 | 23 | 24 | local _push; _push = packet.interpose("push", function (self, section, name, type, class, ttl, rdata) 25 | section = toconst(section, packet.section, "section", 2) 26 | type = toconst(type, record.type, "type", 2) 27 | class = toconst(class, record.class, "class", 2) 28 | 29 | return _push(self, section, name, type, class, ttl, rdata) 30 | end) -- packet:push 31 | 32 | 33 | -- 34 | -- TODO: Don't restrict ourselves to the C iteration interface, 35 | -- which is limited by fixed-sized fields. For example, you cannot 36 | -- specify multiple different record types because the type 37 | -- identifiers cannot be ORd together. 38 | -- 39 | local _grep; _grep = packet.interpose("grep", function (self, opts) 40 | if opts then 41 | opts.type = toconst(opts.type, record.type, "type", 2) 42 | opts.class = toconst(opts.class, record.class, "class", 2) 43 | 44 | if type(opts.section) == "string" then 45 | local n = 0 46 | 47 | for s in string.gmatch(opts.section, "%a+") do 48 | n = n + toconst(s, packet.section, "section", 2) 49 | end 50 | 51 | opts.section = n 52 | elseif type(opts.section) == "table" then 53 | local n = 0 54 | 55 | for i=1,#opts.section do 56 | n = n + toconst(opts.section[i], packet.section, "section", 2) 57 | end 58 | 59 | opts.section = n 60 | end 61 | end 62 | 63 | return _grep(self, opts) 64 | end) -- packet:grep 65 | 66 | 67 | return packet 68 | end 69 | 70 | return loader(loader, ...) 71 | -------------------------------------------------------------------------------- /src/dns.record.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local record = require"_cqueues.dns.record" 3 | 4 | for k, v in pairs(record.class) do 5 | if type(k) == "string" then 6 | record[k] = v 7 | end 8 | end 9 | 10 | for k, v in pairs(record.type) do 11 | if type(k) == "string" then 12 | record[k] = v 13 | end 14 | end 15 | 16 | for k, v in pairs(require"cqueues.dns.packet".section) do 17 | if type(k) == "string" then 18 | record[k] = v 19 | end 20 | end 21 | 22 | return record 23 | end 24 | 25 | return loader(loader, ...) 26 | -------------------------------------------------------------------------------- /src/dns.resolver.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local cqueues = require"cqueues" 3 | local resolver = require"_cqueues.dns.resolver" 4 | local config = require"cqueues.dns.config" 5 | local record = require"cqueues.dns.record" 6 | local errno = require"cqueues.errno" 7 | local EAGAIN = errno.EAGAIN 8 | local ETIMEDOUT = errno.ETIMEDOUT 9 | local monotime = cqueues.monotime 10 | 11 | local _new = resolver.new; resolver.new = function (resconf, hosts, hints) 12 | if type(resconf) == "table" then 13 | resconf = config.new(resconf) 14 | end 15 | 16 | return _new(resconf, hosts, hints) 17 | end 18 | 19 | resolver.stub = function (init) 20 | return resolver.new(config.stub(init), nil, nil) 21 | end 22 | 23 | resolver.root = function (init) 24 | return resolver.new(config.root(init), nil, nil) 25 | end 26 | 27 | local function toconst(id, map, what, lvl) 28 | local n 29 | 30 | if id == nil then 31 | return 32 | elseif type(id) == "number" then 33 | n = map[id] and id 34 | elseif type(id) == "string" then 35 | n = map[id] or map[string.upper(id)] 36 | end 37 | 38 | if not n then 39 | error((tostring(id) .. ": unknown DNS " .. what), lvl + 1) 40 | end 41 | 42 | return n 43 | end -- toconst 44 | 45 | local _submit; _submit = resolver.interpose("submit", function (self, name, type, class) 46 | type = toconst(type, record.type, "type", 2) 47 | class = toconst(class, record.class, "class", 2) 48 | 49 | return _submit(self, name, type, class) 50 | end) 51 | 52 | resolver.interpose("query", function (self, name, type, class, timeout) 53 | local deadline = timeout and (monotime() + timeout) 54 | local ok, why, answer 55 | 56 | ok, why = self:submit(name, type, class) 57 | 58 | if not ok then 59 | return nil, why 60 | end 61 | 62 | repeat 63 | answer, why = self:fetch() 64 | 65 | if not answer then 66 | if why == EAGAIN then 67 | if deadline then 68 | local curtime = monotime() 69 | 70 | if deadline <= curtime then 71 | return nil, ETIMEDOUT 72 | else 73 | cqueues.poll(self, math.min(deadline - curtime, 1)) 74 | end 75 | else 76 | cqueues.poll(self, 1) 77 | end 78 | else 79 | return nil, why 80 | end 81 | end 82 | until answer 83 | 84 | return answer 85 | end) 86 | 87 | resolver.loader = loader 88 | 89 | return resolver 90 | end 91 | 92 | return loader(loader, ...) 93 | -------------------------------------------------------------------------------- /src/dns.resolvers.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local resolver = require"cqueues.dns.resolver" 3 | local config = require"cqueues.dns.config" 4 | local condition = require"cqueues.condition" 5 | local monotime = require"cqueues".monotime 6 | local random = require"cqueues.dns".random 7 | local errno = require"cqueues.errno" 8 | local ETIMEDOUT = errno.ETIMEDOUT 9 | 10 | 11 | local function todeadline(timeout) 12 | return (timeout and (monotime() + timeout)) or nil 13 | end -- todeadline 14 | 15 | local function totimeout(deadline) 16 | return (deadline and math.max(0, deadline - monotime())) or nil 17 | end -- totimeout 18 | 19 | 20 | -- 21 | -- NOTE: Keep track of an unordered collection of objects, and in 22 | -- particular a count of objects in the collection. If an object is 23 | -- garbage collected automatically decrement the count and signal 24 | -- the condition variable. 25 | -- 26 | local alive = {} 27 | 28 | function alive.new(condvar) 29 | local self = setmetatable({}, { __index = alive }) 30 | 31 | self.n = 0 32 | self.table = setmetatable({}, { __mode = "k" }) 33 | self.condvar = condvar 34 | self.hooks = {} 35 | self.hookmt = { __gc = function (hook) 36 | self.n = self.n - 1 37 | self.condvar:signal() 38 | 39 | if self.leakcb then 40 | pcall(self.leakcb) 41 | end 42 | end } 43 | 44 | return self 45 | end -- alive.new 46 | 47 | 48 | function alive:newhook() 49 | if not self._newhook then 50 | if _G._VERSION == "Lua 5.1" then 51 | -- Lua 5.1 does not support __gc on tables, so we need to use newproxy 52 | self._newhook = function(mt) 53 | local u = newproxy(false) 54 | debug.setmetatable(u, mt) 55 | return u 56 | end 57 | else 58 | self._newhook = function(mt) 59 | return setmetatable({}, mt) 60 | end 61 | end 62 | end 63 | 64 | return self._newhook(self.hookmt) 65 | end -- alive:newhook 66 | 67 | 68 | function alive:add(x) 69 | if not self.table[x] then 70 | local hook = self.hooks[#self.hooks] 71 | 72 | if hook then 73 | self.hooks[#self.hooks] = nil 74 | else 75 | hook = self:newhook() 76 | end 77 | 78 | self.table[x] = hook 79 | self.n = self.n + 1 80 | end 81 | end -- alive:add 82 | 83 | 84 | function alive:delete(x) 85 | if self.table[x] then 86 | self.hooks[#self.hooks + 1] = self.table[x] 87 | self.table[x] = nil 88 | self.n = self.n - 1 89 | self.condvar:signal() 90 | end 91 | end -- alive:delete 92 | 93 | 94 | function alive:check() 95 | local n = 0 96 | 97 | for _ in pairs(self.table) do 98 | n = n + 1 99 | end 100 | 101 | return assert(n == self.n, "resolver registry corrupt") 102 | end -- alive:check 103 | 104 | 105 | function alive:onleak(f) 106 | local old = self.onleak 107 | self.leakcb = f 108 | return old 109 | end -- alive:onleak 110 | 111 | 112 | local pool = {} 113 | 114 | local function getby(self, deadline) 115 | local res 116 | while true do 117 | local cache_len = #self.cache 118 | if cache_len > 1 then 119 | res = self.cache[cache_len] 120 | self.cache[cache_len] = nil 121 | if res then 122 | break 123 | else 124 | if deadline and deadline <= monotime() then 125 | return nil, ETIMEDOUT 126 | end 127 | self.condvar:wait(totimeout(deadline)) 128 | end 129 | elseif self.alive.n < self.hiwat then 130 | local why 131 | res, why = resolver.new(self.resconf, self.hosts, self.hints) 132 | if not res then 133 | return nil, why 134 | end 135 | break 136 | end 137 | end 138 | self.alive:add(res) 139 | return res 140 | end -- getby 141 | 142 | 143 | function pool:get(timeout) 144 | return getby(self, todeadline(timeout)) 145 | end -- pool:get 146 | 147 | 148 | function pool:put(res) 149 | self.alive:delete(res) 150 | 151 | local cache_len = #self.cache 152 | if cache_len < self.lowat and res:stat().queries < self.querymax then 153 | if not self.lifo and cache_len > 0 then 154 | local i = random(cache_len+1) + 1 155 | 156 | self.cache[cache_len+1] = self.cache[i] 157 | self.cache[i] = res 158 | else 159 | self.cache[cache_len+1] = res 160 | end 161 | else 162 | res:close() 163 | end 164 | end -- pool:put 165 | 166 | 167 | function pool:signal() 168 | self.condvar:signal() 169 | end -- pool:signal 170 | 171 | 172 | function pool:query(name, type, class, timeout) 173 | local deadline = todeadline(timeout or self.timeout) 174 | local res, why = getby(self, deadline) 175 | 176 | if not res then 177 | return nil, why 178 | end 179 | 180 | local r, y = res:query(name, type, class, totimeout(deadline)) 181 | self:put(res) 182 | if not r then 183 | return nil, y 184 | end 185 | 186 | return r 187 | end -- pool:query 188 | 189 | 190 | function pool:check() 191 | return self.alive:check() 192 | end -- pool:check 193 | 194 | 195 | function pool:onleak(f) 196 | return self.alive:onleak(f) 197 | end -- pool:onleak 198 | 199 | 200 | local resolvers = {} 201 | 202 | resolvers.lowat = 1 203 | resolvers.hiwat = 32 204 | resolvers.querymax = 2048 205 | resolvers.onleak = nil 206 | resolvers.lifo = false 207 | 208 | function resolvers.new(resconf, hosts, hints) 209 | local self = {} 210 | 211 | self.resconf = (type(resconf) == "table" and config.new(resconf)) or resconf 212 | self.hosts = hosts 213 | self.hints = hints 214 | self.condvar = condition.new() 215 | self.lowat = resolvers.lowat 216 | self.hiwat = resolvers.hiwat 217 | self.timeout = resolvers.timeout 218 | self.querymax = resolvers.querymax 219 | self.onleak = resolvers.onleak 220 | self.lifo = resolvers.lifo 221 | self.cache = {} 222 | self.alive = alive.new(self.condvar) 223 | 224 | return setmetatable(self, { __index = pool }) 225 | end -- resolvers.new 226 | 227 | 228 | function resolvers.stub(cfg) 229 | return resolvers.new(config.stub(cfg)) 230 | end -- resolvers.stub 231 | 232 | 233 | function resolvers.root(cfg) 234 | return resolvers.new(config.root(cfg)) 235 | end -- resolvers.root 236 | 237 | 238 | function resolvers.type(o) 239 | local mt = getmetatable(o) 240 | 241 | if mt and mt.__index == pool then 242 | return "dns resolver pool" 243 | end 244 | end -- resolvers.type 245 | 246 | 247 | resolvers.loader = loader 248 | 249 | return resolvers 250 | end 251 | 252 | return loader(loader, ...) 253 | -------------------------------------------------------------------------------- /src/errno.c.m4: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | * errno.c.m4 - Lua Continuation Queues 3 | * -------------------------------------------------------------------------- 4 | * Copyright (c) 2012, 2015 William Ahern 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the 8 | * "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the 12 | * following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 20 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * ========================================================================== 25 | */ 26 | #include "config.h" 27 | 28 | #include /* memcpy(3) strcmp(3) strerror_r(3) strnlen(3) */ 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | #include "lib/dns.h" 35 | #include "lib/socket.h" 36 | 37 | #include "cqueues.h" 38 | 39 | 40 | #ifndef STRERROR_R_CHAR_P 41 | #define STRERROR_R_CHAR_P ((GLIBC_PREREQ(0,0) || UCLIBC_PREREQ(0,0,0)) && (_GNU_SOURCE || !(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600))) 42 | #endif 43 | 44 | cqs_error_t cqs_strerror_r(cqs_error_t error, char *dst, size_t lim) { 45 | const char *src; 46 | 47 | if (error >= DNS_EBASE && error < DNS_ELAST) { 48 | src = dns_strerror(error); 49 | } else if (error >= SO_EBASE && error < SO_ELAST) { 50 | src = so_strerror(error); 51 | } else { 52 | #if STRERROR_R_CHAR_P 53 | if (!(src = strerror_r(error, dst, lim))) 54 | return EINVAL; 55 | #else 56 | /* glibc between 2.3.4 and 2.13 returns -1 on error */ 57 | if (-1 == (error = strerror_r(error, dst, lim))) 58 | return errno; 59 | 60 | return error; 61 | #endif 62 | } 63 | 64 | if (src != dst && lim > 0) { 65 | size_t n = strnlen(src, lim - 1); 66 | memcpy(dst, src, n); 67 | dst[n] = '\0'; 68 | } 69 | 70 | return 0; 71 | } /* cqs_strerror_r() */ 72 | 73 | 74 | const char *(cqs_strerror)(int error, void *dst, size_t lim) { 75 | char *p, *pe, *unknown; 76 | char e10[((sizeof error * CHAR_BIT) / 3) + 1], *ep; 77 | int n; 78 | 79 | if (!lim) 80 | return dst; 81 | 82 | if (0 == cqs_strerror_r(error, dst, lim) && *(char *)dst) 83 | return dst; 84 | 85 | p = dst; 86 | pe = p + lim; 87 | 88 | unknown = "Unknown error: "; 89 | while (*unknown && p < pe) 90 | *p++ = *unknown++; 91 | 92 | if (error < 0 && p < pe) 93 | *p++ = '-'; 94 | 95 | /* translate integer to string in LSB order */ 96 | for (ep = e10, n = error; n; ep++, n /= 10) 97 | *ep = "0123456789"[abs(n % 10)]; 98 | if (ep == e10) 99 | *ep++ = '0'; 100 | 101 | /* copy string, flipping from LSB to MSB */ 102 | while (ep > e10 && p < pe) 103 | *p++ = *--ep; 104 | 105 | p[-1] = '\0'; 106 | 107 | return dst; 108 | } /* cqs_strerror() */ 109 | 110 | 111 | static const struct { 112 | const char *name; 113 | int value; 114 | } errlist[] = { 115 | changequote(<<<,>>>)dnl 116 | ifdef(<<>>,<<>>,<<>>)(<<< 117 | ../mk/errno.ls | awk '{ print "#ifdef "$1"\n\t{ \""$1"\", "$1" },\n#endif" }' 118 | >>>)dnl 119 | }; 120 | 121 | 122 | static int le_strerror(lua_State *L) { 123 | lua_pushstring(L, cqs_strerror(luaL_checkint(L, 1))); 124 | 125 | return 1; 126 | } /* le_strerror() */ 127 | 128 | 129 | static const luaL_Reg le_globals[] = { 130 | { "strerror", &le_strerror }, 131 | { NULL, NULL } 132 | }; 133 | 134 | 135 | int luaopen__cqueues_errno(lua_State *L) { 136 | unsigned i; 137 | 138 | luaL_newlib(L, le_globals); 139 | 140 | for (i = 0; i < sizeof errlist / sizeof *errlist; i++) { 141 | lua_pushstring(L, errlist[i].name); 142 | lua_pushinteger(L, errlist[i].value); 143 | lua_settable(L, -3); 144 | 145 | #if EAGAIN == EWOULDBLOCK 146 | if (!strcmp(errlist[i].name, "EWOULDBLOCK")) 147 | continue; 148 | #endif 149 | 150 | lua_pushinteger(L, errlist[i].value); 151 | lua_pushstring(L, errlist[i].name); 152 | lua_settable(L, -3); 153 | } 154 | 155 | return 1; 156 | } /* luaopen__cqueues_errno() */ 157 | -------------------------------------------------------------------------------- /src/errno.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local errno = require("_cqueues.errno") 3 | 4 | errno.loader = loader 5 | 6 | return errno 7 | end 8 | 9 | return loader(loader, ...) 10 | -------------------------------------------------------------------------------- /src/lib/GNUmakefile: -------------------------------------------------------------------------------- 1 | # non-recursive prologue 2 | sp := $(sp).x 3 | dirstack_$(sp) := $(d) 4 | d := $(abspath $(lastword $(MAKEFILE_LIST))/..) 5 | 6 | ifeq ($(origin GUARD_$(d)), undefined) 7 | GUARD_$(d) := 1 8 | 9 | 10 | # 11 | # E N V I R O N M E N T C O N F I G U R A T I O N 12 | # 13 | include $(d)/../../GNUmakefile 14 | 15 | 16 | # 17 | # C O M P I L I A T I O N F L A G S 18 | # 19 | OS_$(d) := $(shell $(d)/../../mk/vendor.os) 20 | 21 | CPPFLAGS_$(d) = $(ALL_CPPFLAGS) -DSOCKET_DEBUG -DHAVE_CONFIG_H 22 | CFLAGS_$(d) = $(ALL_CFLAGS) 23 | 24 | ifeq ($(findstring DNS_RANDOM,$(CPPFLAGS_$(d))),) 25 | ifeq ($(OS_$(d)), $(filter $(OS_$(d)), OpenBSD NetBSD FreeBSD Darwin)) 26 | CPPFLAGS_$(d) += -DDNS_RANDOM=arc4random 27 | else 28 | CPPFLAGS_$(d) += -DDNS_RANDOM=RAND_bytes 29 | endif 30 | endif 31 | 32 | # 33 | # C O M P I L I A T I O N R U L E S 34 | # 35 | $(d)/config.h: $(abspath $(d)/../..)/config.h 36 | $(CP) $< $@ 37 | 38 | $(d)/%.o: $(d)/%.c $(d)/%.h $(d)/config.h 39 | $(CC) $(CFLAGS_$(@D)) $(CPPFLAGS_$(@D)) -c -o $@ $< 40 | 41 | $(d)/libnonlua.a: $(d)/socket.o $(d)/dns.o $(d)/notify.o 42 | $(AR) cr $@ $^ 43 | $(RANLIB) $@ 44 | 45 | 46 | # 47 | # L O C A L R U L E S 48 | # 49 | ifneq "$(filter $(abspath $(d))/%, $(abspath $(firstword $(MAKEFILE_LIST))))" "" 50 | 51 | libnonlua.a: $(d)/libnonlua.a 52 | 53 | endif # local guard 54 | 55 | 56 | .PHONY: $(d)/clean $(d)/clean~ clean clean~ 57 | 58 | $(d)/clean: 59 | rm -fr $(@D)/config.h $(@D)/*.[oa] $(@D)/*.dSYM 60 | 61 | $(d)/clean~: $(d)/clean 62 | rm -fr $(@D)/*~ 63 | 64 | clean: $(d)/clean 65 | 66 | clean~: $(d)/clean~ 67 | 68 | 69 | endif # include guard 70 | 71 | # non-recursive epilogue 72 | d := $(dirstack_$(sp)) 73 | sp := $(basename $(sp)) 74 | -------------------------------------------------------------------------------- /src/lib/Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | all: 4 | +gmake -f GNUmakefile all 5 | 6 | .DEFAULT: 7 | +gmake -f GNUmakefile $< 8 | -------------------------------------------------------------------------------- /src/lib/notify.h: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | * notify.h - Kernel File Notification. 3 | * -------------------------------------------------------------------------- 4 | * Copyright (c) 2012 William Ahern 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the 8 | * "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the 12 | * following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 20 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * ========================================================================== 25 | */ 26 | #ifndef NOTIFY_H 27 | #define NOTIFY_H 28 | 29 | #define NOTIFY_VERSION 0x20120926 30 | 31 | 32 | #define NOTIFY_CREATE 0x01 33 | #define NOTIFY_ATTRIB 0x02 34 | #define NOTIFY_MODIFY 0x04 35 | #define NOTIFY_REVOKE 0x08 36 | #define NOTIFY_DELETE 0x10 37 | 38 | #define NOTIFY_ALL (NOTIFY_CREATE|NOTIFY_DELETE|NOTIFY_ATTRIB|NOTIFY_MODIFY|NOTIFY_REVOKE) 39 | 40 | #define NOTIFY_GLOB 0x20 41 | #define NOTIFY_GREP 0x40 42 | 43 | 44 | #define nfy_error_t int 45 | #define nfy_timeout_t int 46 | #define nfy_flags_t int 47 | 48 | 49 | struct notify *notify_opendir(const char *, nfy_flags_t, nfy_error_t *); 50 | 51 | void notify_close(struct notify *); 52 | 53 | int notify_pollfd(struct notify *); 54 | 55 | nfy_timeout_t notify_timeout(struct notify *); 56 | 57 | nfy_error_t notify_step(struct notify *, nfy_timeout_t); 58 | 59 | nfy_error_t notify_add(struct notify *, const char *, nfy_flags_t); 60 | 61 | nfy_flags_t notify_get(struct notify *, const char **); 62 | 63 | 64 | #define NOTIFY_INOTIFY 0x010000 65 | #define NOTIFY_FEN 0x020000 66 | #define NOTIFY_KQUEUE 0x040000 67 | #define NOTIFY_KQUEUE1 0x080000 68 | #define NOTIFY_OPENAT 0x100000 69 | #define NOTIFY_FDOPENDIR 0x200000 70 | #define NOTIFY_O_CLOEXEC 0x400000 71 | #define NOTIFY_IN_CLOEXEC 0x800000 72 | 73 | nfy_flags_t notify_features(void); 74 | 75 | const char *notify_strflag(nfy_flags_t); 76 | 77 | 78 | #endif /* NOTIFY_H */ 79 | -------------------------------------------------------------------------------- /src/notify.c: -------------------------------------------------------------------------------- 1 | /* ========================================================================== 2 | * notify.c - Lua Continuation Queues 3 | * -------------------------------------------------------------------------- 4 | * Copyright (c) 2012 William Ahern 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a 7 | * copy of this software and associated documentation files (the 8 | * "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to permit 11 | * persons to whom the Software is furnished to do so, subject to the 12 | * following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 20 | * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 21 | * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 22 | * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 23 | * USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | * ========================================================================== 25 | */ 26 | #include "config.h" 27 | 28 | #include "lib/notify.h" 29 | #include "cqueues.h" 30 | 31 | 32 | struct luanotify { 33 | struct notify *notify; 34 | }; /* luanotify */ 35 | 36 | 37 | static int ln_step(lua_State *L) { 38 | struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); 39 | int error; 40 | 41 | if ((error = notify_step(N->notify, 0))) { 42 | lua_pushboolean(L, 0); 43 | lua_pushinteger(L, error); 44 | 45 | return 2; 46 | } else { 47 | lua_pushboolean(L, 1); 48 | 49 | return 1; 50 | } 51 | } /* ln_step() */ 52 | 53 | 54 | static int ln_get(lua_State *L) { 55 | struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); 56 | const char *name = 0; 57 | int changes; 58 | 59 | if (!(changes = notify_get(N->notify, &name))) 60 | return 0; 61 | 62 | lua_pushinteger(L, changes); 63 | lua_pushstring(L, name); 64 | 65 | return 2; 66 | } /* ln_get() */ 67 | 68 | 69 | static int ln_add(lua_State *L) { 70 | struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); 71 | const char *name = luaL_checkstring(L, 2); 72 | int flags = luaL_optinteger(L, 3, NOTIFY_ALL); 73 | int error; 74 | 75 | if ((error = notify_add(N->notify, name, flags))) { 76 | lua_pushboolean(L, 0); 77 | lua_pushinteger(L, error); 78 | 79 | return 2; 80 | } else { 81 | lua_pushboolean(L, 1); 82 | 83 | return 1; 84 | } 85 | } /* ln_add() */ 86 | 87 | 88 | static int ln_pollfd(lua_State *L) { 89 | struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); 90 | 91 | lua_pushinteger(L, notify_pollfd(N->notify)); 92 | 93 | return 1; 94 | } /* ln_pollfd() */ 95 | 96 | 97 | static int ln_events(lua_State *L) { 98 | luaL_checkudata(L, 1, CQS_NOTIFY); 99 | 100 | lua_pushliteral(L, "r"); 101 | 102 | return 1; 103 | } /* ln_events() */ 104 | 105 | 106 | static int ln_timeout(lua_State *L) { 107 | struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); 108 | int timeout; 109 | 110 | if ((timeout = notify_timeout(N->notify)) >= 0) { 111 | lua_pushnumber(L, (lua_Number)timeout / 1000); 112 | 113 | return 1; 114 | } else { 115 | return 0; 116 | } 117 | } /* ln_timeout() */ 118 | 119 | 120 | static const luaL_Reg ln_methods[] = { 121 | { "step", &ln_step }, 122 | { "get", &ln_get }, 123 | { "add", &ln_add }, 124 | { "pollfd", &ln_pollfd }, 125 | { "events", &ln_events }, 126 | { "timeout", &ln_timeout }, 127 | { NULL, NULL }, 128 | }; /* ln_methods[] */ 129 | 130 | 131 | static int ln__gc(lua_State *L) { 132 | struct luanotify *N = luaL_checkudata(L, 1, CQS_NOTIFY); 133 | 134 | notify_close(N->notify); 135 | N->notify = 0; 136 | 137 | return 0; 138 | } /* ln__gc() */ 139 | 140 | 141 | static const luaL_Reg ln_metatable[] = { 142 | { "__gc", &ln__gc }, 143 | { NULL, NULL }, 144 | }; /* ln_metatable[] */ 145 | 146 | 147 | static int ln_opendir(lua_State *L) { 148 | const char *path = luaL_checkstring(L, 1); 149 | struct luanotify *N = 0; 150 | int error; 151 | 152 | N = lua_newuserdata(L, sizeof *N); 153 | N->notify = 0; 154 | luaL_setmetatable(L, CQS_NOTIFY); 155 | 156 | if (!(N->notify = notify_opendir(path, NOTIFY_ALL, &error))) 157 | goto error; 158 | 159 | return 1; 160 | error: 161 | lua_pushnil(L); 162 | lua_pushinteger(L, error); 163 | 164 | return 2; 165 | } /* ln_opendir */ 166 | 167 | 168 | static int ln_type(lua_State *L) { 169 | if (luaL_testudata(L, 1, CQS_NOTIFY)) { 170 | lua_pushstring(L, "file notifier"); 171 | } else { 172 | lua_pushnil(L); 173 | } 174 | 175 | return 1; 176 | } /* ln_type() */ 177 | 178 | 179 | static int ln_interpose(lua_State *L) { 180 | return cqs_interpose(L, CQS_NOTIFY); 181 | } /* ln_interpose() */ 182 | 183 | 184 | static int ln_strflag(lua_State *L) { 185 | int flags = luaL_checkint(L, 1); 186 | int flag, count = 0; 187 | const char *name; 188 | 189 | while (ffs(flags)) { 190 | flag = 1 << (ffs(flags) - 1); 191 | flags &= ~flag; 192 | 193 | if ((name = notify_strflag(flag))) { 194 | luaL_checkstack(L, 1, "too many results"); 195 | lua_pushstring(L, name); 196 | count++; 197 | } 198 | } 199 | 200 | return count; 201 | } /* ln_strflag() */ 202 | 203 | 204 | static int ln_nxtflag(lua_State *L) { 205 | int flags = (int)lua_tointeger(L, lua_upvalueindex(1)); 206 | int flag; 207 | 208 | if (ffs(flags)) { 209 | flag = 1 << (ffs(flags) - 1); 210 | 211 | lua_pushinteger(L, flags & ~flag); 212 | lua_replace(L, lua_upvalueindex(1)); 213 | 214 | lua_pushinteger(L, flag); 215 | 216 | return 1; 217 | } 218 | 219 | return 0; 220 | } /* ln_nxtflag() */ 221 | 222 | static int ln_flags(lua_State *L) { 223 | int i, flags = 0; 224 | 225 | for (i = 1; i <= lua_gettop(L); i++) 226 | flags |= luaL_checkint(L, i); 227 | 228 | lua_pushinteger(L, flags); 229 | lua_pushcclosure(L, &ln_nxtflag, 1); 230 | 231 | return 1; 232 | } /* ln_flags() */ 233 | 234 | 235 | static const luaL_Reg ln_globals[] = { 236 | { "opendir", &ln_opendir }, 237 | { "type", &ln_type }, 238 | { "interpose", &ln_interpose }, 239 | { "strflag", &ln_strflag }, 240 | { "flags", &ln_flags }, 241 | { NULL, NULL } 242 | }; 243 | 244 | 245 | int luaopen__cqueues_notify(lua_State *L) { 246 | static const struct { 247 | const char *name; 248 | int value; 249 | } flag[] = { 250 | { "CREATE", NOTIFY_CREATE }, 251 | { "DELETE", NOTIFY_DELETE }, 252 | { "ATTRIB", NOTIFY_ATTRIB }, 253 | { "MODIFY", NOTIFY_MODIFY }, 254 | { "REVOKE", NOTIFY_REVOKE }, 255 | { "ALL", NOTIFY_ALL }, 256 | 257 | { "INOTIFY", NOTIFY_INOTIFY }, 258 | { "FEN", NOTIFY_FEN }, 259 | { "KQUEUE", NOTIFY_KQUEUE }, 260 | { "KQUEUE1", NOTIFY_KQUEUE1 }, 261 | { "OPENAT", NOTIFY_OPENAT }, 262 | { "FDOPENDIR", NOTIFY_FDOPENDIR}, 263 | { "O_CLOEXEC", NOTIFY_O_CLOEXEC }, 264 | { "IN_CLOEXEC", NOTIFY_IN_CLOEXEC }, 265 | }; 266 | unsigned i; 267 | 268 | if (luaL_newmetatable(L, CQS_NOTIFY)) { 269 | luaL_setfuncs(L, ln_metatable, 0); 270 | 271 | luaL_newlib(L, ln_methods); 272 | lua_setfield(L, -2, "__index"); 273 | } 274 | 275 | luaL_newlib(L, ln_globals); 276 | 277 | for (i = 0; i < countof(flag); i++) { 278 | lua_pushinteger(L, flag[i].value); 279 | lua_setfield(L, -2, flag[i].name); 280 | 281 | lua_pushinteger(L, flag[i].value); 282 | lua_pushstring(L, flag[i].name); 283 | lua_settable(L, -3); 284 | } 285 | 286 | lua_pushinteger(L, notify_features()); 287 | lua_setfield(L, -2, "FEATURES"); 288 | 289 | return 1; 290 | } /* luaopen__cqueues_notify() */ 291 | -------------------------------------------------------------------------------- /src/notify.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local cqueues = require("cqueues") 3 | local notify = require("_cqueues.notify") 4 | local strerror = require("cqueues.errno").strerror 5 | local ALL = notify.ALL 6 | 7 | local function oops(self, op, why) 8 | local msg = string.format("notify.%s: %s", op, strerror(why)) 9 | error(msg) 10 | end 11 | 12 | -- 13 | -- notify:get 14 | -- 15 | local get; get = notify.interpose("get", function(self, timeout) 16 | local deadline = timeout and (cqueues.monotime() + timeout) 17 | local changes, filename 18 | local ready = function() 19 | local okay, why = self:step() 20 | 21 | if not okay then 22 | oops(self, "get", why) 23 | end 24 | 25 | changes, filename = get(self) 26 | 27 | return changes 28 | end 29 | 30 | while not ready() do 31 | if deadline then 32 | local curtime = cqueues.monotime() 33 | if curtime >= deadline then 34 | return nil 35 | else 36 | cqueues.poll(self, deadline - curtime) 37 | end 38 | else 39 | cqueues.poll(self) 40 | end 41 | end 42 | 43 | return changes, filename 44 | end) 45 | 46 | -- 47 | -- notify:changes 48 | -- 49 | notify.interpose("changes", function(self, timeout) 50 | return function() 51 | return self:get(timeout) 52 | end 53 | end) 54 | 55 | -- 56 | -- notify:add 57 | -- 58 | local add; add = notify.interpose("add", function(self, name, flags) 59 | local okay, why = add(self, name, flags or ALL) 60 | 61 | if not okay then 62 | oops(self, "add", why) 63 | end 64 | 65 | return true 66 | end) 67 | 68 | notify.loader = loader 69 | 70 | return notify 71 | end 72 | 73 | return loader(loader, ...) 74 | -------------------------------------------------------------------------------- /src/promise.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local cqueues = require"cqueues" 3 | local condition = require"cqueues.condition" 4 | local auxlib = require"cqueues.auxlib" 5 | local assert3 = auxlib.assert3 6 | local unpack = assert(table.unpack or unpack) 7 | local pcall = pcall 8 | local error = error 9 | local getmetatable = getmetatable 10 | local tostring = auxlib.tostring 11 | 12 | local promise = {} 13 | 14 | promise.__index = promise -- keep things simple 15 | 16 | function promise.new(f, ...) 17 | local self = setmetatable({ 18 | pollfd = condition.new(), 19 | state = "pending", 20 | }, promise) 21 | 22 | if f then 23 | cqueues.running():wrap(function(f, ...) 24 | self:set(pcall(f, ...)) 25 | end, f, ...) 26 | end 27 | 28 | return self 29 | end -- promise.new 30 | 31 | function promise.type(self) 32 | local mt = getmetatable(self) 33 | 34 | return (mt == promise and "promise") or nil 35 | end -- promise.type 36 | 37 | function promise:set(ok, ...) 38 | assert3(self.state == "pending", "attempt to set value of resolved promise") 39 | 40 | if not ok then 41 | self.state = "rejected" 42 | self.reason = ... 43 | else 44 | self.state = "fulfilled" 45 | self.n = select("#", ...) 46 | 47 | if self.n == 1 then 48 | self.tuple = ... 49 | else 50 | self.tuple = { ... } 51 | end 52 | end 53 | 54 | self.pollfd:signal() 55 | self.timeout = 0 56 | end -- promise:set 57 | 58 | function promise:wait(timeout) 59 | if self.state == "pending" then 60 | self.pollfd:wait(timeout) 61 | end 62 | 63 | return (self.state ~= "pending" and self) or nil 64 | end -- promise:wait 65 | 66 | function promise:get(timeout) 67 | self:wait(timeout) 68 | 69 | if self.state == "fulfilled" then 70 | if self.n == 1 then 71 | return self.tuple 72 | else 73 | return unpack(self.tuple) 74 | end 75 | elseif self.state == "rejected" then 76 | return error(self.reason, 2) 77 | end 78 | end -- promise:get 79 | 80 | function promise:status() 81 | return self.state 82 | end -- promise:status 83 | 84 | -- NOTE: Only LuaJIT supports metamethod yielding. 85 | function promise:__tostring() 86 | return tostring(self:get()) 87 | end -- promise:__tostring 88 | 89 | function promise:__call() 90 | return self:get() 91 | end -- promise:__call 92 | 93 | promise.loader = loader 94 | 95 | return promise 96 | end 97 | 98 | return loader(loader, ...) 99 | -------------------------------------------------------------------------------- /src/signal.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local cqueues = require("cqueues") 3 | local signal = require("_cqueues.signal") 4 | 5 | -- 6 | -- signal:wait 7 | -- 8 | local wait; wait = signal.interpose("wait", function(self, timeout) 9 | local deadline = timeout and (cqueues.monotime() + timeout) 10 | local signo 11 | local ready = function() 12 | signo = wait(self) 13 | return signo 14 | end 15 | 16 | while not ready() do 17 | if deadline then 18 | local curtime = cqueues.monotime() 19 | if curtime >= deadline then 20 | return nil 21 | else 22 | cqueues.poll(self, deadline - curtime) 23 | end 24 | else 25 | cqueues.poll(self) 26 | end 27 | end 28 | 29 | return signo 30 | end) 31 | 32 | signal.loader = loader 33 | 34 | return signal 35 | end 36 | 37 | return loader(loader, ...) 38 | -------------------------------------------------------------------------------- /src/thread.lua: -------------------------------------------------------------------------------- 1 | local loader = function(loader, ...) 2 | local thread = require"_cqueues.thread" 3 | 4 | -- 5 | -- thread.start 6 | -- 7 | local cache = {} 8 | 9 | local function dump(fn) 10 | if type(fn) == "string" then 11 | return fn 12 | else 13 | if not cache[fn] then 14 | cache[fn] = string.dump(fn) 15 | end 16 | 17 | return cache[fn] 18 | end 19 | end 20 | 21 | local include = { 22 | "cqueues", 23 | "cqueues.errno", 24 | "cqueues.socket", 25 | "cqueues.signal", 26 | "cqueues.thread", 27 | "cqueues.notify", 28 | } 29 | 30 | local start = thread.start; thread.start = function(enter, ...) 31 | local function init(self, pipe, nloaders, ...) 32 | local function loadblob(chunk, source, ...) 33 | if _VERSION == "Lua 5.1" then 34 | return loadstring(chunk, source) 35 | else 36 | return load(chunk, source, ...) 37 | end 38 | end 39 | 40 | local function preload(name, code) 41 | local loader = loadblob(code, nil, "bt", _ENV) 42 | package.loaded[name] = loader(loader, name) 43 | end 44 | 45 | local function unpack(n, ...) 46 | if n > 0 then 47 | local name, code = select(1, ...) 48 | preload(name, code) 49 | return unpack(n - 1, select(3, ...)) 50 | else 51 | return ... 52 | end 53 | end 54 | 55 | nloaders = tonumber(nloaders) 56 | 57 | local enter = unpack(nloaders, ...) 58 | 59 | return enter(pipe, select(nloaders * 2 + 2, ...)) 60 | end 61 | 62 | local function pack(i, enter, ...) 63 | if i == 1 then 64 | return init, #include, pack(i + 1, enter, ...) 65 | elseif include[i - 1] then 66 | return include[i - 1], dump(require(include[i - 1]).loader), pack(i + 1, enter, ...) 67 | else 68 | return enter, ... 69 | end 70 | end 71 | 72 | return start(pack(1, enter, ...)) 73 | end 74 | 75 | 76 | -- 77 | -- thread:join 78 | -- 79 | local monotime = require"cqueues".monotime 80 | local poll = require"cqueues".poll 81 | local EAGAIN = require"cqueues.errno".EAGAIN 82 | local ETIMEDOUT = require"cqueues.errno".ETIMEDOUT 83 | 84 | local join; join = thread.interpose("join", function (self, timeout) 85 | local deadline = timeout and (monotime() + timeout) 86 | 87 | while true do 88 | local ok, why = join(self) 89 | 90 | if ok then 91 | return true, why 92 | elseif why ~= EAGAIN then 93 | return false, why 94 | else 95 | if deadline then 96 | local curtime = monotime() 97 | 98 | if curtime >= deadline then 99 | return false, ETIMEDOUT 100 | else 101 | poll(self, deadline - curtime) 102 | end 103 | else 104 | poll(self) 105 | end 106 | end 107 | end 108 | end) 109 | 110 | 111 | thread.loader = loader 112 | 113 | return thread 114 | end -- loader 115 | 116 | return loader(loader, ...) 117 | -------------------------------------------------------------------------------- /util/Makefile: -------------------------------------------------------------------------------- 1 | CPPFLAGS= 2 | CFLAGS=-Wall -Wextra 3 | 4 | tcp-urgent: 5 | -------------------------------------------------------------------------------- /vendor/compat53/.gitignore: -------------------------------------------------------------------------------- 1 | # generated files 2 | *.so 3 | *.dll 4 | *.o 5 | *.obj 6 | HISTO 7 | 8 | # vim temporaries 9 | .*.swp 10 | 11 | -------------------------------------------------------------------------------- /vendor/compat53/.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | compiler: gcc 3 | 4 | sudo: false 5 | 6 | env: 7 | - LUA="lua=5.1" 8 | - LUA="lua=5.1" EXTERNAL=true 9 | - LUA="lua=5.1" COMPILER="g++" 10 | - LUA="lua=5.1" EXTERNAL=true COMPILER="g++" 11 | - LUA="luajit=@v2.1 --compat=none" 12 | - LUA="luajit=@v2.1 --compat=none" EXTERNAL=true 13 | - LUA="luajit=@v2.1 --compat=all" 14 | - LUA="luajit=@v2.1 --compat=all" EXTERNAL=true 15 | - LUA="lua=5.2" 16 | - LUA="lua=5.2" EXTERNAL=true 17 | - LUA="lua=5.2" COMPILER="g++" 18 | - LUA="lua=5.2" EXTERNAL=true COMPILER="g++" 19 | 20 | branches: 21 | only: 22 | - master 23 | 24 | git: 25 | depth: 3 26 | 27 | notifications: 28 | email: false 29 | 30 | before_install: 31 | - pip install --user hererocks 32 | - hererocks old --$LUA 33 | - test -e old/bin/lua || (cd old/bin && ln -s luajit* lua) 34 | - hererocks new --lua=5.3 35 | 36 | install: 37 | - export CC="${COMPILER:-gcc}" DEF="" SRC="" CFLAGS="-Wall -Wextra -Ic-api -O2 -fPIC" 38 | - if [ "x${EXTERNAL:-}" = xtrue ]; then DEF="-DCOMPAT53_PREFIX=compat53" SRC="c-api/compat-5.3.c"; fi 39 | - ${CC} ${CFLAGS} -Iold/include ${DEF} -shared -o old/testmod.so tests/testmod.c ${SRC} 40 | - ${CC} ${CFLAGS} -Inew/include ${DEF} -shared -o new/testmod.so tests/testmod.c ${SRC} 41 | - gcc ${CFLAGS} -Iold/include ${DEF} -shared -o old/compat53.so ltablib.c lutf8lib.c lstrlib.c ${SRC} 42 | 43 | script: 44 | - (cd old && bin/lua ../tests/test.lua) > old.txt 45 | - (cd new && bin/lua ../tests/test.lua) > new.txt 46 | - diff old.txt new.txt || true 47 | 48 | -------------------------------------------------------------------------------- /vendor/compat53/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kepler Project. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /vendor/compat53/lbitlib.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lbitlib.c,v 1.30.1.1 2017/04/19 17:20:42 roberto Exp $ 3 | ** Standard library for bitwise operations 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lbitlib_c 8 | #define LUA_LIB 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include "lua.h" 14 | 15 | #include "lauxlib.h" 16 | #include "lualib.h" 17 | 18 | 19 | #if defined(LUA_COMPAT_BITLIB) /* { */ 20 | 21 | 22 | #define pushunsigned(L,n) lua_pushinteger(L, (lua_Integer)(n)) 23 | #define checkunsigned(L,i) ((lua_Unsigned)luaL_checkinteger(L,i)) 24 | 25 | 26 | /* number of bits to consider in a number */ 27 | #if !defined(LUA_NBITS) 28 | #define LUA_NBITS 32 29 | #endif 30 | 31 | 32 | /* 33 | ** a lua_Unsigned with its first LUA_NBITS bits equal to 1. (Shift must 34 | ** be made in two parts to avoid problems when LUA_NBITS is equal to the 35 | ** number of bits in a lua_Unsigned.) 36 | */ 37 | #define ALLONES (~(((~(lua_Unsigned)0) << (LUA_NBITS - 1)) << 1)) 38 | 39 | 40 | /* macro to trim extra bits */ 41 | #define trim(x) ((x) & ALLONES) 42 | 43 | 44 | /* builds a number with 'n' ones (1 <= n <= LUA_NBITS) */ 45 | #define mask(n) (~((ALLONES << 1) << ((n) - 1))) 46 | 47 | 48 | 49 | static lua_Unsigned andaux (lua_State *L) { 50 | int i, n = lua_gettop(L); 51 | lua_Unsigned r = ~(lua_Unsigned)0; 52 | for (i = 1; i <= n; i++) 53 | r &= checkunsigned(L, i); 54 | return trim(r); 55 | } 56 | 57 | 58 | static int b_and (lua_State *L) { 59 | lua_Unsigned r = andaux(L); 60 | pushunsigned(L, r); 61 | return 1; 62 | } 63 | 64 | 65 | static int b_test (lua_State *L) { 66 | lua_Unsigned r = andaux(L); 67 | lua_pushboolean(L, r != 0); 68 | return 1; 69 | } 70 | 71 | 72 | static int b_or (lua_State *L) { 73 | int i, n = lua_gettop(L); 74 | lua_Unsigned r = 0; 75 | for (i = 1; i <= n; i++) 76 | r |= checkunsigned(L, i); 77 | pushunsigned(L, trim(r)); 78 | return 1; 79 | } 80 | 81 | 82 | static int b_xor (lua_State *L) { 83 | int i, n = lua_gettop(L); 84 | lua_Unsigned r = 0; 85 | for (i = 1; i <= n; i++) 86 | r ^= checkunsigned(L, i); 87 | pushunsigned(L, trim(r)); 88 | return 1; 89 | } 90 | 91 | 92 | static int b_not (lua_State *L) { 93 | lua_Unsigned r = ~checkunsigned(L, 1); 94 | pushunsigned(L, trim(r)); 95 | return 1; 96 | } 97 | 98 | 99 | static int b_shift (lua_State *L, lua_Unsigned r, lua_Integer i) { 100 | if (i < 0) { /* shift right? */ 101 | i = -i; 102 | r = trim(r); 103 | if (i >= LUA_NBITS) r = 0; 104 | else r >>= i; 105 | } 106 | else { /* shift left */ 107 | if (i >= LUA_NBITS) r = 0; 108 | else r <<= i; 109 | r = trim(r); 110 | } 111 | pushunsigned(L, r); 112 | return 1; 113 | } 114 | 115 | 116 | static int b_lshift (lua_State *L) { 117 | return b_shift(L, checkunsigned(L, 1), luaL_checkinteger(L, 2)); 118 | } 119 | 120 | 121 | static int b_rshift (lua_State *L) { 122 | return b_shift(L, checkunsigned(L, 1), -luaL_checkinteger(L, 2)); 123 | } 124 | 125 | 126 | static int b_arshift (lua_State *L) { 127 | lua_Unsigned r = checkunsigned(L, 1); 128 | lua_Integer i = luaL_checkinteger(L, 2); 129 | if (i < 0 || !(r & ((lua_Unsigned)1 << (LUA_NBITS - 1)))) 130 | return b_shift(L, r, -i); 131 | else { /* arithmetic shift for 'negative' number */ 132 | if (i >= LUA_NBITS) r = ALLONES; 133 | else 134 | r = trim((r >> i) | ~(trim(~(lua_Unsigned)0) >> i)); /* add signal bit */ 135 | pushunsigned(L, r); 136 | return 1; 137 | } 138 | } 139 | 140 | 141 | static int b_rot (lua_State *L, lua_Integer d) { 142 | lua_Unsigned r = checkunsigned(L, 1); 143 | int i = d & (LUA_NBITS - 1); /* i = d % NBITS */ 144 | r = trim(r); 145 | if (i != 0) /* avoid undefined shift of LUA_NBITS when i == 0 */ 146 | r = (r << i) | (r >> (LUA_NBITS - i)); 147 | pushunsigned(L, trim(r)); 148 | return 1; 149 | } 150 | 151 | 152 | static int b_lrot (lua_State *L) { 153 | return b_rot(L, luaL_checkinteger(L, 2)); 154 | } 155 | 156 | 157 | static int b_rrot (lua_State *L) { 158 | return b_rot(L, -luaL_checkinteger(L, 2)); 159 | } 160 | 161 | 162 | /* 163 | ** get field and width arguments for field-manipulation functions, 164 | ** checking whether they are valid. 165 | ** ('luaL_error' called without 'return' to avoid later warnings about 166 | ** 'width' being used uninitialized.) 167 | */ 168 | static int fieldargs (lua_State *L, int farg, int *width) { 169 | lua_Integer f = luaL_checkinteger(L, farg); 170 | lua_Integer w = luaL_optinteger(L, farg + 1, 1); 171 | luaL_argcheck(L, 0 <= f, farg, "field cannot be negative"); 172 | luaL_argcheck(L, 0 < w, farg + 1, "width must be positive"); 173 | if (f + w > LUA_NBITS) 174 | luaL_error(L, "trying to access non-existent bits"); 175 | *width = (int)w; 176 | return (int)f; 177 | } 178 | 179 | 180 | static int b_extract (lua_State *L) { 181 | int w; 182 | lua_Unsigned r = trim(checkunsigned(L, 1)); 183 | int f = fieldargs(L, 2, &w); 184 | r = (r >> f) & mask(w); 185 | pushunsigned(L, r); 186 | return 1; 187 | } 188 | 189 | 190 | static int b_replace (lua_State *L) { 191 | int w; 192 | lua_Unsigned r = trim(checkunsigned(L, 1)); 193 | lua_Unsigned v = trim(checkunsigned(L, 2)); 194 | int f = fieldargs(L, 3, &w); 195 | lua_Unsigned m = mask(w); 196 | r = (r & ~(m << f)) | ((v & m) << f); 197 | pushunsigned(L, r); 198 | return 1; 199 | } 200 | 201 | 202 | static const luaL_Reg bitlib[] = { 203 | {"arshift", b_arshift}, 204 | {"band", b_and}, 205 | {"bnot", b_not}, 206 | {"bor", b_or}, 207 | {"bxor", b_xor}, 208 | {"btest", b_test}, 209 | {"extract", b_extract}, 210 | {"lrotate", b_lrot}, 211 | {"lshift", b_lshift}, 212 | {"replace", b_replace}, 213 | {"rrotate", b_rrot}, 214 | {"rshift", b_rshift}, 215 | {NULL, NULL} 216 | }; 217 | 218 | 219 | 220 | LUAMOD_API int luaopen_bit32 (lua_State *L) { 221 | luaL_newlib(L, bitlib); 222 | return 1; 223 | } 224 | 225 | 226 | #else /* }{ */ 227 | 228 | 229 | LUAMOD_API int luaopen_bit32 (lua_State *L) { 230 | return luaL_error(L, "library 'bit32' has been deprecated"); 231 | } 232 | 233 | #endif /* } */ 234 | -------------------------------------------------------------------------------- /vendor/compat53/lprefix.h: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lprefix.h,v 1.2 2014/12/29 16:54:13 roberto Exp $ 3 | ** Definitions for Lua code that must come before any other header file 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #ifndef lprefix_h 8 | #define lprefix_h 9 | 10 | 11 | /* 12 | ** Allows POSIX/XSI stuff 13 | */ 14 | #if !defined(LUA_USE_C89) /* { */ 15 | 16 | #if !defined(_XOPEN_SOURCE) 17 | #define _XOPEN_SOURCE 600 18 | #elif _XOPEN_SOURCE == 0 19 | #undef _XOPEN_SOURCE /* use -D_XOPEN_SOURCE=0 to undefine it */ 20 | #endif 21 | 22 | /* 23 | ** Allows manipulation of large files in gcc and some other compilers 24 | */ 25 | #if !defined(LUA_32BITS) && !defined(_FILE_OFFSET_BITS) 26 | #define _LARGEFILE_SOURCE 1 27 | #define _FILE_OFFSET_BITS 64 28 | #endif 29 | 30 | #endif /* } */ 31 | 32 | 33 | /* 34 | ** Windows stuff 35 | */ 36 | #if defined(_WIN32) /* { */ 37 | 38 | #if !defined(_CRT_SECURE_NO_WARNINGS) 39 | #define _CRT_SECURE_NO_WARNINGS /* avoid warnings about ISO C functions */ 40 | #endif 41 | 42 | #endif /* } */ 43 | 44 | 45 | /* COMPAT53 adaptation */ 46 | #include "c-api/compat-5.3.h" 47 | 48 | #undef LUAMOD_API 49 | #define LUAMOD_API extern 50 | 51 | 52 | #ifdef lutf8lib_c 53 | # define luaopen_utf8 luaopen_compat53_utf8 54 | /* we don't support the %U format string of lua_pushfstring! 55 | * code below adapted from the Lua 5.3 sources: 56 | */ 57 | static const char *compat53_utf8_escape (lua_State* L, long x) { 58 | if (x < 0x80) { /* ASCII */ 59 | char c = (char)x; 60 | lua_pushlstring(L, &c, 1); 61 | } else { 62 | char buff[8] = { 0 }; 63 | unsigned int mfb = 0x3f; 64 | int n = 1; 65 | do { 66 | buff[8 - (n++)] = (char)(0x80|(x & 0x3f)); 67 | x >>= 6; 68 | mfb >>= 1; 69 | } while (x > mfb); 70 | buff[8-n] = (char)((~mfb << 1) | x); 71 | lua_pushlstring(L, buff+8-n, n); 72 | } 73 | return lua_tostring(L, -1); 74 | } 75 | # define lua_pushfstring(L, fmt, l) \ 76 | compat53_utf8_escape(L, l) 77 | #endif 78 | 79 | 80 | #ifdef ltablib_c 81 | # define luaopen_table luaopen_compat53_table 82 | # ifndef LUA_MAXINTEGER 83 | /* conservative estimate: */ 84 | # define LUA_MAXINTEGER INT_MAX 85 | # endif 86 | #endif /* ltablib_c */ 87 | 88 | 89 | #ifdef lstrlib_c 90 | #include 91 | #include 92 | /* move the string library open function out of the way (we only take 93 | * the string packing functions)! 94 | */ 95 | # define luaopen_string luaopen_string_XXX 96 | /* used in string.format implementation, which we don't use: */ 97 | # ifndef LUA_INTEGER_FRMLEN 98 | # define LUA_INTEGER_FRMLEN "" 99 | # define LUA_NUMBER_FRMLEN "" 100 | # endif 101 | # ifndef LUA_MININTEGER 102 | # define LUA_MININTEGER 0 103 | # endif 104 | # ifndef LUA_INTEGER_FMT 105 | # define LUA_INTEGER_FMT "%d" 106 | # endif 107 | # ifndef LUAI_UACINT 108 | # define LUAI_UACINT lua_Integer 109 | # endif 110 | /* different Lua 5.3 versions have conflicting variants of this macro 111 | * in luaconf.h, there's a fallback implementation in lstrlib.c, and 112 | * the macro isn't used for string (un)packing anyway! 113 | * */ 114 | # undef lua_number2strx 115 | # if LUA_VERSION_NUM < 503 116 | /* lstrlib assumes that lua_Integer and lua_Unsigned have the same 117 | * size, so we use the unsigned equivalent of ptrdiff_t! */ 118 | # define lua_Unsigned size_t 119 | # endif 120 | # ifndef l_mathlim 121 | # ifdef LUA_NUMBER_DOUBLE 122 | # define l_mathlim(n) (DBL_##n) 123 | # else 124 | # define l_mathlim(n) (FLT_##n) 125 | # endif 126 | # endif 127 | # ifndef l_mathop 128 | # ifdef LUA_NUMBER_DOUBLE 129 | # define l_mathop(op) op 130 | # else 131 | # define l_mathop(op) op##f 132 | # endif 133 | # endif 134 | # ifndef lua_getlocaledecpoint 135 | # define lua_getlocaledecpoint() (localeconv()->decimal_point[0]) 136 | # endif 137 | # ifndef l_sprintf 138 | # if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L 139 | # define l_sprintf(s,sz,f,i) (snprintf(s, sz, f, i)) 140 | # else 141 | # define l_sprintf(s,sz,f,i) ((void)(sz), sprintf(s, f, i)) 142 | # endif 143 | # endif 144 | 145 | static int str_pack (lua_State *L); 146 | static int str_packsize (lua_State *L); 147 | static int str_unpack (lua_State *L); 148 | LUAMOD_API int luaopen_compat53_string (lua_State *L) { 149 | luaL_Reg const funcs[] = { 150 | { "pack", str_pack }, 151 | { "packsize", str_packsize }, 152 | { "unpack", str_unpack }, 153 | { NULL, NULL } 154 | }; 155 | luaL_newlib(L, funcs); 156 | return 1; 157 | } 158 | /* fake CLANG feature detection on other compilers */ 159 | # ifndef __has_attribute 160 | # define __has_attribute(x) 0 161 | # endif 162 | /* make luaopen_string(_XXX) static, so it (and all other referenced 163 | * string functions) won't be included in the resulting dll 164 | * (hopefully). 165 | */ 166 | # undef LUAMOD_API 167 | # if defined(__GNUC__) || __has_attribute(__unused__) 168 | # define LUAMOD_API __attribute__((__unused__)) static 169 | # else 170 | # define LUAMOD_API static 171 | # endif 172 | #endif /* lstrlib.c */ 173 | 174 | #endif 175 | 176 | -------------------------------------------------------------------------------- /vendor/compat53/lutf8lib.c: -------------------------------------------------------------------------------- 1 | /* 2 | ** $Id: lutf8lib.c,v 1.16.1.1 2017/04/19 17:29:57 roberto Exp $ 3 | ** Standard library for UTF-8 manipulation 4 | ** See Copyright Notice in lua.h 5 | */ 6 | 7 | #define lutf8lib_c 8 | #define LUA_LIB 9 | 10 | #include "lprefix.h" 11 | 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "lua.h" 19 | 20 | #include "lauxlib.h" 21 | #include "lualib.h" 22 | 23 | #define MAXUNICODE 0x10FFFF 24 | 25 | #define iscont(p) ((*(p) & 0xC0) == 0x80) 26 | 27 | 28 | /* from strlib */ 29 | /* translate a relative string position: negative means back from end */ 30 | static lua_Integer u_posrelat (lua_Integer pos, size_t len) { 31 | if (pos >= 0) return pos; 32 | else if (0u - (size_t)pos > len) return 0; 33 | else return (lua_Integer)len + pos + 1; 34 | } 35 | 36 | 37 | /* 38 | ** Decode one UTF-8 sequence, returning NULL if byte sequence is invalid. 39 | */ 40 | static const char *utf8_decode (const char *o, int *val) { 41 | static const unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF}; 42 | const unsigned char *s = (const unsigned char *)o; 43 | unsigned int c = s[0]; 44 | unsigned int res = 0; /* final result */ 45 | if (c < 0x80) /* ascii? */ 46 | res = c; 47 | else { 48 | int count = 0; /* to count number of continuation bytes */ 49 | while (c & 0x40) { /* still have continuation bytes? */ 50 | int cc = s[++count]; /* read next byte */ 51 | if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ 52 | return NULL; /* invalid byte sequence */ 53 | res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ 54 | c <<= 1; /* to test next bit */ 55 | } 56 | res |= ((c & 0x7F) << (count * 5)); /* add first byte */ 57 | if (count > 3 || res > MAXUNICODE || res <= limits[count]) 58 | return NULL; /* invalid byte sequence */ 59 | s += count; /* skip continuation bytes read */ 60 | } 61 | if (val) *val = res; 62 | return (const char *)s + 1; /* +1 to include first byte */ 63 | } 64 | 65 | 66 | /* 67 | ** utf8len(s [, i [, j]]) --> number of characters that start in the 68 | ** range [i,j], or nil + current position if 's' is not well formed in 69 | ** that interval 70 | */ 71 | static int utflen (lua_State *L) { 72 | int n = 0; 73 | size_t len; 74 | const char *s = luaL_checklstring(L, 1, &len); 75 | lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); 76 | lua_Integer posj = u_posrelat(luaL_optinteger(L, 3, -1), len); 77 | luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 2, 78 | "initial position out of string"); 79 | luaL_argcheck(L, --posj < (lua_Integer)len, 3, 80 | "final position out of string"); 81 | while (posi <= posj) { 82 | const char *s1 = utf8_decode(s + posi, NULL); 83 | if (s1 == NULL) { /* conversion error? */ 84 | lua_pushnil(L); /* return nil ... */ 85 | lua_pushinteger(L, posi + 1); /* ... and current position */ 86 | return 2; 87 | } 88 | posi = s1 - s; 89 | n++; 90 | } 91 | lua_pushinteger(L, n); 92 | return 1; 93 | } 94 | 95 | 96 | /* 97 | ** codepoint(s, [i, [j]]) -> returns codepoints for all characters 98 | ** that start in the range [i,j] 99 | */ 100 | static int codepoint (lua_State *L) { 101 | size_t len; 102 | const char *s = luaL_checklstring(L, 1, &len); 103 | lua_Integer posi = u_posrelat(luaL_optinteger(L, 2, 1), len); 104 | lua_Integer pose = u_posrelat(luaL_optinteger(L, 3, posi), len); 105 | int n; 106 | const char *se; 107 | luaL_argcheck(L, posi >= 1, 2, "out of range"); 108 | luaL_argcheck(L, pose <= (lua_Integer)len, 3, "out of range"); 109 | if (posi > pose) return 0; /* empty interval; return no values */ 110 | if (pose - posi >= INT_MAX) /* (lua_Integer -> int) overflow? */ 111 | return luaL_error(L, "string slice too long"); 112 | n = (int)(pose - posi) + 1; 113 | luaL_checkstack(L, n, "string slice too long"); 114 | n = 0; 115 | se = s + pose; 116 | for (s += posi - 1; s < se;) { 117 | int code; 118 | s = utf8_decode(s, &code); 119 | if (s == NULL) 120 | return luaL_error(L, "invalid UTF-8 code"); 121 | lua_pushinteger(L, code); 122 | n++; 123 | } 124 | return n; 125 | } 126 | 127 | 128 | static void pushutfchar (lua_State *L, int arg) { 129 | lua_Integer code = luaL_checkinteger(L, arg); 130 | luaL_argcheck(L, 0 <= code && code <= MAXUNICODE, arg, "value out of range"); 131 | lua_pushfstring(L, "%U", (long)code); 132 | } 133 | 134 | 135 | /* 136 | ** utfchar(n1, n2, ...) -> char(n1)..char(n2)... 137 | */ 138 | static int utfchar (lua_State *L) { 139 | int n = lua_gettop(L); /* number of arguments */ 140 | if (n == 1) /* optimize common case of single char */ 141 | pushutfchar(L, 1); 142 | else { 143 | int i; 144 | luaL_Buffer b; 145 | luaL_buffinit(L, &b); 146 | for (i = 1; i <= n; i++) { 147 | pushutfchar(L, i); 148 | luaL_addvalue(&b); 149 | } 150 | luaL_pushresult(&b); 151 | } 152 | return 1; 153 | } 154 | 155 | 156 | /* 157 | ** offset(s, n, [i]) -> index where n-th character counting from 158 | ** position 'i' starts; 0 means character at 'i'. 159 | */ 160 | static int byteoffset (lua_State *L) { 161 | size_t len; 162 | const char *s = luaL_checklstring(L, 1, &len); 163 | lua_Integer n = luaL_checkinteger(L, 2); 164 | lua_Integer posi = (n >= 0) ? 1 : len + 1; 165 | posi = u_posrelat(luaL_optinteger(L, 3, posi), len); 166 | luaL_argcheck(L, 1 <= posi && --posi <= (lua_Integer)len, 3, 167 | "position out of range"); 168 | if (n == 0) { 169 | /* find beginning of current byte sequence */ 170 | while (posi > 0 && iscont(s + posi)) posi--; 171 | } 172 | else { 173 | if (iscont(s + posi)) 174 | return luaL_error(L, "initial position is a continuation byte"); 175 | if (n < 0) { 176 | while (n < 0 && posi > 0) { /* move back */ 177 | do { /* find beginning of previous character */ 178 | posi--; 179 | } while (posi > 0 && iscont(s + posi)); 180 | n++; 181 | } 182 | } 183 | else { 184 | n--; /* do not move for 1st character */ 185 | while (n > 0 && posi < (lua_Integer)len) { 186 | do { /* find beginning of next character */ 187 | posi++; 188 | } while (iscont(s + posi)); /* (cannot pass final '\0') */ 189 | n--; 190 | } 191 | } 192 | } 193 | if (n == 0) /* did it find given character? */ 194 | lua_pushinteger(L, posi + 1); 195 | else /* no such character */ 196 | lua_pushnil(L); 197 | return 1; 198 | } 199 | 200 | 201 | static int iter_aux (lua_State *L) { 202 | size_t len; 203 | const char *s = luaL_checklstring(L, 1, &len); 204 | lua_Integer n = lua_tointeger(L, 2) - 1; 205 | if (n < 0) /* first iteration? */ 206 | n = 0; /* start from here */ 207 | else if (n < (lua_Integer)len) { 208 | n++; /* skip current byte */ 209 | while (iscont(s + n)) n++; /* and its continuations */ 210 | } 211 | if (n >= (lua_Integer)len) 212 | return 0; /* no more codepoints */ 213 | else { 214 | int code; 215 | const char *next = utf8_decode(s + n, &code); 216 | if (next == NULL || iscont(next)) 217 | return luaL_error(L, "invalid UTF-8 code"); 218 | lua_pushinteger(L, n + 1); 219 | lua_pushinteger(L, code); 220 | return 2; 221 | } 222 | } 223 | 224 | 225 | static int iter_codes (lua_State *L) { 226 | luaL_checkstring(L, 1); 227 | lua_pushcfunction(L, iter_aux); 228 | lua_pushvalue(L, 1); 229 | lua_pushinteger(L, 0); 230 | return 3; 231 | } 232 | 233 | 234 | /* pattern to match a single UTF-8 character */ 235 | #define UTF8PATT "[\0-\x7F\xC2-\xF4][\x80-\xBF]*" 236 | 237 | 238 | static const luaL_Reg funcs[] = { 239 | {"offset", byteoffset}, 240 | {"codepoint", codepoint}, 241 | {"char", utfchar}, 242 | {"len", utflen}, 243 | {"codes", iter_codes}, 244 | /* placeholders */ 245 | {"charpattern", NULL}, 246 | {NULL, NULL} 247 | }; 248 | 249 | 250 | LUAMOD_API int luaopen_utf8 (lua_State *L) { 251 | luaL_newlib(L, funcs); 252 | lua_pushlstring(L, UTF8PATT, sizeof(UTF8PATT)/sizeof(char) - 1); 253 | lua_setfield(L, -2, "charpattern"); 254 | return 1; 255 | } 256 | 257 | -------------------------------------------------------------------------------- /vendor/compat53/rockspecs/bit32-5.3.5-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "bit32" 2 | version = "5.3.5-1" 3 | source = { 4 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.9.zip", 5 | dir = "lua-compat-5.3-0.9", 6 | } 7 | description = { 8 | summary = "Lua 5.2 bit manipulation library", 9 | detailed = [[ 10 | bit32 is the native Lua 5.2 bit manipulation library, in the version 11 | from Lua 5.3; it is compatible with Lua 5.1, 5.2 and 5.3. 12 | ]], 13 | homepage = "http://www.lua.org/manual/5.2/manual.html#6.7", 14 | license = "MIT" 15 | } 16 | dependencies = { 17 | "lua >= 5.1, < 5.5" 18 | } 19 | build = { 20 | type = "builtin", 21 | modules = { 22 | bit32 = { 23 | sources = { "lbitlib.c" }, 24 | defines = { "LUA_COMPAT_BITLIB" }, 25 | incdirs = { "c-api" }, 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /vendor/compat53/rockspecs/bit32-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "bit32" 2 | version = "scm-1" 3 | source = { 4 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/master.zip", 5 | branch = "lua-compat-5.3-master", 6 | } 7 | description = { 8 | summary = "Lua 5.2 bit manipulation library", 9 | detailed = [[ 10 | bit32 is the native Lua 5.2 bit manipulation library, in the version 11 | from Lua 5.3; it is compatible with Lua 5.1, 5.2 and 5.3. 12 | ]], 13 | homepage = "http://www.lua.org/manual/5.2/manual.html#6.7", 14 | license = "MIT" 15 | } 16 | dependencies = { 17 | "lua >= 5.1, < 5.5" 18 | } 19 | build = { 20 | type = "builtin", 21 | modules = { 22 | bit32 = { 23 | sources = { "lbitlib.c" }, 24 | defines = { "LUA_COMPAT_BITLIB" }, 25 | incdirs = { "c-api" }, 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /vendor/compat53/rockspecs/compat53-0.1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "compat53" 2 | version = "0.1-1" 3 | source = { 4 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.1.zip", 5 | dir = "lua-compat-5.3-0.1", 6 | } 7 | description = { 8 | summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1", 9 | detailed = [[ 10 | This is a small module that aims to make it easier to write Lua 11 | code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1. 12 | It does *not* make Lua 5.2 (or even 5.1) entirely compatible 13 | with Lua 5.3, but it brings the API closer to that of Lua 5.3. 14 | ]], 15 | homepage = "https://github.com/keplerproject/lua-compat-5.3", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1, < 5.4", 20 | --"struct" -- make Roberto's struct module optional 21 | } 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["compat53"] = "compat53.lua", 26 | ["compat53.utf8"] = "lutf8lib.c", 27 | ["compat53.table"] = "ltablib.c", 28 | ["compat53.string"] = "lstrlib.c", 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /vendor/compat53/rockspecs/compat53-0.2-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "compat53" 2 | version = "0.2-1" 3 | source = { 4 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.2.zip", 5 | dir = "lua-compat-5.3-0.2", 6 | } 7 | description = { 8 | summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1", 9 | detailed = [[ 10 | This is a small module that aims to make it easier to write Lua 11 | code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1. 12 | It does *not* make Lua 5.2 (or even 5.1) entirely compatible 13 | with Lua 5.3, but it brings the API closer to that of Lua 5.3. 14 | ]], 15 | homepage = "https://github.com/keplerproject/lua-compat-5.3", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1, < 5.4", 20 | --"struct" -- make Roberto's struct module optional 21 | } 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["compat53.init"] = "compat53/init.lua", 26 | ["compat53.module"] = "compat53/module.lua", 27 | ["compat53.utf8"] = "lutf8lib.c", 28 | ["compat53.table"] = "ltablib.c", 29 | ["compat53.string"] = "lstrlib.c", 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /vendor/compat53/rockspecs/compat53-0.3-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "compat53" 2 | version = "0.3-1" 3 | source = { 4 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.3.zip", 5 | dir = "lua-compat-5.3-0.3", 6 | } 7 | description = { 8 | summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1", 9 | detailed = [[ 10 | This is a small module that aims to make it easier to write Lua 11 | code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1. 12 | It does *not* make Lua 5.2 (or even 5.1) entirely compatible 13 | with Lua 5.3, but it brings the API closer to that of Lua 5.3. 14 | ]], 15 | homepage = "https://github.com/keplerproject/lua-compat-5.3", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1, < 5.4", 20 | --"struct" -- make Roberto's struct module optional 21 | } 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["compat53.init"] = "compat53/init.lua", 26 | ["compat53.module"] = "compat53/module.lua", 27 | ["compat53.utf8"] = "lutf8lib.c", 28 | ["compat53.table"] = "ltablib.c", 29 | ["compat53.string"] = "lstrlib.c", 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /vendor/compat53/rockspecs/compat53-0.4-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "compat53" 2 | version = "0.4-1" 3 | source = { 4 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.4.zip", 5 | dir = "lua-compat-5.3-0.4", 6 | } 7 | description = { 8 | summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1", 9 | detailed = [[ 10 | This is a small module that aims to make it easier to write Lua 11 | code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1. 12 | It does *not* make Lua 5.2 (or even 5.1) entirely compatible 13 | with Lua 5.3, but it brings the API closer to that of Lua 5.3. 14 | ]], 15 | homepage = "https://github.com/keplerproject/lua-compat-5.3", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1, < 5.4", 20 | --"struct" -- make Roberto's struct module optional 21 | } 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["compat53.init"] = "compat53/init.lua", 26 | ["compat53.module"] = "compat53/module.lua", 27 | ["compat53.utf8"] = "lutf8lib.c", 28 | ["compat53.table"] = "ltablib.c", 29 | ["compat53.string"] = "lstrlib.c", 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /vendor/compat53/rockspecs/compat53-0.5-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "compat53" 2 | version = "0.5-1" 3 | source = { 4 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.5.zip", 5 | dir = "lua-compat-5.3-0.5", 6 | } 7 | description = { 8 | summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1", 9 | detailed = [[ 10 | This is a small module that aims to make it easier to write Lua 11 | code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1. 12 | It does *not* make Lua 5.2 (or even 5.1) entirely compatible 13 | with Lua 5.3, but it brings the API closer to that of Lua 5.3. 14 | ]], 15 | homepage = "https://github.com/keplerproject/lua-compat-5.3", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1, < 5.4", 20 | --"struct" -- make Roberto's struct module optional 21 | } 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["compat53.init"] = "compat53/init.lua", 26 | ["compat53.module"] = "compat53/module.lua", 27 | ["compat53.utf8"] = "lutf8lib.c", 28 | ["compat53.table"] = "ltablib.c", 29 | ["compat53.string"] = "lstrlib.c", 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /vendor/compat53/rockspecs/compat53-0.7-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "compat53" 2 | version = "0.7-1" 3 | source = { 4 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.7.zip", 5 | dir = "lua-compat-5.3-0.7", 6 | } 7 | description = { 8 | summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1", 9 | detailed = [[ 10 | This is a small module that aims to make it easier to write Lua 11 | code in a Lua-5.3-style that runs on Lua 5.3, 5.2, and 5.1. 12 | It does *not* make Lua 5.2 (or even 5.1) entirely compatible 13 | with Lua 5.3, but it brings the API closer to that of Lua 5.3. 14 | ]], 15 | homepage = "https://github.com/keplerproject/lua-compat-5.3", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1, < 5.4", 20 | --"struct" -- make Roberto's struct module optional 21 | } 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["compat53.init"] = "compat53/init.lua", 26 | ["compat53.module"] = "compat53/module.lua", 27 | ["compat53.utf8"] = "lutf8lib.c", 28 | ["compat53.table"] = "ltablib.c", 29 | ["compat53.string"] = "lstrlib.c", 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /vendor/compat53/rockspecs/compat53-0.8-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "compat53" 2 | version = "0.8-1" 3 | source = { 4 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/v0.8.zip", 5 | dir = "lua-compat-5.3-0.8", 6 | } 7 | description = { 8 | summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1", 9 | detailed = [[ 10 | This is a small module that aims to make it easier to write Lua 11 | code in a Lua-5.3-style that runs on Lua 5.1+. 12 | It does *not* make Lua 5.2 (or even 5.1) entirely compatible 13 | with Lua 5.3, but it brings the API closer to that of Lua 5.3. 14 | ]], 15 | homepage = "https://github.com/keplerproject/lua-compat-5.3", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1, < 5.5", 20 | --"struct" -- make Roberto's struct module optional 21 | } 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["compat53.init"] = "compat53/init.lua", 26 | ["compat53.module"] = "compat53/module.lua", 27 | ["compat53.utf8"] = "lutf8lib.c", 28 | ["compat53.table"] = "ltablib.c", 29 | ["compat53.string"] = "lstrlib.c", 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /vendor/compat53/rockspecs/compat53-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "compat53" 2 | version = "scm-0" 3 | source = { 4 | url = "https://github.com/keplerproject/lua-compat-5.3/archive/master.zip", 5 | dir = "lua-compat-5.3-master", 6 | } 7 | description = { 8 | summary = "Compatibility module providing Lua-5.3-style APIs for Lua 5.2 and 5.1", 9 | detailed = [[ 10 | This is a small module that aims to make it easier to write Lua 11 | code in a Lua-5.3-style that runs on Lua 5.1+. 12 | It does *not* make Lua 5.2 (or even 5.1) entirely compatible 13 | with Lua 5.3, but it brings the API closer to that of Lua 5.3. 14 | ]], 15 | homepage = "https://github.com/keplerproject/lua-compat-5.3", 16 | license = "MIT" 17 | } 18 | dependencies = { 19 | "lua >= 5.1, < 5.5", 20 | --"struct" -- make Roberto's struct module optional 21 | } 22 | build = { 23 | type = "builtin", 24 | modules = { 25 | ["compat53.init"] = "compat53/init.lua", 26 | ["compat53.module"] = "compat53/module.lua", 27 | ["compat53.utf8"] = "lutf8lib.c", 28 | ["compat53.table"] = "ltablib.c", 29 | ["compat53.string"] = "lstrlib.c", 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /vendor/compat53/tests/test-bit32.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | local bit32 = require("bit32") 4 | 5 | 6 | assert(bit32.bnot(0) == 2^32-1) 7 | assert(bit32.band(1, 3, 5) == 1) 8 | assert(bit32.bor(1, 3, 5) == 7) 9 | 10 | --------------------------------------------------------------------------------