// socket_backend.cpp (Native Sockets Implementation) #include "wren.h" #include #include #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #include #pragma comment(lib, "ws2_32.lib") typedef SOCKET socket_t; #define IS_SOCKET_VALID(s) ((s) != INVALID_SOCKET) #define CLOSE_SOCKET(s) closesocket(s) #else #include #include #include #include #include #include #include typedef int socket_t; #define INVALID_SOCKET -1 #define IS_SOCKET_VALID(s) ((s) >= 0) #define CLOSE_SOCKET(s) close(s) #endif // --- Thread-Safe Queue for Asynchronous Operations --- template class ThreadSafeQueue { public: void push(T item) { std::lock_guard lock(mutex_); queue_.push(item); cond_.notify_one(); } T pop() { std::unique_lock lock(mutex_); cond_.wait(lock, [this] { return !queue_.empty(); }); T item = queue_.front(); queue_.pop(); return item; } bool empty() { std::lock_guard lock(mutex_); return queue_.empty(); } private: std::queue queue_; std::mutex mutex_; std::condition_variable cond_; }; // --- Context for Asynchronous Socket Operations --- enum class SocketOp { CONNECT, ACCEPT, READ, WRITE }; struct SocketData { socket_t sock; bool isListener; }; struct SocketContext { SocketOp operation; WrenVM* vm; WrenHandle* socketHandle; WrenHandle* callback; // Operation-specific data std::string host; int port; std::string data; int bytesToRead; // Result data bool success; std::string resultData; std::string errorMessage; socket_t newSocket; // For accepted connections }; // --- Asynchronous Socket Manager --- class AsyncSocketManager { public: AsyncSocketManager(WrenVM* vm) : vm_(vm), running_(true) { for (int i = 0; i < 4; ++i) { threads_.emplace_back([this] { workerThread(); }); } } ~AsyncSocketManager() { running_ = false; for (size_t i = 0; i < threads_.size(); ++i) { requestQueue_.push(nullptr); } for (auto& thread : threads_) { thread.join(); } } void submit(SocketContext* context) { requestQueue_.push(context); } bool completionQueueEmpty() { return completionQueue_.empty(); } void processCompletions() { while (!completionQueue_.empty()) { SocketContext* context = completionQueue_.pop(); WrenHandle* callHandle = wrenMakeCallHandle(vm_, "call(_,_)"); wrenEnsureSlots(vm_, 3); wrenSetSlotHandle(vm_, 0, context->callback); if (context->success) { wrenSetSlotNull(vm_, 1); // No error switch (context->operation) { case SocketOp::ACCEPT: { wrenGetVariable(vm_, "socket", "Socket", 2); void* foreign = wrenSetSlotNewForeign(vm_, 2, 2, sizeof(SocketData)); SocketData* clientData = (SocketData*)foreign; clientData->sock = context->newSocket; clientData->isListener = false; break; } case SocketOp::READ: wrenSetSlotBytes(vm_, 2, context->resultData.c_str(), context->resultData.length()); break; default: wrenSetSlotNull(vm_, 2); break; } } else { wrenSetSlotString(vm_, 1, context->errorMessage.c_str()); wrenSetSlotNull(vm_, 2); } wrenCall(vm_, callHandle); wrenReleaseHandle(vm_, context->socketHandle); wrenReleaseHandle(vm_, context->callback); wrenReleaseHandle(vm_, callHandle); delete context; } } private: void workerThread() { while (running_) { SocketContext* context = requestQueue_.pop(); if (!context || !running_) break; wrenEnsureSlots(context->vm, 1); wrenSetSlotHandle(context->vm, 0, context->socketHandle); SocketData* socketData = (wrenGetSlotType(context->vm, 0) == WREN_TYPE_FOREIGN) ? (SocketData*)wrenGetSlotForeign(context->vm, 0) : nullptr; if (!socketData) { context->success = false; context->errorMessage = "Invalid socket object."; completionQueue_.push(context); continue; } switch (context->operation) { case SocketOp::CONNECT: { addrinfo hints = {}, *addrs; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; getaddrinfo(context->host.c_str(), std::to_string(context->port).c_str(), &hints, &addrs); socket_t sock = INVALID_SOCKET; for (addrinfo* addr = addrs; addr; addr = addr->ai_next) { sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (!IS_SOCKET_VALID(sock)) continue; if (connect(sock, addr->ai_addr, (int)addr->ai_addrlen) == 0) break; CLOSE_SOCKET(sock); sock = INVALID_SOCKET; } freeaddrinfo(addrs); if (IS_SOCKET_VALID(sock)) { socketData->sock = sock; socketData->isListener = false; context->success = true; } else { context->success = false; context->errorMessage = "Connection failed."; } break; } case SocketOp::ACCEPT: { if (socketData->isListener) { context->newSocket = accept(socketData->sock, nullptr, nullptr); context->success = IS_SOCKET_VALID(context->newSocket); if (!context->success) context->errorMessage = "Accept failed."; } else { context->success = false; context->errorMessage = "Cannot accept on a non-listening socket."; } break; } case SocketOp::READ: { if (!socketData->isListener) { char buf[4096]; ssize_t len = recv(socketData->sock, buf, sizeof(buf), 0); if (len > 0) { context->resultData.assign(buf, len); context->success = true; } else { context->success = false; context->errorMessage = "Read failed or connection closed."; } } break; } case SocketOp::WRITE: { if (!socketData->isListener) { ssize_t written = send(socketData->sock, context->data.c_str(), context->data.length(), 0); context->success = (written == (ssize_t)context->data.length()); if(!context->success) context->errorMessage = "Write failed."; } break; } } completionQueue_.push(context); } } WrenVM* vm_; std::atomic running_; std::vector threads_; ThreadSafeQueue requestQueue_; ThreadSafeQueue completionQueue_; }; static AsyncSocketManager* socketManager = nullptr; // --- Socket Foreign Class/Methods --- void socketAllocate(WrenVM* vm) { SocketData* data = (SocketData*)wrenSetSlotNewForeign(vm, 0, 0, sizeof(SocketData)); data->sock = INVALID_SOCKET; data->isListener = false; } void socketConnect(WrenVM* vm) { SocketContext* context = new SocketContext(); context->operation = SocketOp::CONNECT; context->vm = vm; context->socketHandle = wrenGetSlotHandle(vm, 0); context->host = wrenGetSlotString(vm, 1); context->port = (int)wrenGetSlotDouble(vm, 2); context->callback = wrenGetSlotHandle(vm, 3); socketManager->submit(context); wrenSetSlotNull(vm, 0); } void socketListen(WrenVM* vm) { const char* host = wrenGetSlotString(vm, 1); int port = (int)wrenGetSlotDouble(vm, 2); int backlog = (int)wrenGetSlotDouble(vm, 3); addrinfo hints = {}, *addrs; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; getaddrinfo(host, std::to_string(port).c_str(), &hints, &addrs); socket_t sock = INVALID_SOCKET; for (addrinfo* addr = addrs; addr; addr = addr->ai_next) { sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); if (!IS_SOCKET_VALID(sock)) continue; int yes = 1; setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&yes, sizeof(yes)); if (bind(sock, addr->ai_addr, (int)addr->ai_addrlen) == 0) break; CLOSE_SOCKET(sock); sock = INVALID_SOCKET; } freeaddrinfo(addrs); if (IS_SOCKET_VALID(sock) && listen(sock, backlog) == 0) { SocketData* data = (SocketData*)wrenGetSlotForeign(vm, 0); data->sock = sock; data->isListener = true; wrenSetSlotBool(vm, 0, true); } else { if(IS_SOCKET_VALID(sock)) CLOSE_SOCKET(sock); wrenSetSlotBool(vm, 0, false); } } void socketAccept(WrenVM* vm) { SocketContext* context = new SocketContext(); context->operation = SocketOp::ACCEPT; context->vm = vm; context->socketHandle = wrenGetSlotHandle(vm, 0); context->callback = wrenGetSlotHandle(vm, 1); socketManager->submit(context); wrenSetSlotNull(vm, 0); } void socketRead(WrenVM* vm) { SocketContext* context = new SocketContext(); context->operation = SocketOp::READ; context->vm = vm; context->socketHandle = wrenGetSlotHandle(vm, 0); context->bytesToRead = (int)wrenGetSlotDouble(vm, 1); context->callback = wrenGetSlotHandle(vm, 2); socketManager->submit(context); wrenSetSlotNull(vm, 0); } void socketWrite(WrenVM* vm) { SocketContext* context = new SocketContext(); context->operation = SocketOp::WRITE; context->vm = vm; context->socketHandle = wrenGetSlotHandle(vm, 0); int len; const char* bytes = wrenGetSlotBytes(vm, 1, &len); context->data.assign(bytes, len); context->callback = wrenGetSlotHandle(vm, 2); socketManager->submit(context); wrenSetSlotNull(vm, 0); } void socketClose(WrenVM* vm) { SocketData* data = (SocketData*)wrenGetSlotForeign(vm, 0); if (IS_SOCKET_VALID(data->sock)) { CLOSE_SOCKET(data->sock); data->sock = INVALID_SOCKET; } } void socketIsOpen(WrenVM* vm) { SocketData* data = (SocketData*)wrenGetSlotForeign(vm, 0); wrenSetSlotBool(vm, 0, IS_SOCKET_VALID(data->sock)); } WrenForeignMethodFn bindSocketForeignMethod(WrenVM* vm, const char* module, const char* className, bool isStatic, const char* signature) { if (strcmp(module, "socket") != 0) return NULL; if (strcmp(className, "Socket") == 0 && !isStatic) { if (strcmp(signature, "connect(_,_,_)") == 0) return socketConnect; if (strcmp(signature, "listen(_,_,_)") == 0) return socketListen; if (strcmp(signature, "accept(_)") == 0) return socketAccept; if (strcmp(signature, "read(_,_)") == 0) return socketRead; if (strcmp(signature, "write(_,_)") == 0) return socketWrite; if (strcmp(signature, "close()") == 0) return socketClose; if (strcmp(signature, "isOpen") == 0) return socketIsOpen; } return NULL; } WrenForeignClassMethods bindSocketForeignClass(WrenVM* vm, const char* module, const char* className) { WrenForeignClassMethods methods = {0}; if (strcmp(module, "socket") == 0 && strcmp(className, "Socket") == 0) { methods.allocate = socketAllocate; } return methods; }