I am working on designing libraries in C and C++ for my company’s proprietary language through ffi. The two libraries which are causing the problem related to the title are the threads and sockets libraries.
threads.cc
#include <windows.h>
#include <stdlib.h>
#include <iostream>
DWORD WINAPI thread_call(void* data)
{
void** datap = (void**)data;
void*(*datafn)(void*) = (void*(*)(void*))datap[0];
datafn(datap[1]);
return 0;
}
extern "C" {
void* gc_alloc(int size);
char* create_thread(char *(*worker)(char *), char *data) {
void** datap = (void**)gc_alloc(sizeof(char*(*)(char*))+sizeof(char*));
datap[0] = (void*)worker;
datap[1] = (void*)data;
HANDLE* handle = (HANDLE*)gc_alloc(sizeof(HANDLE));
*handle = CreateThread(NULL, 0, thread_call, datap, 0, NULL);
return (char*)handle;
}
void close_thread(char* th) {
HANDLE* thandle = (HANDLE*)th;
CloseHandle(*thandle);
}
}
sockets.cc
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <iostream>
#include <string.h>
#include "../websocket.h"
#pragma comment(lib, "ws2_32.lib")
#define BUFFER_SIZE 2048
extern "C"
{
// called at the entry of the program in the proprietary language
int windowsStart()
{
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
return 1;
}
return 0;
}
// called at the end of the program in the proprietary language
void windowsCleanup()
{
WSACleanup();
}
void *gc_alloc(int bytes);
char* runtime_int_to_string(int number);
SOCKET create_socket(int port)
{
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
int iSendResult;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the server address and port
iResult = getaddrinfo(NULL, runtime_int_to_string(port), &hints, &result);
if (iResult != 0)
{
printf("getaddrinfo failed with error: %dn", iResult);
WSACleanup();
return 1;
}
// Create a SOCKET for the server to listen for client connections.
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET)
{
printf("socket failed with error: %ldn", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// Setup the TCP listening socket
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR)
{
printf("bind failed with error: %dn", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result);
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR)
{
printf("listen failed with error: %dn", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
return ListenSocket;
}
int open_socket(int *port, bool *run_socket, int *socketfd, char *(*request_handler)(char *, char *), char *data)
{
SOCKET ListenSocket = create_socket(*port);
SOCKET ClientSocket = INVALID_SOCKET;
int iSendResult;
int iRecvResult;
char buffer[BUFFER_SIZE];
while (*run_socket)
{
ClientSocket = accept(ListenSocket, NULL, NULL); // this is where the error occurs
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %dn", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
iRecvResult = recv(ClientSocket, buffer, BUFFER_SIZE, 0);
char *response = request_handler(data, buffer);
iSendResult = send(ClientSocket, response, strlen(response), 0);
if (iSendResult == SOCKET_ERROR) {
closesocket(ClientSocket);
WSACleanup();
return 1;
}
close_socket(ClientSocket);
}
}
}
Then, two threads with their own sockets are created in a similar fashion to what follows:
char* worker(char* data) {
open_socket(*((int*)data));
}
int main() {
int port1 = 1234;
int port2 = 4321;
windowsStart();
create_thread(worker, (char*)(&port1));
create_thread(worker, (char*)(&port2));
windowsCleanup();
}
If this code were to execute properly, two sockets (one on port 1234 and the other on port 4321) would be continuously running and awaiting a request until the program is ended by the user.
Instead what happens is the first thread runs with success and creates the first socket, but when the second thread is created, the program exits when the accept
function is called from the open_socket
function.
When inspecting the error raised by calling the accept function a second time through Application Verifier, it states “First chance access violation for current stack trace”
Why is it that the first thread runs with no issues but the same exact code fails in the second thread?
Note: Some of the function definitions or code within the functions may look oddly placed or bizarre, and this is due to the fact that this code has to link with my companies proprietary language through c ffi.