├── Makefile ├── Makefile.win ├── README.md ├── configure ├── doc ├── coxpcall.png ├── doc.css ├── index.html └── license.html ├── rockspec ├── coxpcall-1.11.0-1.rockspec ├── coxpcall-1.12.0-1.rockspec ├── coxpcall-1.12.0rc1-1.rockspec ├── coxpcall-1.13.0-1.rockspec ├── coxpcall-1.13.0-2.rockspec ├── coxpcall-1.14.0-1.rockspec ├── coxpcall-1.14.0-2.rockspec ├── coxpcall-1.15.0-1.rockspec ├── coxpcall-1.16.0-1.rockspec ├── coxpcall-cvs-1.rockspec ├── coxpcall-cvs-2.rockspec └── coxpcall-scm-1.rockspec ├── src └── coxpcall.lua └── tests └── test.lua /Makefile: -------------------------------------------------------------------------------- 1 | # $Id: Makefile,v 1.3 2008/01/22 18:39:18 mascarenhas Exp $ 2 | 3 | config_file:=config 4 | 5 | DESTDIR ?= / 6 | 7 | ifneq '$(wildcard $(config_file))' '' 8 | include $(config_file) 9 | endif 10 | 11 | $(config_file): 12 | chmod +x configure 13 | 14 | install: $(config_file) 15 | mkdir -p $(DESTDIR)$(LUA_DIR) 16 | cp src/coxpcall.lua $(DESTDIR)$(LUA_DIR)/ 17 | 18 | install-doc: install 19 | mkdir -p $(DESTDIR)$(DOC_PREFIX)/doc 20 | cp -r doc/* $(DESTDIR)$(DOC_PREFIX)/doc 21 | echo "Go to $(DOC_PREFIX) for docs!" 22 | 23 | -------------------------------------------------------------------------------- /Makefile.win: -------------------------------------------------------------------------------- 1 | # $Id: Makefile.win,v 1.3 2008/01/23 03:01:31 mascarenhas Exp $ 2 | 3 | LUA_DIR=C:\lua5.1\lua 4 | 5 | install: 6 | IF NOT EXIST "$(LUA_DIR)" mkdir "$(LUA_DIR)" 7 | copy src\coxpcall.lua "$(LUA_DIR)\" 8 | 9 | install-rocks: install 10 | IF NOT EXIST "$(PREFIX)\doc" mkdir "$(PREFIX)\doc" 11 | xcopy /E doc "$(PREFIX)\doc\" 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Coxpcall 2 | 3 | Coxpcall encapsulates the protected calls with a coroutine based loop, 4 | so errors can be handled without the usual pcall/xpcall issues with coroutines 5 | for Lua 5.1. 6 | 7 | http://keplerproject.github.io/coxpcall/ 8 | 9 | Using Coxpcall usually consists in simply loading the module and then replacing Lua 10 | pcall and xpcall by copcall and coxpcall. 11 | 12 | Coxpcall is free software and uses the same license as Lua 5.1. 13 | 14 | Coxpcall can be downloaded from its GitHub page. You can also get Coxpcall using LuaRocks: 15 | luarocks install coxpcall 16 | 17 | Lua 5.2 was extended with the Coxpcall functionality and hence it is no longer required. The 18 | 5.2+ compatibility by coxpcall means that it maintains backward compatibility while using 19 | the built-in Lua implementation. 20 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ $1 == "--help" ]; then 4 | echo "Usage: configure lua51" 5 | echo "where lua51 is the name of your Lua executable" 6 | exit 0 7 | fi 8 | 9 | echo "Trying to find where you installed Lua..." 10 | 11 | if [ $1 != "" ]; then 12 | lua=$1 13 | else 14 | lua="lua51" 15 | fi 16 | 17 | lua_bin=`which $lua` 18 | lua_bin_dir=`dirname $lua_bin` 19 | 20 | lua_root=`dirname $lua_bin_dir` 21 | 22 | if [ $lua_root != "" ]; then 23 | echo "Lua is in $lua_root" 24 | echo "Writing config" 25 | lua_share=$lua_root/share/lua/5.1 26 | lua_lib=$lua_root/lib/lua/5.1 27 | bin_dir=$lua_root/bin 28 | echo "LUA_BIN= $lua_bin" > config 29 | echo "LUA_DIR= $lua_share" >> config 30 | echo "BIN_DIR= $bin_dir" >> config 31 | echo "LUA_LIBDIR= $lua_lib" >> config 32 | echo "Now run 'make && sudo make install'" 33 | else 34 | echo "Lua not found, please install Lua 5.1 (and put in your PATH)" 35 | fi 36 | 37 | -------------------------------------------------------------------------------- /doc/coxpcall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/keplerproject/coxpcall/50ccf2651b50a1ad882722984ea049cedd4ec811/doc/coxpcall.png -------------------------------------------------------------------------------- /doc/doc.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #47555c; 3 | font-size: 16px; 4 | font-family: "Open Sans", sans-serif; 5 | margin: 0; 6 | padding: 0; 7 | background: #eff4ff; 8 | } 9 | 10 | a:link { color: #008fee; } 11 | a:visited { color: #008fee; } 12 | a:hover { color: #22a7ff; } 13 | 14 | h1 { font-size:26px; } 15 | h2 { font-size:24px; } 16 | h3 { font-size:18px; } 17 | h4 { font-size:16px; } 18 | 19 | hr { 20 | height: 1px; 21 | background: #c1cce4; 22 | border: 0px; 23 | margin: 20px 0; 24 | } 25 | 26 | code { 27 | font-family: "Open Sans Mono", "Andale Mono", monospace; 28 | } 29 | 30 | tt { 31 | font-family: "Open Sans Mono", "Andale Mono", monospace; 32 | } 33 | 34 | body, td, th { 35 | } 36 | 37 | textarea, pre, tt { 38 | font-family: "Open Sans Mono", "Andale Mono", monospace; 39 | } 40 | 41 | img { 42 | border-width: 0px; 43 | } 44 | 45 | .example { 46 | background-color: #323744; 47 | color: white; 48 | font-size: 16px; 49 | padding: 16px 24px; 50 | border-radius: 2px; 51 | } 52 | 53 | div.header, div.footer { 54 | } 55 | 56 | #container { 57 | } 58 | 59 | #product { 60 | background-color: white; 61 | padding: 10px; 62 | height: 130px; 63 | border-bottom: solid #d3dbec 1px; 64 | } 65 | 66 | #product big { 67 | font-size: 42px; 68 | } 69 | #product strong { 70 | font-weight: normal; 71 | } 72 | 73 | #product_logo { 74 | float: right; 75 | } 76 | 77 | #product_name { 78 | padding-top: 15px; 79 | padding-left: 30px; 80 | font-size: 42px; 81 | font-weight: normal; 82 | } 83 | 84 | #product_description { 85 | padding-left: 30px; 86 | color: #757779; 87 | } 88 | 89 | #main { 90 | background: #eff4ff; 91 | margin: 0; 92 | } 93 | 94 | #navigation { 95 | width: 100%; 96 | background-color: rgb(44,62,103); 97 | padding: 10px; 98 | margin: 0; 99 | } 100 | 101 | #navigation h1 { 102 | display: none; 103 | } 104 | 105 | #navigation a:hover { 106 | text-decoration: underline; 107 | } 108 | 109 | #navigation ul li a { 110 | color: rgb(136, 208, 255); 111 | font-weight: bold; 112 | text-decoration: none; 113 | } 114 | 115 | #navigation ul li li a { 116 | color: rgb(136, 208, 255); 117 | font-weight: normal; 118 | text-decoration: none; 119 | } 120 | 121 | #navigation ul { 122 | display: inline; 123 | color: white; 124 | padding: 0px; 125 | padding-top: 10px; 126 | padding-bottom: 10px; 127 | } 128 | 129 | #navigation li { 130 | display: inline; 131 | list-style-type: none; 132 | padding-left: 5px; 133 | padding-right: 5px; 134 | } 135 | 136 | #navigation li { 137 | padding: 10px; 138 | padding: 10px; 139 | } 140 | 141 | #navigation li li { 142 | } 143 | 144 | #navigation li:hover a { 145 | color: rgb(166, 238, 255); 146 | } 147 | 148 | #content { 149 | padding: 20px; 150 | width: 800px; 151 | margin-left: auto; 152 | margin-right: auto; 153 | } 154 | 155 | #about { 156 | display: none; 157 | } 158 | 159 | dl.reference { 160 | background-color: white; 161 | padding-left: 20px; 162 | padding-right: 20px; 163 | padding-bottom: 20px; 164 | border: solid #d3dbec 1px; 165 | } 166 | 167 | dl.reference dt { 168 | padding: 5px; 169 | padding-top: 25px; 170 | color: #637bbc; 171 | } 172 | 173 | dl.reference dl dt { 174 | padding-top: 5px; 175 | color: #637383; 176 | } 177 | 178 | dl.reference dd { 179 | } 180 | 181 | @media print { 182 | body { 183 | font: 10pt "Times New Roman", "TimeNR", Times, serif; 184 | } 185 | a { 186 | font-weight:bold; color: #004080; text-decoration: underline; 187 | } 188 | #main { 189 | background-color: #ffffff; border-left: 0px; 190 | } 191 | #container { 192 | margin-left: 2%; margin-right: 2%; background-color: #ffffff; 193 | } 194 | #content { 195 | margin-left: 0px; padding: 1em; border-left: 0px; border-right: 0px; background-color: #ffffff; 196 | } 197 | #navigation { 198 | display: none; 199 | } 200 | #product_logo { 201 | display: none; 202 | } 203 | #about img { 204 | display: none; 205 | } 206 | .example { 207 | font-family: "Andale Mono", monospace; 208 | font-size: 8pt; 209 | page-break-inside: avoid; 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /doc/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | Coxpcall - Coroutine Safe xpcall and pcall 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 |
16 | 19 |
Coxpcall
20 |
Coroutine-safe xpcall and pcall versions
21 |
22 | 23 |
24 | 25 | 46 | 47 |
48 | 49 |

Overview

50 | 51 |

Coxpcall encapsulates the protected calls with a coroutine based loop, 52 | so errors can be dealed without the usual pcall/xpcall 53 | issues with coroutines. 54 |

55 | 56 |

Using Coxpcall usually consists in simply loading the module and then 57 | replacing Lua pcall, xpcall and coroutine.running 58 | by copcall, coxpcall, and running.

59 | 60 |

Coxpcall is free software and uses the same 61 | license as Lua 5.1 and 5.2. 62 |

63 | 64 |

Status

65 | 66 |

It supports Lua 5.1, 5.2 and 5.3. Lua 5.2 was extended with the Coxpcall 67 | functionality and hence coxpcall is no longer required. The 68 | 5.2+ compatibility by coxpcall means that it maintains backward compatibility 69 | while using the built-in implementation.

70 | 71 |

Download

72 | 73 |

74 | You can get Coxpcall using LuaRocks: 75 |

76 | 77 |
 78 | luarocks install coxpcall
 79 | 
80 | 81 |

82 | See also its 83 | GitHub page. 84 |

85 | 86 |

Reference

87 | 88 |

Coxpcall module offers three functions that reproduce the behaviour of 89 | pcall, xpcall, and coroutine.running:

90 | 91 |
 92 | local coxpcall = require "coxpcall"
 93 | 
94 | 95 |
96 |
coxpcall.xpcall(f, err)
97 |
Offers the same functionality as Lua xpcall(f, err), 98 | but calls the error handler after the error unwinds the stack, 99 | so that it's impossible to gather more information about the error. 100 | To compensate, if the error is a string, stack trace is appended to it. 101 | Also set as global coxpcall.
102 | 103 |
coxpcall.pcall(f, ...)
104 |
Offers the same functionality as Lua pcall(f, ...). 105 | Also set as global copcall.
106 | 107 |
coxpcall.running([coro])
108 |
Because coxpcall and copcall run the function to protect 109 | inside a new coroutine, coroutine.running() will return an unexpected 110 | coroutine when used inside the protected function. If the coroutine coro was 111 | created by the coxpcall module, then running(coro) will return the original 112 | coroutine that created it. If coro is not provided, it will default to the 113 | currently running coroutine.
114 |
115 | 116 |

Credits

117 | 118 |

119 | Coxpcall was designed and implemented by Roberto Ierusalimschy and 120 | André Carregal with the colaboration of Thomas Harning Jr., Ignacio Burgueño, 121 | Gary NG and Fábio Mascarenhas as part of the 122 | Kepler Project which holds its copyright. 123 |

124 | 125 |
126 | 127 |
128 | 129 |
130 |

Valid XHTML 1.0!

131 |
132 | 133 |
134 | 135 | 136 | 137 | -------------------------------------------------------------------------------- /doc/license.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | Coxpcall License 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 | 18 |
Coxpcall
19 |
Coroutine-safe xpcall and pcall versions
20 |
21 | 22 |
23 | 24 | 47 | 48 | 49 |
50 | 51 |

Coxpcall License

52 | 53 |

Coxpcall is free software: it can be used for both academic and 54 | commercial purposes at absolutely no cost. There are no royalties 55 | or GNU-like "copyleft" restrictions. Coxpcall qualifies as Open Source 57 | software. Its licenses are compatible with GPL. Coxpcall is not in 59 | the public domain and its authors 60 | keep its copyright. The legal details are below. 61 |

62 | 63 |

The spirit of the license is that you are free to use Coxpcall for 64 | any purpose at no cost without having to ask us. The only 65 | requirement is that if you do use Coxpcall, then you should give us 66 | credit by including the appropriate copyright notice somewhere in 67 | your product or its documentation.

68 | 69 |

Coxpcall was designed and implemented by Roberto Ierusalimschy and 70 | André Carregal. The implementation is not derived from licensed software.

71 | 72 |

73 | 74 |

75 | 76 |
77 |

Copyright © 2005 Kepler Project.

78 | 79 |

Permission is hereby granted, free of charge, to any person 80 | obtaining a copy of this software and associated documentation 81 | files (the "Software"), to deal in the Software without 82 | restriction, including without limitation the rights to use, copy, 83 | modify, merge, publish, distribute, sublicense, and/or sell copies 84 | of the Software, and to permit persons to whom the Software is 85 | furnished to do so, subject to the following conditions:

86 | 87 |

The above copyright notice and this permission notice shall be 88 | included in all copies or substantial portions of the Software.

89 | 90 |

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 91 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 92 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 93 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 94 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 95 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 96 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 97 | SOFTWARE.

98 | 99 |
100 | 101 |
102 | 103 |
104 |

Valid XHTML 1.0!

105 |

$Id: license.html,v 1.1 2008/01/17 20:41:05 carregal Exp $

106 |
107 | 108 |
109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /rockspec/coxpcall-1.11.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "1.11.0-1" 4 | 5 | description = { 6 | summary = "Coroutine safe xpcall and pcall", 7 | detailed = [[ 8 | Encapsulates the protected calls with a coroutine based loop, so errors can 9 | be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 10 | yielding inside the call to pcall or xpcall. 11 | ]], 12 | license = "MIT/X11", 13 | homepage = "http://coxpcall.luaforge.net" 14 | } 15 | 16 | dependencies = { } 17 | 18 | source = { 19 | url = "http://luaforge.net/frs/download.php/3047/coxpcall-1.11.0.tar.gz", 20 | } 21 | 22 | build = { 23 | type = "make", 24 | build_pass = false, 25 | install_target = "install-rocks", 26 | install_variables = { 27 | PREFIX = "$(PREFIX)", 28 | LUA_DIR = "$(LUADIR)", 29 | BIN_DIR = "$(BINDIR)" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rockspec/coxpcall-1.12.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "1.12.0-1" 4 | 5 | source = { 6 | url = "http://luaforge.net/frs/download.php/3351/coxpcall-1.12.0.tar.gz", 7 | } 8 | 9 | description = { 10 | summary = "Coroutine safe xpcall and pcall", 11 | detailed = [[ 12 | Encapsulates the protected calls with a coroutine based loop, so errors can 13 | be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 14 | yielding inside the call to pcall or xpcall. 15 | ]], 16 | license = "MIT/X11", 17 | homepage = "http://coxpcall.luaforge.net" 18 | } 19 | 20 | dependencies = { } 21 | 22 | build = { 23 | type = "make", 24 | build_pass = false, 25 | install_target = "install-rocks", 26 | install_variables = { 27 | PREFIX = "$(PREFIX)", 28 | LUA_DIR = "$(LUADIR)", 29 | BIN_DIR = "$(BINDIR)" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rockspec/coxpcall-1.12.0rc1-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "1.12.0rc1-1" 4 | 5 | description = { 6 | summary = "Coroutine safe xpcall and pcall", 7 | detailed = [[ 8 | Encapsulates the protected calls with a coroutine based loop, so errors can 9 | be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 10 | yielding inside the call to pcall or xpcall. 11 | ]], 12 | license = "MIT/X11", 13 | homepage = "http://coxpcall.luaforge.net" 14 | } 15 | 16 | dependencies = { } 17 | 18 | source = { 19 | url = "http://coxpcall.luaforge.net/coxpcall-1.12.0rc1.tar.gz", 20 | } 21 | 22 | build = { 23 | type = "make", 24 | build_pass = false, 25 | install_target = "install-rocks", 26 | install_variables = { 27 | PREFIX = "$(PREFIX)", 28 | LUA_DIR = "$(LUADIR)", 29 | BIN_DIR = "$(BINDIR)" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rockspec/coxpcall-1.13.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "1.13.0-1" 4 | 5 | source = { 6 | url = "http://luaforge.net/frs/download.php/3406/coxpcall-1.13.0.tar.gz", 7 | } 8 | 9 | description = { 10 | summary = "Coroutine safe xpcall and pcall", 11 | detailed = [[ 12 | Encapsulates the protected calls with a coroutine based loop, so errors can 13 | be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 14 | yielding inside the call to pcall or xpcall. 15 | ]], 16 | license = "MIT/X11", 17 | homepage = "http://coxpcall.luaforge.net" 18 | } 19 | 20 | dependencies = { } 21 | 22 | build = { 23 | type = "make", 24 | build_pass = false, 25 | install_target = "install-rocks", 26 | install_variables = { 27 | PREFIX = "$(PREFIX)", 28 | LUA_DIR = "$(LUADIR)", 29 | BIN_DIR = "$(BINDIR)" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /rockspec/coxpcall-1.13.0-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "1.13.0-2" 4 | 5 | source = { 6 | url = "http://luaforge.net/frs/download.php/3406/coxpcall-1.13.0.tar.gz", 7 | } 8 | 9 | description = { 10 | summary = "Coroutine safe xpcall and pcall", 11 | detailed = [[ 12 | Encapsulates the protected calls with a coroutine based loop, so errors can 13 | be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 14 | yielding inside the call to pcall or xpcall. 15 | ]], 16 | license = "MIT/X11", 17 | homepage = "http://coxpcall.luaforge.net" 18 | } 19 | 20 | dependencies = { } 21 | 22 | build = { 23 | type = "module", 24 | modules = { coxpcall = "src/coxpcall.lua" } 25 | } 26 | -------------------------------------------------------------------------------- /rockspec/coxpcall-1.14.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "1.14.0-1" 4 | 5 | source = { 6 | url = "http://www.keplerproject.org/files/coxpcall-1.14.0.tar.gz", 7 | } 8 | 9 | description = { 10 | summary = "Coroutine safe xpcall and pcall", 11 | detailed = [[ 12 | Encapsulates the protected calls with a coroutine based loop, so errors can 13 | be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 14 | yielding inside the call to pcall or xpcall. 15 | ]], 16 | license = "MIT/X11", 17 | homepage = "http://coxpcall.luaforge.net" 18 | } 19 | 20 | dependencies = { } 21 | 22 | build = { 23 | type = "builtin", 24 | modules = { coxpcall = "src/coxpcall.lua" } 25 | } 26 | -------------------------------------------------------------------------------- /rockspec/coxpcall-1.14.0-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "1.14.0-1" 4 | 5 | source = { 6 | url = "http://www.keplerproject.org/files/coxpcall-1.14.0.tar.gz", 7 | } 8 | 9 | description = { 10 | summary = "Coroutine safe xpcall and pcall", 11 | detailed = [[ 12 | Encapsulates the protected calls with a coroutine based loop, so errors can 13 | be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 14 | yielding inside the call to pcall or xpcall. 15 | ]], 16 | license = "MIT/X11", 17 | homepage = "http://keplerproject.github.com/coxpcall" 18 | } 19 | 20 | dependencies = { } 21 | 22 | build = { 23 | type = "builtin", 24 | modules = { coxpcall = "src/coxpcall.lua" } 25 | } 26 | -------------------------------------------------------------------------------- /rockspec/coxpcall-1.15.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "1.15.0-1" 4 | 5 | source = { 6 | url = "git://github.com/keplerproject/coxpcall", 7 | tag = "v1_15_0", 8 | } 9 | 10 | description = { 11 | summary = "Coroutine safe xpcall and pcall", 12 | detailed = [[ 13 | Encapsulates the protected calls with a coroutine based loop, so errors can 14 | be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 15 | yielding inside the call to pcall or xpcall. 16 | ]], 17 | license = "MIT/X11", 18 | homepage = "http://keplerproject.github.com/coxpcall" 19 | } 20 | 21 | dependencies = { } 22 | 23 | build = { 24 | type = "builtin", 25 | modules = { coxpcall = "src/coxpcall.lua" } 26 | } 27 | -------------------------------------------------------------------------------- /rockspec/coxpcall-1.16.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "1.16.0-1" 4 | 5 | source = { 6 | url = "git://github.com/keplerproject/coxpcall", 7 | tag = "v1_16_0", 8 | } 9 | 10 | description = { 11 | summary = "Coroutine safe xpcall and pcall", 12 | detailed = [[ 13 | Encapsulates the protected calls with a coroutine based loop, so errors can 14 | be handled without the usual Lua 5.x pcall/xpcall issues with coroutines 15 | yielding inside the call to pcall or xpcall. 16 | ]], 17 | license = "MIT/X11", 18 | homepage = "http://keplerproject.github.io/coxpcall" 19 | } 20 | 21 | dependencies = { } 22 | 23 | build = { 24 | type = "builtin", 25 | modules = { coxpcall = "src/coxpcall.lua" } 26 | } 27 | -------------------------------------------------------------------------------- /rockspec/coxpcall-cvs-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "cvs-1" 4 | 5 | description = { 6 | summary = "Coroutine safe xpcall and pcall", 7 | detailed = [[ 8 | Encapsulates the protected calls with a coroutine based loop, so errors can 9 | be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 10 | yielding inside the call to pcall or xpcall. 11 | ]], 12 | license = "MIT/X11", 13 | homepage = "http://coxpcall.luaforge.net" 14 | } 15 | 16 | dependencies = { } 17 | 18 | source = { 19 | url = "cvs://:pserver:anonymous:@cvs.luaforge.net:/cvsroot/coxpcall", 20 | cvs_tag = "HEAD" 21 | } 22 | 23 | build = { 24 | type = "make", 25 | build_pass = false, 26 | install_target = "install-rocks", 27 | install_variables = { 28 | PREFIX = "$(PREFIX)", 29 | LUA_DIR = "$(LUADIR)", 30 | BIN_DIR = "$(BINDIR)" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rockspec/coxpcall-cvs-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "cvs-2" 4 | 5 | description = { 6 | summary = "Coroutine safe xpcall and pcall", 7 | detailed = [[ 8 | Encapsulates the protected calls with a coroutine based loop, so errors can 9 | be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 10 | yielding inside the call to pcall or xpcall. 11 | ]], 12 | license = "MIT/X11", 13 | homepage = "http://coxpcall.luaforge.net" 14 | } 15 | 16 | dependencies = { } 17 | 18 | source = { 19 | url = "git://github.com/keplerproject/coxpcall.git" 20 | } 21 | 22 | build = { 23 | type = "module", 24 | modules = { coxpcall = "src/coxpcall.lua" } 25 | } 26 | -------------------------------------------------------------------------------- /rockspec/coxpcall-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "Coxpcall" 2 | 3 | version = "scm-1" 4 | 5 | description = { 6 | summary = "Coroutine safe xpcall and pcall", 7 | detailed = [[ 8 | Encapsulates the protected calls with a coroutine based loop, so errors can 9 | be handled without the usual Lua 5.x pcall/xpcall issues with coroutines 10 | yielding inside the call to pcall or xpcall. 11 | ]], 12 | license = "MIT/X11", 13 | homepage = "http://keplerproject.github.io/coxpcall" 14 | } 15 | 16 | dependencies = { } 17 | 18 | source = { 19 | url = "git://github.com/keplerproject/coxpcall.git" 20 | } 21 | 22 | build = { 23 | type = "builtin", 24 | modules = { coxpcall = "src/coxpcall.lua" } 25 | } 26 | -------------------------------------------------------------------------------- /src/coxpcall.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | -- Coroutine safe xpcall and pcall versions 3 | -- 4 | -- Encapsulates the protected calls with a coroutine based loop, so errors can 5 | -- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines 6 | -- yielding inside the call to pcall or xpcall. 7 | -- 8 | -- Authors: Roberto Ierusalimschy and Andre Carregal 9 | -- Contributors: Thomas Harning Jr., Ignacio Burgueño, Fabio Mascarenhas 10 | -- 11 | -- Copyright 2005 - Kepler Project 12 | -- 13 | -- $Id: coxpcall.lua,v 1.13 2008/05/19 19:20:02 mascarenhas Exp $ 14 | ------------------------------------------------------------------------------- 15 | 16 | ------------------------------------------------------------------------------- 17 | -- Checks if (x)pcall function is coroutine safe 18 | ------------------------------------------------------------------------------- 19 | local function isCoroutineSafe(func) 20 | local co = coroutine.create(function() 21 | return func(coroutine.yield, function() end) 22 | end) 23 | 24 | coroutine.resume(co) 25 | return coroutine.resume(co) 26 | end 27 | 28 | -- No need to do anything if pcall and xpcall are already safe. 29 | if isCoroutineSafe(pcall) and isCoroutineSafe(xpcall) then 30 | copcall = pcall 31 | coxpcall = xpcall 32 | return { pcall = pcall, xpcall = xpcall, running = coroutine.running } 33 | end 34 | 35 | ------------------------------------------------------------------------------- 36 | -- Implements xpcall with coroutines 37 | ------------------------------------------------------------------------------- 38 | local performResume, handleReturnValue 39 | local oldpcall, oldxpcall = pcall, xpcall 40 | local pack = table.pack or function(...) return {n = select("#", ...), ...} end 41 | local unpack = table.unpack or unpack 42 | local running = coroutine.running 43 | local coromap = setmetatable({}, { __mode = "k" }) 44 | 45 | function handleReturnValue(err, co, status, ...) 46 | if not status then 47 | return false, err(debug.traceback(co, (...)), ...) 48 | end 49 | if coroutine.status(co) == 'suspended' then 50 | return performResume(err, co, coroutine.yield(...)) 51 | else 52 | return true, ... 53 | end 54 | end 55 | 56 | function performResume(err, co, ...) 57 | return handleReturnValue(err, co, coroutine.resume(co, ...)) 58 | end 59 | 60 | local function id(trace, ...) 61 | return trace 62 | end 63 | 64 | function coxpcall(f, err, ...) 65 | local current = running() 66 | if not current then 67 | if err == id then 68 | return oldpcall(f, ...) 69 | else 70 | if select("#", ...) > 0 then 71 | local oldf, params = f, pack(...) 72 | f = function() return oldf(unpack(params, 1, params.n)) end 73 | end 74 | return oldxpcall(f, err) 75 | end 76 | else 77 | local res, co = oldpcall(coroutine.create, f) 78 | if not res then 79 | local newf = function(...) return f(...) end 80 | co = coroutine.create(newf) 81 | end 82 | coromap[co] = current 83 | return performResume(err, co, ...) 84 | end 85 | end 86 | 87 | local function corunning(coro) 88 | if coro ~= nil then 89 | assert(type(coro)=="thread", "Bad argument; expected thread, got: "..type(coro)) 90 | else 91 | coro = running() 92 | end 93 | while coromap[coro] do 94 | coro = coromap[coro] 95 | end 96 | if coro == "mainthread" then return nil end 97 | return coro 98 | end 99 | 100 | ------------------------------------------------------------------------------- 101 | -- Implements pcall with coroutines 102 | ------------------------------------------------------------------------------- 103 | 104 | function copcall(f, ...) 105 | return coxpcall(f, id, ...) 106 | end 107 | 108 | return { pcall = copcall, xpcall = coxpcall, running = corunning } 109 | -------------------------------------------------------------------------------- /tests/test.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | 3 | package.path = "../src/?.lua;./src/?.lua" 4 | local M = require("coxpcall") 5 | 6 | local count, errortable = 0, {} 7 | 8 | -- test helper functions 9 | local function assert(ok, ...) 10 | if not ok then 11 | local msg = ... 12 | error(msg == nil and "assertion failed" or msg, 0) 13 | else 14 | return ok, ... 15 | end 16 | end 17 | 18 | local function error_is(f, xmsg) 19 | local ok, msg = pcall(f) 20 | assert(ok == false, "function did not raise an error") 21 | assert(msg == xmsg, 22 | "error message not as expected:\n\t"..tostring(msg)) 23 | count = count + 1 24 | end 25 | 26 | local function error_matches(f, pat) 27 | local ok, msg = pcall(f) 28 | assert(ok == false, "function did not raise an error") 29 | assert(type(msg) == "string", 30 | "error message is not a string:\n\t"..type(msg)) 31 | assert(msg:match(pat), 32 | "error message didn't match pattern:\n\t"..pat.."\n\t"..msg) 33 | count = count + 1 34 | end 35 | 36 | local function succeeds(f) 37 | local ok, msg = pcall(f) 38 | assert(ok, "function raised an error\n\t:"..tostring(msg)) 39 | count = count + 1 40 | end 41 | 42 | local function succeeds_with(f, xrets) 43 | local function packrets(ok, ...) 44 | return ok, { n=select("#", ...), ... } 45 | end 46 | local ok, rets = packrets(pcall(f)) 47 | assert(ok, "function raised an error\n\t:"..tostring(rets[1])) 48 | assert(rets.n == xrets.n or #xrets, 49 | "unexpected number of return values: "..rets.n.. 50 | " (expected: "..(xrets.n or #xrets)..")") 51 | for i = 1, rets.n do 52 | assert(rets[i] == xrets[i], 53 | "unexpected return value no. "..i.." (".. 54 | tostring(rets[i])..", expected: "..tostring(xrets[i])..")") 55 | end 56 | count = count + 1 57 | end 58 | 59 | local function traceback() 60 | return "XXX" 61 | end 62 | 63 | 64 | 65 | -- the tests: 66 | 67 | -- co(x)pcall from main thread 68 | succeeds_with(function() 69 | return M.pcall(function(...) 70 | return ... 71 | end, 1, 2, 3) 72 | end, { true, 1, 2, 3 }) 73 | 74 | succeeds_with(function() 75 | return M.xpcall(function() 76 | return 1, 2, 3 77 | end, debug.traceback) 78 | end, { true, 1, 2, 3 }) 79 | 80 | error_matches(function() 81 | return assert(M.pcall(function(...) 82 | error("ARGH", 0) 83 | end, 1, 2, 3)) 84 | end, "ARGH") 85 | 86 | error_is(function() 87 | return assert(M.pcall(function(...) 88 | error(errortable, 0) 89 | end, 1, 2, 3)) 90 | end, errortable) 91 | 92 | error_matches(function() 93 | return assert(M.xpcall(function() 94 | error("ARGH", 0) 95 | end, debug.traceback)) 96 | end, "ARGH") 97 | 98 | error_is(function() 99 | return assert(M.xpcall(function() 100 | error(errortable, 0) 101 | end, debug.traceback)) 102 | end, errortable) 103 | 104 | error_matches(function() 105 | return assert(M.xpcall(function() 106 | error("ARGH", 0) 107 | end, traceback)) 108 | end, "XXX") 109 | 110 | error_matches(function() 111 | return assert(M.pcall(rawset)) 112 | end, "bad argument") 113 | 114 | 115 | -- co(x)pcall from within coroutine (without yielding) 116 | succeeds_with(coroutine.wrap(function() 117 | return M.pcall(function(...) 118 | return ... 119 | end, 1, 2, 3) 120 | end), { true, 1, 2, 3 }) 121 | 122 | succeeds_with(coroutine.wrap(function() 123 | return M.xpcall(function() 124 | return 1, 2, 3 125 | end, debug.traceback) 126 | end), { true, 1, 2, 3 }) 127 | 128 | error_matches(coroutine.wrap(function() 129 | return assert(M.pcall(function(...) 130 | error("ARGH", 0) 131 | end, 1, 2, 3)) 132 | end), "ARGH") 133 | 134 | error_is(coroutine.wrap(function() 135 | return assert(M.pcall(function(...) 136 | error(errortable, 0) 137 | end, 1, 2, 3)) 138 | end), errortable) 139 | 140 | error_matches(coroutine.wrap(function() 141 | return assert(M.xpcall(function() 142 | error("ARGH", 0) 143 | end, debug.traceback)) 144 | end), "ARGH") 145 | 146 | error_is(coroutine.wrap(function() 147 | return assert(M.xpcall(function() 148 | error(errortable, 0) 149 | end, debug.traceback)) 150 | end), errortable) 151 | 152 | error_matches(coroutine.wrap(function() 153 | return assert(M.xpcall(function() 154 | error("ARGH", 0) 155 | end, traceback)) 156 | end), "XXX") 157 | 158 | error_matches(coroutine.wrap(function() 159 | return assert(M.pcall(rawset)) 160 | end), "bad argument") 161 | 162 | 163 | -- co(x)pcall from within coroutine (with yielding) 164 | succeeds_with(function() 165 | local f = coroutine.wrap(function(...) 166 | return M.pcall(function(...) 167 | coroutine.yield() 168 | return ... 169 | end, ...) 170 | end) 171 | f(1, 2, 3) 172 | return f() 173 | end, { true, 1, 2, 3 }) 174 | 175 | succeeds_with(function() 176 | local f = coroutine.wrap(function() 177 | return M.xpcall(function() 178 | coroutine.yield() 179 | return 1, 2, 3 180 | end, debug.traceback) 181 | end) 182 | f() 183 | return f() 184 | end, { true, 1, 2, 3 }) 185 | 186 | error_matches(function() 187 | local f = coroutine.wrap(function(...) 188 | return assert(M.pcall(function(...) 189 | coroutine.yield() 190 | error("ARGH", 0) 191 | end, ...)) 192 | end) 193 | f( 1, 2, 3) 194 | return f() 195 | end, "ARGH") 196 | 197 | error_is(function() 198 | local f = coroutine.wrap(function(...) 199 | return assert(M.pcall(function(...) 200 | coroutine.yield() 201 | error(errortable, 0) 202 | end, ...)) 203 | end) 204 | f( 1, 2, 3) 205 | return f() 206 | end, errortable) 207 | 208 | error_matches(function() 209 | local f = coroutine.wrap(function() 210 | return assert(M.xpcall(function() 211 | coroutine.yield() 212 | error("ARGH", 0) 213 | end, debug.traceback)) 214 | end) 215 | f() 216 | return f() 217 | end, "ARGH") 218 | 219 | error_is(function() 220 | local f = coroutine.wrap(function() 221 | return assert(M.xpcall(function() 222 | coroutine.yield() 223 | error(errortable, 0) 224 | end, debug.traceback)) 225 | end) 226 | f() 227 | return f() 228 | end, errortable) 229 | 230 | error_matches(function() 231 | local f = coroutine.wrap(function() 232 | return assert(M.xpcall(function() 233 | coroutine.yield() 234 | error("ARGH", 0) 235 | end, traceback)) 236 | end) 237 | f() 238 | return f() 239 | end, "XXX") 240 | 241 | 242 | -- running 243 | succeeds(function() 244 | local co = coroutine.create(function() 245 | local _,c2 = M.pcall(M.running) 246 | local _,c3 = M.xpcall(M.running, debug.traceback) 247 | return c2, c3 248 | end) 249 | local _, r1, r2 = assert(coroutine.resume(co)) 250 | assert(r1 == co, "running returned wrong thread") 251 | assert(r2 == co, "running returned wrong thread") 252 | end) 253 | 254 | 255 | print("OK", count) 256 | 257 | --------------------------------------------------------------------------------