Optimize util.threadlocal, Add luci.store as global threadlocal store
[project/luci.git] / libs / core / luasrc / util.lua
1 --[[
2 LuCI - Utility library
3
4 Description:
5 Several common useful Lua functions
6
7 FileId:
8 $Id$
9
10 License:
11 Copyright 2008 Steven Barth <steven@midlink.org>
12
13 Licensed under the Apache License, Version 2.0 (the "License");
14 you may not use this file except in compliance with the License.
15 You may obtain a copy of the License at
16
17         http://www.apache.org/licenses/LICENSE-2.0
18
19 Unless required by applicable law or agreed to in writing, software
20 distributed under the License is distributed on an "AS IS" BASIS,
21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 See the License for the specific language governing permissions and
23 limitations under the License.
24
25 ]]--
26
27 local io = require "io"
28 local math = require "math"
29 local table = require "table"
30 local debug = require "debug"
31 local ldebug = require "luci.debug"
32 local string = require "string"
33 local coroutine = require "coroutine"
34
35 local getmetatable, setmetatable = getmetatable, setmetatable
36 local rawget, rawset, unpack = rawget, rawset, unpack
37 local tostring, type, assert = tostring, type, assert
38 local ipairs, pairs, loadstring = ipairs, pairs, loadstring
39 local require, pcall, xpcall = require, pcall, xpcall
40 local collectgarbage, get_memory_limit = collectgarbage, get_memory_limit
41
42 --- LuCI utility functions.
43 module "luci.util"
44
45 --
46 -- Pythonic string formatting extension
47 --
48 getmetatable("").__mod = function(a, b)
49         if not b then
50                 return a
51         elseif type(b) == "table" then
52                 return a:format(unpack(b))
53         else
54                 return a:format(b)
55         end
56 end
57
58
59 --
60 -- Class helper routines
61 --
62
63 -- Instantiates a class
64 local function _instantiate(class, ...)
65         local inst = setmetatable({}, {__index = class})
66
67         if inst.__init__ then
68                 inst:__init__(...)
69         end
70
71         return inst
72 end
73
74 --- Create a Class object (Python-style object model).
75 -- The class object can be instantiated by calling itself.
76 -- Any class functions or shared parameters can be attached to this object.
77 -- Attaching a table to the class object makes this table shared between
78 -- all instances of this class. For object parameters use the __init__ function.
79 -- Classes can inherit member functions and values from a base class.
80 -- Class can be instantiated by calling them. All parameters will be passed
81 -- to the __init__ function of this class - if such a function exists.
82 -- The __init__ function must be used to set any object parameters that are not shared
83 -- with other objects of this class. Any return values will be ignored.
84 -- @param base  The base class to inherit from (optional)
85 -- @return              A class object
86 -- @see                 instanceof
87 -- @see                 clone
88 function class(base)
89         return setmetatable({}, {
90                 __call  = _instantiate,
91                 __index = base
92         })
93 end
94
95 --- Test whether the given object is an instance of the given class.
96 -- @param object        Object instance
97 -- @param class         Class object to test against
98 -- @return                      Boolean indicating whether the object is an instance
99 -- @see                         class
100 -- @see                         clone
101 function instanceof(object, class)
102         local meta = getmetatable(object)
103         while meta and meta.__index do
104                 if meta.__index == class then
105                         return true
106                 end
107                 meta = getmetatable(meta.__index)
108         end
109         return false
110 end
111
112
113 --
114 -- Scope manipulation routines
115 --
116
117 local tl_meta = {
118         __mode = "k",
119
120         __index = function(self, key)
121                 local t = rawget(self, coxpt[coroutine.running()]
122                  or coroutine.running() or 0)
123                 return t and t[key]
124         end,
125
126         __newindex = function(self, key, value)
127                 local c = coxpt[coroutine.running()] or coroutine.running() or 0
128                 if not rawget(self, c) then
129                         rawset(self, c, { [key] = value })
130                 else
131                         rawget(self, c)[key] = value
132                 end
133         end
134 }
135
136 --- Create a new or get an already existing thread local store associated with
137 -- the current active coroutine. A thread local store is private a table object
138 -- whose values can't be accessed from outside of the running coroutine.
139 -- @return      Table value representing the corresponding thread local store
140 function threadlocal(tbl)
141         return setmetatable(tbl or {}, tl_meta)
142 end
143
144
145 --
146 -- Debugging routines
147 --
148
149 --- Write given object to stderr.
150 -- @param obj   Value to write to stderr
151 -- @return              Boolean indicating whether the write operation was successful
152 function perror(obj)
153         return io.stderr:write(tostring(obj) .. "\n")
154 end
155
156 --- Recursively dumps a table to stdout, useful for testing and debugging.
157 -- @param t     Table value to dump
158 -- @param maxdepth      Maximum depth
159 -- @return      Always nil
160 function dumptable(t, maxdepth, i, seen)
161         i = i or 0
162         seen = seen or setmetatable({}, {__mode="k"})
163
164         for k,v in pairs(t) do
165                 perror(string.rep("\t", i) .. tostring(k) .. "\t" .. tostring(v))
166                 if type(v) == "table" and (not maxdepth or i < maxdepth) then
167                         if not seen[v] then
168                                 seen[v] = true
169                                 dumptable(v, maxdepth, i+1, seen)
170                         else
171                                 perror(string.rep("\t", i) .. "*** RECURSION ***")
172                         end
173                 end
174         end
175 end
176
177
178 --
179 -- String and data manipulation routines
180 --
181
182 --- Escapes all occurrences of the given character in given string.
183 -- @param s     String value containing unescaped characters
184 -- @param c     String value with character to escape (optional, defaults to "\")
185 -- @return      String value with each occurrence of character escaped with "\"
186 function escape(s, c)
187         c = c or "\\"
188         return s:gsub(c, "\\" .. c)
189 end
190
191 --- Create valid XML PCDATA from given string.
192 -- @param value String value containing the data to escape
193 -- @return              String value containing the escaped data
194 local function _pcdata_repl(c)
195         local i = string.byte(c)
196
197         if ( i >= 0x00 and i <= 0x08 ) or ( i >= 0x0B and i <= 0x0C ) or
198            ( i >= 0x0E and i <= 0x1F ) or ( i == 0x7F )
199         then
200                 return ""
201                 
202         elseif ( i == 0x26 ) or ( i == 0x27 ) or ( i == 0x22 ) or
203                ( i == 0x3C ) or ( i == 0x3E )
204         then
205                 return string.format("&#%i;", i)
206         end
207
208         return c
209 end
210
211 function pcdata(value)
212         return value and tostring(value):gsub("[&\"'<>%c]", _pcdata_repl)
213 end
214
215 --- Strip HTML tags from given string.
216 -- @param value String containing the HTML text
217 -- @return      String with HTML tags stripped of
218 function striptags(s)
219         return pcdata(tostring(s):gsub("</?[A-Za-z][A-Za-z0-9:_%-]*[^>]*>", " "):gsub("%s+", " "))
220 end
221
222 --- Splits given string on a defined separator sequence and return a table
223 -- containing the resulting substrings. The optional max parameter specifies
224 -- the number of bytes to process, regardless of the actual length of the given
225 -- string. The optional last parameter, regex, specifies whether the separator
226 -- sequence is interpreted as regular expression.
227 -- @param str           String value containing the data to split up
228 -- @param pat           String with separator pattern (optional, defaults to "\n")
229 -- @param max           Maximum times to split (optional)
230 -- @param regex         Boolean indicating whether to interpret the separator
231 --                                      pattern as regular expression (optional, default is false)
232 -- @return                      Table containing the resulting substrings
233 function split(str, pat, max, regex)
234         pat = pat or "\n"
235         max = max or #str
236
237         local t = {}
238         local c = 1
239
240         if #str == 0 then
241                 return {""}
242         end
243
244         if #pat == 0 then
245                 return nil
246         end
247
248         if max == 0 then
249                 return str
250         end
251
252         repeat
253                 local s, e = str:find(pat, c, not regex)
254                 max = max - 1
255                 if s and max < 0 then
256                         t[#t+1] = str:sub(c)
257                 else
258                         t[#t+1] = str:sub(c, s and s - 1)
259                 end
260                 c = e and e + 1 or #str + 1
261         until not s or max < 0
262
263         return t
264 end
265
266 --- Remove leading and trailing whitespace from given string value.
267 -- @param str   String value containing whitespace padded data
268 -- @return              String value with leading and trailing space removed
269 function trim(str)
270         return (str:gsub("^%s*(.-)%s*$", "%1"))
271 end
272
273 --- Count the occurences of given substring in given string.
274 -- @param str           String to search in
275 -- @param pattern       String containing pattern to find
276 -- @return                      Number of found occurences
277 function cmatch(str, pat)
278         local count = 0
279         for _ in str:gmatch(pat) do count = count + 1 end
280         return count
281 end
282
283 --- Parse certain units from the given string and return the canonical integer
284 -- value or 0 if the unit is unknown. Upper- or lower case is irrelevant.
285 -- Recognized units are:
286 --      o "y"   - one year   (60*60*24*366)
287 --  o "m"       - one month  (60*60*24*31)
288 --  o "w"       - one week   (60*60*24*7)
289 --  o "d"       - one day    (60*60*24)
290 --  o "h"       - one hour       (60*60)
291 --  o "min"     - one minute (60)
292 --  o "kb"  - one kilobyte (1024)
293 --  o "mb"      - one megabyte (1024*1024)
294 --  o "gb"      - one gigabyte (1024*1024*1024)
295 --  o "kib" - one si kilobyte (1000)
296 --  o "mib"     - one si megabyte (1000*1000)
297 --  o "gib"     - one si gigabyte (1000*1000*1000)
298 -- @param ustr  String containing a numerical value with trailing unit
299 -- @return              Number containing the canonical value
300 function parse_units(ustr)
301
302         local val = 0
303
304         -- unit map
305         local map = {
306                 -- date stuff
307                 y   = 60 * 60 * 24 * 366,
308                 m   = 60 * 60 * 24 * 31,
309                 w   = 60 * 60 * 24 * 7,
310                 d   = 60 * 60 * 24,
311                 h   = 60 * 60,
312                 min = 60,
313
314                 -- storage sizes
315                 kb  = 1024,
316                 mb  = 1024 * 1024,
317                 gb  = 1024 * 1024 * 1024,
318
319                 -- storage sizes (si)
320                 kib = 1000,
321                 mib = 1000 * 1000,
322                 gib = 1000 * 1000 * 1000
323         }
324
325         -- parse input string
326         for spec in ustr:lower():gmatch("[0-9%.]+[a-zA-Z]*") do
327
328                 local num = spec:gsub("[^0-9%.]+$","")
329                 local spn = spec:gsub("^[0-9%.]+", "")
330
331                 if map[spn] or map[spn:sub(1,1)] then
332                         val = val + num * ( map[spn] or map[spn:sub(1,1)] )
333                 else
334                         val = val + num
335                 end
336         end
337
338
339         return val
340 end
341
342 -- also register functions above in the central string class for convenience
343 string.escape      = escape
344 string.pcdata      = pcdata
345 string.striptags   = striptags
346 string.split       = split
347 string.trim        = trim
348 string.cmatch      = cmatch
349 string.parse_units = parse_units
350
351
352 --- Appends numerically indexed tables or single objects to a given table.
353 -- @param src   Target table
354 -- @param ...   Objects to insert
355 -- @return              Target table
356 function append(src, ...)
357         for i, a in ipairs({...}) do
358                 if type(a) == "table" then
359                         for j, v in ipairs(a) do
360                                 src[#src+1] = v
361                         end
362                 else
363                         src[#src+1] = a
364                 end
365         end
366         return src
367 end
368
369 --- Combines two or more numerically indexed tables and single objects into one table.
370 -- @param tbl1  Table value to combine
371 -- @param tbl2  Table value to combine
372 -- @param ...   More tables to combine
373 -- @return              Table value containing all values of given tables
374 function combine(...)
375         return append({}, ...)
376 end
377
378 --- Checks whether the given table contains the given value.
379 -- @param table Table value
380 -- @param value Value to search within the given table
381 -- @return              Boolean indicating whether the given value occurs within table
382 function contains(table, value)
383         for k, v in pairs(table) do
384                 if value == v then
385                         return k
386                 end
387         end
388         return false
389 end
390
391 --- Update values in given table with the values from the second given table.
392 -- Both table are - in fact - merged together.
393 -- @param t                     Table which should be updated
394 -- @param updates       Table containing the values to update
395 -- @return                      Always nil
396 function update(t, updates)
397         for k, v in pairs(updates) do
398                 t[k] = v
399         end
400 end
401
402 --- Retrieve all keys of given associative table.
403 -- @param t     Table to extract keys from
404 -- @return      Sorted table containing the keys
405 function keys(t)
406         local keys = { }
407         if t then
408                 for k, _ in kspairs(t) do
409                         keys[#keys+1] = k
410                 end
411         end
412         return keys
413 end
414
415 --- Clones the given object and return it's copy.
416 -- @param object        Table value to clone
417 -- @param deep          Boolean indicating whether to do recursive cloning
418 -- @return                      Cloned table value
419 function clone(object, deep)
420         local copy = {}
421
422         for k, v in pairs(object) do
423                 if deep and type(v) == "table" then
424                         v = clone(v, deep)
425                 end
426                 copy[k] = v
427         end
428
429         return setmetatable(copy, getmetatable(object))
430 end
431
432
433 --- Create a dynamic table which automatically creates subtables.
434 -- @return      Dynamic Table
435 function dtable()
436         return setmetatable({}, { __index =
437                 function(tbl, key)
438                         return rawget(tbl, key)
439                          or rawget(rawset(tbl, key, dtable()), key)
440                 end
441         })
442 end
443
444
445 -- Serialize the contents of a table value.
446 function _serialize_table(t, seen)
447         assert(not seen[t], "Recursion detected.")
448         seen[t] = true
449
450         local data  = ""
451         local idata = ""
452         local ilen  = 0
453
454         for k, v in pairs(t) do
455                 if type(k) ~= "number" or k < 1 or math.floor(k) ~= k or ( k - #t ) > 3 then
456                         k = serialize_data(k, seen)
457                         v = serialize_data(v, seen)
458                         data = data .. ( #data > 0 and ", " or "" ) ..
459                                 '[' .. k .. '] = ' .. v
460                 elseif k > ilen then
461                         ilen = k
462                 end
463         end
464
465         for i = 1, ilen do
466                 local v = serialize_data(t[i], seen)
467                 idata = idata .. ( #idata > 0 and ", " or "" ) .. v
468         end
469
470         return idata .. ( #data > 0 and #idata > 0 and ", " or "" ) .. data
471 end
472
473 --- Recursively serialize given data to lua code, suitable for restoring
474 -- with loadstring().
475 -- @param val   Value containing the data to serialize
476 -- @return              String value containing the serialized code
477 -- @see                 restore_data
478 -- @see                 get_bytecode
479 function serialize_data(val, seen)
480         seen = seen or setmetatable({}, {__mode="k"})
481
482         if val == nil then
483                 return "nil"
484         elseif type(val) == "number" then
485                 return val
486         elseif type(val) == "string" then
487                 return "%q" % val
488         elseif type(val) == "boolean" then
489                 return val and "true" or "false"
490         elseif type(val) == "function" then
491                 return "loadstring(%q)" % get_bytecode(val)
492         elseif type(val) == "table" then
493                 return "{ " .. _serialize_table(val, seen) .. " }"
494         else
495                 return '"[unhandled data type:' .. type(val) .. ']"'
496         end
497 end
498
499 --- Restore data previously serialized with serialize_data().
500 -- @param str   String containing the data to restore
501 -- @return              Value containing the restored data structure
502 -- @see                 serialize_data
503 -- @see                 get_bytecode
504 function restore_data(str)
505         return loadstring("return " .. str)()
506 end
507
508
509 --
510 -- Byte code manipulation routines
511 --
512
513 --- Return the current runtime bytecode of the given data. The byte code
514 -- will be stripped before it is returned.
515 -- @param val   Value to return as bytecode
516 -- @return              String value containing the bytecode of the given data
517 function get_bytecode(val)
518         local code
519
520         if type(val) == "function" then
521                 code = string.dump(val)
522         else
523                 code = string.dump( loadstring( "return " .. serialize_data(val) ) )
524         end
525
526         return code and strip_bytecode(code)
527 end
528
529 --- Strips unnescessary lua bytecode from given string. Information like line
530 -- numbers and debugging numbers will be discarded. Original version by
531 -- Peter Cawley (http://lua-users.org/lists/lua-l/2008-02/msg01158.html)
532 -- @param code  String value containing the original lua byte code
533 -- @return              String value containing the stripped lua byte code
534 function strip_bytecode(code)
535         local version, format, endian, int, size, ins, num, lnum = code:byte(5, 12)
536         local subint
537         if endian == 1 then
538                 subint = function(code, i, l)
539                         local val = 0
540                         for n = l, 1, -1 do
541                                 val = val * 256 + code:byte(i + n - 1)
542                         end
543                         return val, i + l
544                 end
545         else
546                 subint = function(code, i, l)
547                         local val = 0
548                         for n = 1, l, 1 do
549                                 val = val * 256 + code:byte(i + n - 1)
550                         end
551                         return val, i + l
552                 end
553         end
554
555         local function strip_function(code)
556                 local count, offset = subint(code, 1, size)
557                 local stripped = { string.rep("\0", size) }
558                 local dirty = offset + count
559                 offset = offset + count + int * 2 + 4
560                 offset = offset + int + subint(code, offset, int) * ins
561                 count, offset = subint(code, offset, int)
562                 for n = 1, count do
563                         local t
564                         t, offset = subint(code, offset, 1)
565                         if t == 1 then
566                                 offset = offset + 1
567                         elseif t == 4 then
568                                 offset = offset + size + subint(code, offset, size)
569                         elseif t == 3 then
570                                 offset = offset + num
571                         elseif t == 254 or t == 9 then
572                                 offset = offset + lnum
573                         end
574                 end
575                 count, offset = subint(code, offset, int)
576                 stripped[#stripped+1] = code:sub(dirty, offset - 1)
577                 for n = 1, count do
578                         local proto, off = strip_function(code:sub(offset, -1))
579                         stripped[#stripped+1] = proto
580                         offset = offset + off - 1
581                 end
582                 offset = offset + subint(code, offset, int) * int + int
583                 count, offset = subint(code, offset, int)
584                 for n = 1, count do
585                         offset = offset + subint(code, offset, size) + size + int * 2
586                 end
587                 count, offset = subint(code, offset, int)
588                 for n = 1, count do
589                         offset = offset + subint(code, offset, size) + size
590                 end
591                 stripped[#stripped+1] = string.rep("\0", int * 3)
592                 return table.concat(stripped), offset
593         end
594
595         return code:sub(1,12) .. strip_function(code:sub(13,-1))
596 end
597
598
599 --
600 -- Sorting iterator functions
601 --
602
603 function _sortiter( t, f )
604         local keys = { }
605
606         for k, v in pairs(t) do
607                 keys[#keys+1] = k
608         end
609
610         local _pos = 0
611
612         table.sort( keys, f )
613
614         return function()
615                 _pos = _pos + 1
616                 if _pos <= #keys then
617                         return keys[_pos], t[keys[_pos]]
618                 end
619         end
620 end
621
622 --- Return a key, value iterator which returns the values sorted according to
623 -- the provided callback function.
624 -- @param t     The table to iterate
625 -- @param f A callback function to decide the order of elements
626 -- @return      Function value containing the corresponding iterator
627 function spairs(t,f)
628         return _sortiter( t, f )
629 end
630
631 --- Return a key, value iterator for the given table.
632 -- The table pairs are sorted by key.
633 -- @param t     The table to iterate
634 -- @return      Function value containing the corresponding iterator
635 function kspairs(t)
636         return _sortiter( t )
637 end
638
639 --- Return a key, value iterator for the given table.
640 -- The table pairs are sorted by value.
641 -- @param t     The table to iterate
642 -- @return      Function value containing the corresponding iterator
643 function vspairs(t)
644         return _sortiter( t, function (a,b) return t[a] < t[b] end )
645 end
646
647
648 --
649 -- System utility functions
650 --
651
652 --- Test whether the current system is operating in big endian mode.
653 -- @return      Boolean value indicating whether system is big endian
654 function bigendian()
655         return string.byte(string.dump(function() end), 7) == 0
656 end
657
658 --- Execute given commandline and gather stdout.
659 -- @param command       String containing command to execute
660 -- @return                      String containing the command's stdout
661 function exec(command)
662         local pp   = io.popen(command)
663         local data = pp:read("*a")
664         pp:close()
665
666         return data
667 end
668
669 --- Return a line-buffered iterator over the output of given command.
670 -- @param command       String containing the command to execute
671 -- @return                      Iterator
672 function execi(command)
673         local pp = io.popen(command)
674
675         return pp and function()
676                 local line = pp:read()
677
678                 if not line then
679                         pp:close()
680                 end
681
682                 return line
683         end
684 end
685
686 -- Deprecated
687 function execl(command)
688         local pp   = io.popen(command)
689         local line = ""
690         local data = {}
691
692         while true do
693                 line = pp:read()
694                 if (line == nil) then break end
695                 data[#data+1] = line
696         end
697         pp:close()
698
699         return data
700 end
701
702 --- Returns the absolute path to LuCI base directory.
703 -- @return              String containing the directory path
704 function libpath()
705         return require "nixio.fs".dirname(ldebug.__file__)
706 end
707
708
709 --
710 -- Coroutine safe xpcall and pcall versions modified for Luci
711 -- original version:
712 -- coxpcall 1.13 - Copyright 2005 - Kepler Project (www.keplerproject.org)
713 --
714 -- Copyright © 2005 Kepler Project.
715 -- Permission is hereby granted, free of charge, to any person obtaining a
716 -- copy of this software and associated documentation files (the "Software"),
717 -- to deal in the Software without restriction, including without limitation
718 -- the rights to use, copy, modify, merge, publish, distribute, sublicense,
719 -- and/or sell copies of the Software, and to permit persons to whom the
720 -- Software is furnished to do so, subject to the following conditions:
721 --
722 -- The above copyright notice and this permission notice shall be
723 -- included in all copies or substantial portions of the Software.
724 --
725 -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
726 -- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
727 -- OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
728 -- IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
729 -- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
730 -- TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
731 -- OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
732
733 local performResume, handleReturnValue
734 local oldpcall, oldxpcall = pcall, xpcall
735 coxpt = {}
736 setmetatable(coxpt, {__mode = "kv"})
737
738 -- Identity function for copcall
739 local function copcall_id(trace, ...)
740   return ...
741 end
742
743 --- This is a coroutine-safe drop-in replacement for Lua's "xpcall"-function
744 -- @param f             Lua function to be called protected
745 -- @param err   Custom error handler
746 -- @param ...   Parameters passed to the function
747 -- @return              A boolean whether the function call succeeded and the return
748 --                              values of either the function or the error handler
749 function coxpcall(f, err, ...)
750         local res, co = oldpcall(coroutine.create, f)
751         if not res then
752                 local params = {...}
753                 local newf = function() return f(unpack(params)) end
754                 co = coroutine.create(newf)
755         end
756         local c = coroutine.running()
757         coxpt[co] = coxpt[c] or c or 0
758
759         return performResume(err, co, ...)
760 end
761
762 --- This is a coroutine-safe drop-in replacement for Lua's "pcall"-function
763 -- @param f             Lua function to be called protected
764 -- @param ...   Parameters passed to the function
765 -- @return              A boolean whether the function call succeeded and the returns
766 --                              values of the function or the error object
767 function copcall(f, ...)
768         return coxpcall(f, copcall_id, ...)
769 end
770
771 -- Handle return value of protected call
772 function handleReturnValue(err, co, status, arg1, arg2, arg3, arg4, arg5)
773         if not status then
774                 return false, err(debug.traceback(co, arg1), arg1, arg2, arg3, arg4, arg5)
775         end
776
777         if coroutine.status(co) ~= 'suspended' then
778                 return true, arg1, arg2, arg3, arg4, arg5
779         end
780
781         return performResume(err, co, coroutine.yield(arg1, arg2, arg3, arg4, arg5))
782 end
783
784 -- Resume execution of protected function call
785 function performResume(err, co, arg1, arg2, arg3, arg4, arg5)
786         return handleReturnValue(err, co, coroutine.resume(co, arg1, arg2, arg3, arg4, arg5))
787 end