static int rb_save_with_growth(lua_State *L, int index, struct ringbuffer_t *rb) { while (1) { ringbuffer_push_write_pos(rb); int ret = rb_save(L, index, rb, 0); if (ret == -ENOMEM) { ringbuffer_pop_write_pos(rb); ringbuffer_grow_by(rb, MAX_ARG_SIZE); } else { return ret; } } }
static int sock_send_msg(lua_State *L, int index, int sock, ringbuffer_t *rb, copy_context_t *copy_context) { ringbuffer_push_write_pos(rb); int ret = rb_save(L, index, rb, 1); if (ret) return LUA_HANDLE_ERROR(L, ret); size_t len = ringbuffer_peek(rb); ringbuffer_pop_write_pos(rb); if (ret) return ret; ret = sock_send(sock, &len, sizeof(len), copy_context); if (ret < 0) return LUA_HANDLE_ERROR(L, errno); if (ret != sizeof(len)) return LUA_HANDLE_ERROR_STR(L, "failed to send the correct number of bytes"); ret = sock_send(sock, ringbuffer_buf_ptr(rb), len, copy_context); if (ret < 0) return LUA_HANDLE_ERROR(L, errno); if ((size_t)ret != len) return LUA_HANDLE_ERROR_STR(L, "failed to send the correct number of bytes"); return 0; }
static int workqueue_queue_write(lua_State *L, int index, queue_t *queue) { pthread_mutex_lock(&queue->mutex); int ret = 0; while (1) { ringbuffer_push_write_pos(queue->rb); ret = rb_save(L, index, queue->rb, 0); if (ret) { ringbuffer_pop_write_pos(queue->rb); if (ringbuffer_peek(queue->rb)) { pthread_cond_wait(&queue->write_avail_cond, &queue->mutex); } else { return LUA_HANDLE_ERROR_STR(L, "workqueue.write message is too big for the ring buffer."); } } else { break; } } queue->num_items++; pthread_cond_signal(&queue->read_avail_cond); pthread_mutex_unlock(&queue->mutex); return ret; }
int rb_save(lua_State *L, int index, ringbuffer_t *rb, int oop, int upval) { char type = lua_type(L, index); switch (type) { case LUA_TNIL: { RB_WRITE(L, rb, &type, sizeof(char)); return 0; } case LUA_TBOOLEAN: { RB_WRITE(L, rb, &type, sizeof(char)); type = lua_toboolean(L, index); RB_WRITE(L, rb, &type, sizeof(char)); return 0; } case LUA_TNUMBER: { #if LUA_VERSION_NUM >= 503 if (lua_isinteger(L, index)) { type = EXTRA_LUA_TINTEGER; RB_WRITE(L, rb, &type, sizeof(char)); lua_Integer n = lua_tointeger(L, index); RB_WRITE(L, rb, &n, sizeof(n)); } else #endif { RB_WRITE(L, rb, &type, sizeof(char)); lua_Number n = lua_tonumber(L, index); RB_WRITE(L, rb, &n, sizeof(n)); } return 0; } case LUA_TSTRING: { RB_WRITE(L, rb, &type, sizeof(char)); size_t str_len; const char *str = lua_tolstring(L, index, &str_len); RB_WRITE(L, rb, &str_len, sizeof(str_len)); RB_WRITE(L, rb, str, str_len); return 0; } case LUA_TTABLE: { RB_WRITE(L, rb, &type, sizeof(char)); int top = lua_gettop(L); int ret; lua_pushnil(L); while (lua_next(L, index) != 0) { ret = rb_save(L, top + 1, rb, oop, upval); // key if (ret) { lua_pop(L, 2); return ret; } ret = rb_save(L, top + 2, rb, oop, upval); // value if (ret) { lua_pop(L, 2); return ret; } lua_pop(L, 1); } type = LUA_TNIL; RB_WRITE(L, rb, &type, sizeof(char)); // breaks the read loop // the typename identifies the metatable const char *str = luaT_typename(L, index); if (!str) { if (luaL_callmeta(L, index, "metatablename")) { str = lua_tostring(L, lua_gettop(L)); lua_pop(L, 1); } else { str = ""; } } size_t str_len = strlen(str); RB_WRITE(L, rb, &str_len, sizeof(str_len)); RB_WRITE(L, rb, str, str_len); return 0; } case LUA_TFUNCTION: { RB_WRITE(L, rb, &type, sizeof(char)); if (index != lua_gettop(L)) { lua_pushvalue(L, index); } lua_Debug ar; lua_pushvalue(L, -1); lua_getinfo(L, ">nuS", &ar); if (ar.what[0] != 'L') { luaL_error(L, "attempt to persist a C function '%s'", ar.name); } // this returns different things under LuaJIT vs Lua #if LUA_VERSION_NUM >= 503 lua_dump(L, rb_lua_writer, rb, 0); #else lua_dump(L, rb_lua_writer, rb); #endif size_t str_len = 0; RB_WRITE(L, rb, &str_len, sizeof(size_t)); // zero-terminated // does the serialization accept upvalues? RB_WRITE(L, rb, &upval, sizeof(int)); // upvalues if (upval == 1) { lua_newtable(L); int envIdx = -1; for (int i=1; i <= ar.nups; i++) { const char *name = lua_getupvalue(L, -2, i); if (strcmp(name, "_ENV") != 0) { lua_rawseti(L, -2, i); } else { // ignore _ENV as we assume that this is the global _G variable lua_pop(L, 1); envIdx = i; } } // write upvalue index of _ENV RB_WRITE(L, rb, &envIdx, sizeof(int)); // write upvalue table int ret = rb_save(L, lua_gettop(L), rb, oop, upval); if (ret) { return ret; } lua_pop(L, 1); } else if (ar.nups > 1) { luaL_error(L, "attempt to serialize a funciton with upvalues (i.e. a closure). Use ipc.workqueue.writeup()."); } if (index != lua_gettop(L)) { lua_pop(L, 1); } return 0; } case LUA_TUSERDATA: { if (oop) return -EPERM; const char *str = luaT_typename(L, index); if (!str) { if (luaL_callmeta(L, index, "metatablename")) { str = lua_tostring(L, lua_gettop(L)); lua_pop(L, 1); type = -type; } else { return -EINVAL; } } RB_WRITE(L, rb, &type, sizeof(char)); size_t str_len = strlen(str); RB_WRITE(L, rb, &str_len, sizeof(str_len)); RB_WRITE(L, rb, str, str_len); void *ptr = lua_touserdata(L, index); RB_WRITE(L, rb, ptr, sizeof(void *)); if (luaL_callmeta(L, index, "retain")) { lua_pop(L, 1); } else { return -EINVAL; } return 0; } default: return -EPERM; } }