devkitPro/wut

Network socket changes: socket_lib_finish() shouldn't be a stub function

Closed this issue · 2 comments

If one calls somemopt the homebrew will never be able to exit as somemopt blocks untill socket_lib_finish is called which can't be done if said function is replaced by a stub.

Now you might ask "why call somemopt in the first place?" - the answer is simple: Performance reasons. You'll get much faster up-/downloads by using I/O buffers in userspace and the way to set these buffers is somemopt (socket memory option).

Example code to show the issue:

#define DLBGT_STACK_SIZE	0x2000
#define SOCKET_BUFSIZE		(128 * 1024)
#define SOCKLIB_BUFSIZE		(SOCKET_BUFSIZE * 4) // For send & receive + double buffering

static OSThread dlbgThread;
static uint8_t *dlbgThreadStack;

int dlbgThreadMain(int argc, const char **argv)
{
	void *buf = MEMAllocFromDefaultHeapEx(SOCKLIB_BUFSIZE, 64);
	if(buf == NULL)
		return 1;
	
	if(somemopt(0x01, buf, SOCKLIB_BUFSIZE, 0) == -1 && socketlasterr() != 50) // This will block untill socket_lib_finish() is called
		return 1;
	
	MEMFreeToDefaultHeap(buf);
	return 0;
}

bool initDownloader()
{
	dlbgThreadStack = MEMAllocFromDefaultHeapEx(DLBGT_STACK_SIZE, 8);
	
	if(dlbgThreadStack == NULL || !OSCreateThread(&dlbgThread, dlbgThreadMain, 0, NULL, dlbgThreadStack + DLBGT_STACK_SIZE, DLBGT_STACK_SIZE, 0, OS_THREAD_ATTRIB_AFFINITY_ANY))
		return false;
	
	OSSetThreadName(&dlbgThread, "DL background thread");
	OSResumeThread(&dlbgThread);
	return true;
}

void deinitDownloader()
{
	socket_lib_finish();
	int ret;
	OSJoinThread(&dlbgThread, &ret);
	MEMFreeToDefaultHeap(dlbgThreadStack);
	socket_lib_init();
}

static curl_off_t initSocket(void *ptr, curl_socket_t socket, curlsocktype type)
{
	int o = 1;
	// Activate userspace buffer (fom somemopt)
	if(setsockopt(socket, SOL_SOCKET, 0x10000, &o, sizeof(o)) != 0)
		return 1;
	
	o = SOCKET_BUFSIZE;
	// Set send buffersize
	if(setsockopt(socket, SOL_SOCKET, SO_SNDBUF, &o, sizeof(o)) != 0)
		return 1;
	
	// Set receive buffersize
	if(setsockopt(socket, SOL_SOCKET, SO_RCVBUF, &o, sizeof(o)) != 0)
		return 1;
	
	return 0;
}

int main()
{
	initDownloader();
	CURL *curl = curl_easy_init();
	curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, (curl_sockopt_callback)initSocket);
	
	// Do something, like a download...
	
	curl_easy_cleanup(curl);
	deinitDownloader();
}
fincs commented

I've created a branch: https://github.com/devkitPro/wut/tree/socket-init

This branch makes it possible to call socket_lib_init/finalize again, which now performs refcounting and handles devoptab registration/etc. However, the main addition is the ability to override the socket initialization/deinitialization logic, so that it can be customized to do things such as setting up somemopt. E.g.

void __init_wut_socket()
{
    socket_lib_init();
    // somemopt thread creation goes here
    // wait for somemopt to be initialized? dunno how that's done
    // AC initialization/connection goes here
}

void __fini_wut_socket()
{
    // AC deinitialization goes here
    socket_lib_finish();
    // somemopt thread teardown goes here
}

Please test this branch, and see if the changes suit your use case.

fincs commented

See eb03061

socket_lib_init/finish are no longer stubs.