1/* Copyright (c) 2018 CivetWeb developers 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 deal 5 * in the Software without restriction, including without limitation the rights 6 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 * 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 FROM, 18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 * THE SOFTWARE. 20 */ 21 22 23/* Interface functions */ 24LUA_SHARED_INTERFACE void lua_shared_init(void); 25 26LUA_SHARED_INTERFACE void lua_shared_exit(void); 27 28LUA_SHARED_INTERFACE void lua_shared_register(struct lua_State *L); 29 30 31/* Shared data for all Lua states */ 32static lua_State *L_shared; 33static pthread_mutex_t lua_shared_lock; 34 35 36/* Library init. 37 * This function must be called before all other functions. Not thread-safe. */ 38LUA_SHARED_INTERFACE void 39lua_shared_init(void) 40{ 41 /* Create a new Lua state to store all shared data. 42 * In fact, this is used as a hashmap. */ 43 L_shared = lua_newstate(lua_allocator, NULL); 44 45 lua_newtable(L_shared); 46 lua_setglobal(L_shared, "shared"); 47 48 /* Mutex for locking access to the shared state from different threads. */ 49 pthread_mutex_init(&lua_shared_lock, &pthread_mutex_attr); 50} 51 52 53/* Library exit. 54 * This function should be called for cleanup. Not thread-safe. */ 55LUA_SHARED_INTERFACE void 56lua_shared_exit(void) 57{ 58 /* Destroy Lua state */ 59 lua_close(L_shared); 60 L_shared = 0; 61 62 /* Destroy mutex. */ 63 pthread_mutex_destroy(&lua_shared_lock); 64} 65 66#if defined(MG_EXPERIMENTAL_INTERFACES) 67static double 68shared_locked_add(const char *name, size_t namlen, double value, int op) 69{ 70 double ret; 71 72 pthread_mutex_lock(&lua_shared_lock); 73 74 lua_getglobal(L_shared, "shared"); 75 lua_pushlstring(L_shared, name, namlen); 76 lua_rawget(L_shared, -2); 77 ret = lua_tonumber(L_shared, -1); 78 79 if (op > 0) { 80 ret += value; 81 } else { 82 ret = value; 83 } 84 85 lua_getglobal(L_shared, "shared"); 86 lua_pushlstring(L_shared, name, namlen); 87 lua_pushnumber(L_shared, ret); 88 lua_rawset(L_shared, -3); 89 90 lua_pop(L_shared, 3); 91 92 pthread_mutex_unlock(&lua_shared_lock); 93 94 return ret; 95} 96 97 98static int 99lua_shared_add(struct lua_State *L) 100{ 101 size_t symlen = 0; 102 const char *sym = lua_tolstring(L, 1, &symlen); 103 double num = lua_tonumber(L, 2); 104 105 double ret = shared_locked_add(sym, symlen, num, 1); 106 lua_pushnumber(L, ret); 107 return 1; 108} 109 110 111static int 112lua_shared_inc(struct lua_State *L) 113{ 114 size_t symlen = 0; 115 const char *sym = lua_tolstring(L, 1, &symlen); 116 117 double ret = shared_locked_add(sym, symlen, +1.0, 1); 118 lua_pushnumber(L, ret); 119 return 1; 120} 121 122 123static int 124lua_shared_dec(struct lua_State *L) 125{ 126 size_t symlen = 0; 127 const char *sym = lua_tolstring(L, 1, &symlen); 128 129 double ret = shared_locked_add(sym, symlen, -1.0, 1); 130 lua_pushnumber(L, ret); 131 return 1; 132} 133 134 135static int 136lua_shared_exchange(struct lua_State *L) 137{ 138 size_t namlen = 0; 139 const char *name = lua_tolstring(L, 1, &namlen); 140 double num = lua_tonumber(L, 2); 141 double ret; 142 143 pthread_mutex_lock(&lua_shared_lock); 144 145 lua_getglobal(L_shared, "shared"); 146 lua_pushlstring(L_shared, name, namlen); 147 lua_rawget(L_shared, -2); 148 ret = lua_tonumber(L_shared, -1); 149 150 lua_getglobal(L_shared, "shared"); 151 lua_pushlstring(L_shared, name, namlen); 152 lua_pushnumber(L_shared, num); 153 lua_rawset(L_shared, -3); 154 155 lua_pop(L_shared, 3); 156 157 pthread_mutex_unlock(&lua_shared_lock); 158 159 lua_pushnumber(L, ret); 160 return 1; 161} 162 163/* 164static int 165lua_shared_push(struct lua_State *L) 166{ 167 int val_type = lua_type(L, 1); 168 169 if ((val_type != LUA_TNUMBER) && (val_type != LUA_TSTRING) 170 && (val_type != LUA_TBOOLEAN)) { 171 return luaL_error(L, "shared value must be string, number or boolean"); 172 } 173 174 pthread_mutex_lock(&lua_shared_lock); 175 176 lua_getglobal(L_shared, "shared"); 177 lua_pushnumber(L_shared, num); 178 179 if (val_type == LUA_TNUMBER) { 180 double num = lua_tonumber(L, 3); 181 lua_pushnumber(L_shared, num); 182 183 } else if (val_type == LUA_TBOOLEAN) { 184 int i = lua_toboolean(L, 3); 185 lua_pushboolean(L_shared, i); 186 187 } else { 188 size_t len = 0; 189 const char *str = lua_tolstring(L, 3, &len); 190 lua_pushlstring(L_shared, str, len); 191 } 192 193 lua_rawset(L_shared, -3); 194 lua_pop(L_shared, 1); 195 196 pthread_mutex_unlock(&lua_shared_lock); 197 198 return 0; 199} 200*/ 201#endif 202 203 204/* Read access to shared element (x = shared.element) */ 205static int 206lua_shared_index(struct lua_State *L) 207{ 208 int key_type = lua_type(L, 2); 209 int val_type; 210 211 if ((key_type != LUA_TNUMBER) && (key_type != LUA_TSTRING) 212 && (key_type != LUA_TBOOLEAN)) { 213 return luaL_error(L, "shared index must be string, number or boolean"); 214 } 215 216 if (key_type == LUA_TNUMBER) { 217 double num = lua_tonumber(L, 2); 218 219 pthread_mutex_lock(&lua_shared_lock); 220 lua_getglobal(L_shared, "shared"); 221 lua_pushnumber(L_shared, num); 222 223 } else if (key_type == LUA_TBOOLEAN) { 224 int i = lua_toboolean(L, 2); 225 226 pthread_mutex_lock(&lua_shared_lock); 227 lua_getglobal(L_shared, "shared"); 228 lua_pushboolean(L_shared, i); 229 230 } else { 231 size_t len = 0; 232 const char *str = lua_tolstring(L, 2, &len); 233 234 if ((len > 1) && (0 == memcmp(str, "__", 2))) { 235#if defined(MG_EXPERIMENTAL_INTERFACES) 236 /* Return functions */ 237 if (0 == strcmp(str, "__add")) { 238 lua_pushcclosure(L, lua_shared_add, 0); 239 } else if (0 == strcmp(str, "__inc")) { 240 lua_pushcclosure(L, lua_shared_inc, 0); 241 } else if (0 == strcmp(str, "__dec")) { 242 lua_pushcclosure(L, lua_shared_dec, 0); 243 } else if (0 == strcmp(str, "__exchange")) { 244 lua_pushcclosure(L, lua_shared_exchange, 0); 245 /* 246 } else if (0 == strcmp(str, "__push")) { 247 lua_pushcclosure(L, lua_shared_push, 0); 248 } else if (0 == strcmp(str, "__pop")) { 249 lua_pushcclosure(L, lua_shared_pop, 0); 250 */ 251 } else 252#endif 253 { 254 /* Unknown reserved index */ 255 lua_pushnil(L); 256 } 257 return 1; 258 } 259 260 pthread_mutex_lock(&lua_shared_lock); 261 lua_getglobal(L_shared, "shared"); 262 lua_pushlstring(L_shared, str, len); 263 } 264 265 lua_rawget(L_shared, -2); 266 267 val_type = lua_type(L_shared, -1); 268 269 if (val_type == LUA_TNUMBER) { 270 double num = lua_tonumber(L_shared, -1); 271 lua_pushnumber(L, num); 272 273 } else if (val_type == LUA_TBOOLEAN) { 274 int i = lua_toboolean(L_shared, -1); 275 lua_pushboolean(L, i); 276 277 } else if (val_type == LUA_TNIL) { 278 lua_pushnil(L); 279 280 } else { 281 size_t len = 0; 282 const char *str = lua_tolstring(L_shared, -1, &len); 283 lua_pushlstring(L, str, len); 284 } 285 286 lua_pop(L_shared, 2); 287 288 pthread_mutex_unlock(&lua_shared_lock); 289 290 return 1; 291} 292 293 294/* Write access to shared element (shared.element = x) */ 295static int 296lua_shared_newindex(struct lua_State *L) 297{ 298 int key_type = lua_type(L, 2); 299 int val_type = lua_type(L, 3); 300 301 if ((key_type != LUA_TNUMBER) && (key_type != LUA_TSTRING) 302 && (key_type != LUA_TBOOLEAN)) { 303 return luaL_error(L, "shared index must be string, number or boolean"); 304 } 305 if ((val_type != LUA_TNUMBER) && (val_type != LUA_TSTRING) 306 && (val_type != LUA_TBOOLEAN) && (val_type != LUA_TNIL)) { 307 return luaL_error(L, "shared value must be string, number or boolean"); 308 } 309 310 if (key_type == LUA_TNUMBER) { 311 double num = lua_tonumber(L, 2); 312 313 pthread_mutex_lock(&lua_shared_lock); 314 lua_getglobal(L_shared, "shared"); 315 lua_pushnumber(L_shared, num); 316 317 } else if (key_type == LUA_TBOOLEAN) { 318 int i = lua_toboolean(L, 2); 319 320 pthread_mutex_lock(&lua_shared_lock); 321 lua_getglobal(L_shared, "shared"); 322 lua_pushboolean(L_shared, i); 323 324 } else { 325 size_t len = 0; 326 const char *str = lua_tolstring(L, 2, &len); 327 328 if ((len > 1) && (0 == memcmp(str, "__", 2))) { 329 return luaL_error(L, "shared index is reserved"); 330 } 331 332 pthread_mutex_lock(&lua_shared_lock); 333 lua_getglobal(L_shared, "shared"); 334 lua_pushlstring(L_shared, str, len); 335 } 336 337 if (val_type == LUA_TNUMBER) { 338 double num = lua_tonumber(L, 3); 339 lua_pushnumber(L_shared, num); 340 341 } else if (val_type == LUA_TBOOLEAN) { 342 int i = lua_toboolean(L, 3); 343 lua_pushboolean(L_shared, i); 344 345 } else if (val_type == LUA_TNIL) { 346 lua_pushnil(L_shared); 347 348 } else { 349 size_t len = 0; 350 const char *str = lua_tolstring(L, 3, &len); 351 lua_pushlstring(L_shared, str, len); 352 } 353 354 lua_rawset(L_shared, -3); 355 lua_pop(L_shared, 1); 356 357 pthread_mutex_unlock(&lua_shared_lock); 358 359 return 0; 360} 361 362 363/* Register the "shared" library in a new Lua state. 364 * Call it once for every Lua state accessing "shared" elements. */ 365LUA_SHARED_INTERFACE void 366lua_shared_register(struct lua_State *L) 367{ 368 lua_newuserdata(L, 0); 369 lua_newtable(L); 370 371 lua_pushliteral(L, "__index"); 372 lua_pushcclosure(L, lua_shared_index, 0); 373 lua_rawset(L, -3); 374 375 lua_pushliteral(L, "__newindex"); 376 lua_pushcclosure(L, lua_shared_newindex, 0); 377 lua_rawset(L, -3); 378 379 lua_setmetatable(L, -2); 380 lua_setglobal(L, "shared"); 381} 382