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; }
int workqueue_drain(lua_State *L) { context_t *context = (context_t *)lua_touserdata(L, 1); workqueue_t *workqueue = context->workqueue; if (!workqueue) return LUA_HANDLE_ERROR_STR(L, "workqueue is not open"); if (workqueue->owner_thread != pthread_self()) return LUA_HANDLE_ERROR_STR(L, "workqueue drain is only available on the owner thread"); pthread_mutex_lock(&workqueue->questions.mutex); pthread_mutex_lock(&workqueue->answers.mutex); uint32_t mark = workqueue->answers.num_items + workqueue->questions.num_items; pthread_mutex_unlock(&workqueue->questions.mutex); while (workqueue->answers.num_items < mark) { pthread_cond_wait(&workqueue->answers.read_avail_cond, &workqueue->answers.mutex); } pthread_mutex_unlock(&workqueue->answers.mutex); return 0; }
int map_join(lua_State *L) { int rc = 0; int err_rc = -1; map_t *map = (map_t *)lua_touserdata(L, 1); for (uint32_t i = 0; i < map->num_threads; i++) { if (map->threads[i].rb) { int ret = pthread_join(map->threads[i].thread, NULL); if (ret) return LUA_HANDLE_ERROR(L, ret); if (map->threads[i].ret) { err_rc = rc; } while (ringbuffer_peek(map->threads[i].rb)) { rb_load(L, map->threads[i].rb); rc++; } ringbuffer_destroy(map->threads[i].rb); } } free(map->threads); map->threads = NULL; map->num_threads = 0; if (err_rc >= 0) { return LUA_HANDLE_ERROR_STR(L, lua_tostring(L, err_rc - rc)); } return rc; }
int map_open(lua_State *L) { uint32_t num_threads = lua_tonumber(L, 1); if (lua_type(L, 2) != LUA_TFUNCTION) return LUA_HANDLE_ERROR_STR(L, "map arg #2 expected a function"); map_thread_t *threads = (map_thread_t *)calloc(num_threads, sizeof(map_thread_t)); int k = lua_gettop(L); for (uint32_t i = 0; i < num_threads; i++) { threads[i].rb = ringbuffer_create(MAX_ARG_SIZE); for (int j = 2; j <= k; j++) { int ret = rb_save_with_growth(L, j, threads[i].rb); if (ret) return LUA_HANDLE_ERROR(L, ret); } lua_pushinteger(L, i + 1); int ret = rb_save_with_growth(L, k + 1, threads[i].rb); if (ret) return LUA_HANDLE_ERROR(L, ret); lua_pop(L, 1); ret = pthread_create(&threads[i].thread, NULL, thread_func, &threads[i]); if (ret) return LUA_HANDLE_ERROR(L, ret); } map_t *map = (map_t *)lua_newuserdata(L, sizeof(map_t)); map->num_threads = num_threads; map->threads = threads; luaL_getmetatable(L, "ipc.map"); lua_setmetatable(L, -2); return 1; }
int cliser_server_client_close(lua_State *L) { server_client_t *server_client = (server_client_t *)lua_touserdata(L, 1); if (server_client->client == NULL) return LUA_HANDLE_ERROR_STR(L, "server client is invalid, either closed or used outside of server function scope"); remove_client(server_client->server, server_client->client); destroy_client(L, server_client->client); server_client->server = NULL; server_client->client = NULL; return 0; }
static int sock_recv_msg(lua_State *L, int sock, ringbuffer_t *rb, copy_context_t *copy_context) { size_t len; int ret = sock_recv(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 recv the correct number of bytes"); if (len > SEND_RECV_SIZE) return LUA_HANDLE_ERROR_STR(L, "message size is too large"); ret = sock_recv(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 recv the correct number of bytes"); ringbuffer_reset_read_pos(rb); ringbuffer_push_write_pos(rb); if (ringbuffer_write(rb, NULL, ret) != (size_t)ret) { ringbuffer_pop_write_pos(rb); return LUA_HANDLE_ERROR_STR(L, "failed to write the correct number of bytes into the ringbuffer"); } ret = rb_load(L, rb); if (ret < 0) return LUA_HANDLE_ERROR(L, ret); return ret; }
int cliser_server_id(lua_State *L) { server_client_t *server_client = (server_client_t *)lua_touserdata(L, 1); if (server_client->client == NULL) return LUA_HANDLE_ERROR_STR(L, "server client is invalid, either closed or used outside of server function scope"); if (lua_gettop(L) > 1) { server_client->client->id = lua_tointeger(L, 2); return 0; } lua_pushinteger(L, server_client->client->id); return 1; }
int workqueue_write(lua_State *L) { context_t *context = (context_t *)lua_touserdata(L, 1); workqueue_t *workqueue = context->workqueue; if (!workqueue) return LUA_HANDLE_ERROR_STR(L, "workqueue is not open"); if (workqueue->owner_thread == pthread_self()) { return workqueue_queue_write(L, 2, &workqueue->questions); } else { return workqueue_queue_write(L, 2, &workqueue->answers); } }
int workqueue_read(lua_State *L) { context_t *context = (context_t *)lua_touserdata(L, 1); workqueue_t *workqueue = context->workqueue; if (!workqueue) return LUA_HANDLE_ERROR_STR(L, "workqueue is not open"); int doNotBlock = luaT_optboolean(L, 2, 0); if (workqueue->owner_thread == pthread_self()) { return workqueue_queue_read(L, &workqueue->answers, doNotBlock); } else { return workqueue_queue_read(L, &workqueue->questions, doNotBlock); } }
static int sock_recv_userdata(lua_State *L, int index, int sock, copy_context_t *copy_context) { if (!luaL_getmetafield(L, index, "_cliser_read")) return LUA_HANDLE_ERROR_STR(L, "could not find _cliser_read function in metatable"); lua_pushvalue(L, index); lua_pushinteger(L, sock); lua_pushlightuserdata(L, copy_context); if (lua_type(L, index + 1) == LUA_TUSERDATA) { lua_pushvalue(L, index + 1); lua_call(L, 4, 0); } else { lua_call(L, 3, 0); } return 0; }
int map_check_errors(lua_State *L) { map_t *map = (map_t *)lua_touserdata(L, 1); for (uint32_t i = 0; i < map->num_threads; i++) { if (map->threads[i].ret) { pthread_join(map->threads[i].thread, NULL); while (ringbuffer_peek(map->threads[i].rb)) { rb_load(L, map->threads[i].rb); } ringbuffer_destroy(map->threads[i].rb); map->threads[i].rb = NULL; return LUA_HANDLE_ERROR_STR(L, lua_tostring(L, -1)); } } return 0; }
int cliser_server_send(lua_State *L) { double t0 = _ipc_seconds(); server_client_t *server_client = (server_client_t *)lua_touserdata(L, 1); if (server_client->client == NULL) return LUA_HANDLE_ERROR_STR(L, "server client is invalid, either closed or used outside of server function scope"); int ret; if (lua_type(L, 2) == LUA_TUSERDATA) { server_client->server->copy_context.use_fastpath = server_client->client->copy_context.use_fastpath; ret = sock_send_userdata(L, 2, server_client->client->sock, &server_client->server->copy_context); } else { ret = sock_send_msg(L, 2, server_client->client->sock, server_client->client->send_rb, &server_client->server->copy_context); } server_client->server->copy_context.tx.total_seconds += (_ipc_seconds() - t0); server_client->server->copy_context.tx.num_calls++; return ret; }
int workqueue_close(lua_State *L) { context_t *context = (context_t *)lua_touserdata(L, 1); workqueue_t *workqueue = context->workqueue; if (!workqueue) return LUA_HANDLE_ERROR_STR(L, "workqueue has already been closed"); pthread_mutex_lock(&workqueue_mutex); workqueue->refcount--; if (workqueue->refcount == 0) { workqueue_remove(workqueue); workqueue_destroy_queue(&workqueue->questions); workqueue_destroy_queue(&workqueue->answers); pthread_mutex_destroy(&workqueue->mutex); free((void *)workqueue->name); } pthread_mutex_unlock(&workqueue_mutex); context->workqueue = NULL; return 0; }
static int sock_recv_msg_peek(lua_State *L, int sock, ringbuffer_t *rb) { size_t len; int ret = recv(sock, &len, sizeof(len), MSG_PEEK | MSG_DONTWAIT); if (ret < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; return LUA_HANDLE_ERROR(L, errno); } if (ret != sizeof(len)) return 0; if (len > SEND_RECV_SIZE) return LUA_HANDLE_ERROR_STR(L, "message size is too large"); ret = recv(sock, ringbuffer_buf_ptr(rb), len, MSG_PEEK | MSG_DONTWAIT); if (ret < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) return 0; return LUA_HANDLE_ERROR(L, errno); } if ((size_t)ret != len) return 0; return ret; }
int cliser_server_tag(lua_State *L) { server_client_t *server_client = (server_client_t *)lua_touserdata(L, 1); if (server_client->client == NULL) return LUA_HANDLE_ERROR_STR(L, "server client is invalid, either closed or used outside of server function scope"); const char *tag = luaL_optstring(L, 2, NULL); if (!tag) { if (server_client->client->tag) { lua_pushstring(L, server_client->client->tag); return 1; } } else { if (server_client->client->tag) { free(server_client->client->tag); } server_client->client->tag = strdup(tag); } return 0; }
static int get_sockaddr(lua_State *L, const char *host, const char *port, struct sockaddr *addr, socklen_t *addrlen) { struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; struct addrinfo *ai; int ret = getaddrinfo(host, port, &hints, &ai); if (ret) return LUA_HANDLE_ERROR_STR(L, gai_strerror(ret)); if (*addrlen < ai->ai_addrlen) { freeaddrinfo(ai); return LUA_HANDLE_ERROR(L, ENOMEM); } memcpy(addr, ai->ai_addr, ai->ai_addrlen); *addrlen = ai->ai_addrlen; freeaddrinfo(ai); 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; }
static int sock_recv_raw(lua_State *L, int sock, void *ptr, size_t len, copy_context_t *copy_context) { int ret = sock_recv(sock, ptr, 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 recv the correct number of bytes"); return 0; }
int cliser_server_clients(lua_State *L) { server_t *server = (server_t *)lua_touserdata(L, 1); uint32_t wait; const char *tag; int fi; int invert_order; if (lua_type(L, 2) == LUA_TFUNCTION) { wait = 0; fi = 2; if (lua_type(L, 3) == LUA_TSTRING) { tag = luaL_optstring(L, 3, NULL); invert_order = luaL_optinteger(L, 4, 0); } else { tag = NULL; invert_order = luaL_optinteger(L, 3, 0); } } else { wait = luaL_checkint(L, 2); if (lua_type(L, 3) != LUA_TFUNCTION) return LUA_HANDLE_ERROR_STR(L, "expected a callback function as argument #3"); fi = 3; tag = NULL; invert_order = 0; } while (wait > server->num_clients) { struct sockaddr addr; socklen_t addrlen = sizeof(addr); int ret = accept(server->sock, &addr, &addrlen); if (ret <= 0) return LUA_HANDLE_ERROR(L, errno); int sock = ret; int value = 1; ret = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)); if (ret) return LUA_HANDLE_ERROR(L, errno); int use_fastpath = can_use_fastpath(L, sock, server->ip_address, ((struct sockaddr_in *)&addr)->sin_addr.s_addr); client_t *client = (client_t *)calloc(1, sizeof(client_t)); client->sock = sock; client->send_rb = ringbuffer_create(SEND_RECV_SIZE); client->recv_rb = ringbuffer_create(SEND_RECV_SIZE); client->copy_context.use_fastpath = use_fastpath; insert_client(server, client); } client_t **clients = alloca(server->num_clients * sizeof(client_t*)); client_t *client = server->clients; uint32_t i = 0; while (client) { if (!tag || (client->tag && strcmp(tag, client->tag) == 0)) { clients[i] = client; i++; } client = client->next; } if (invert_order) { qsort(clients, i, sizeof(client_t*), compare_clients_inverted); } else { qsort(clients, i, sizeof(client_t*), compare_clients); } for (uint32_t j = 0; j < i; j++) { lua_pushvalue(L, fi); server_client_t *server_client = (server_client_t *)lua_newuserdata(L, sizeof(server_client_t)); server_client->server = server; server_client->client = clients[j]; luaL_getmetatable(L, "ipc.server.client"); lua_setmetatable(L, -2); lua_call(L, 1, 0); server_client->server = NULL; server_client->client = NULL; } lua_pushinteger(L, i); return 1; }