Closed squeek502 closed 1 year ago
It can be reproduced with a super minimal Lua module that does the same as the bare Libuv test code above:
libuv_test.c
#include <lua.h>
#include <lauxlib.h>
#include <uv.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
static void stat_cb(uv_fs_t* req) {
if (req->result < 0) {
printf("error %ld\n", req->result);
} else {
uv_stat_t* stat = &req->statbuf;
printf("size %" PRIu64 "\n", stat->st_size);
}
uv_fs_req_cleanup(req);
}
static int libuv_test(lua_State *L)
{
uv_fs_t req;
int ret;
ret = uv_fs_stat(uv_default_loop(), &req, "test.lua", stat_cb);
printf("stat %d\n", ret);
ret = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
printf("run %d\n", ret);
uv_loop_close(uv_default_loop());
return 0;
}
int luaopen_libuv_test(lua_State *L)
{
lua_pushcfunction(L, libuv_test);
return 1;
}
test.lua
local libuv_test = require('libuv_test')
libuv_test()
Maybe related to dynamic library loading? Not sure what the difference would be otherwise.
Does look to be dl
-related. Can create a reproduction from the 'using libuv directly' test by sticking it in a dynamic library:
libuv_dl.c
#include <uv.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
static void stat_cb(uv_fs_t* req) {
if (req->result < 0) {
printf("error %ld\n", req->result);
} else {
uv_stat_t* stat = &req->statbuf;
printf("size %" PRIu64 "\n", stat->st_size);
}
uv_fs_req_cleanup(req);
}
void libuv_test(void) {
uv_fs_t req;
int ret;
ret = uv_fs_stat(uv_default_loop(), &req, "test.lua", stat_cb);
printf("stat %d\n", ret);
ret = uv_run(uv_default_loop(), UV_RUN_DEFAULT);
printf("run %d\n", ret);
uv_loop_close(uv_default_loop());
}
main.c
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
int main() {
void *handle;
void (*libuv_test_fn)(void);
char *error;
handle = dlopen ("./libuv_dl.so", RTLD_NOW|RTLD_LOCAL);
if (!handle) {
fputs (dlerror(), stderr);
exit(1);
}
libuv_test_fn = dlsym(handle, "libuv_test");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
(*libuv_test_fn)();
dlclose(handle);
}
Also possibly of interest:
The leak can be fixed in the example in the previous comment (https://github.com/luvit/luv/issues/552#issuecomment-873365524) by creating a thread from main
(when it's called doesn't matter). These are probably relevant for what type of thing is possibly happening (as mentioned in the previous comment):
(note: simply linking against pthread
is not enough, though, pthread_create
/pthread_join
has to actually be called to fix the leak)
Not sure exactly what this means or how we should treat this. Still unsure why it wasn't being reported previously, will try testing with Valgrind 3.13.0 (the version that was being used on Travis CI previously) to see if it's reported there. EDIT: Valgrind 3.13.0 has a bug that makes it incompatible with newer versions of glibc, so I'm not able to test this easily.
"fixed" main.c
#include <stdlib.h>
#include <stdio.h>
#include <dlfcn.h>
#include <pthread.h>
void* thread_func(void * arg)
{
return NULL;
}
int main(int argc, char **argv) {
void *handle;
void (*libuv_test_fn)(void);
char *error;
pthread_t thread_id;
pthread_create(&thread_id, NULL, &thread_func, NULL);
pthread_join(thread_id, NULL);
handle = dlopen ("./libuv_dl.so", RTLD_NOW|RTLD_LOCAL);
if (!handle) {
fputs (dlerror(), stderr);
exit(1);
}
libuv_test_fn = dlsym(handle, "libuv_test");
if ((error = dlerror()) != NULL) {
fputs(error, stderr);
exit(1);
}
(*libuv_test_fn)();
dlclose(handle);
}
Valgrind output:
==155368== Command: ./libuv_main
==155368==
stat 0
size 24
run 0
==155368==
==155368== HEAP SUMMARY:
==155368== in use at exit: 0 bytes in 0 blocks
==155368== total heap usage: 14 allocs, 14 frees, 4,219 bytes allocated
==155368==
==155368== All heap blocks were freed -- no leaks are possible
Yeah, seems to be fixed. :+1:
In setting up CI with Github Actions (https://github.com/luvit/luv/pull/551), the valgrind step is reporting leaks (and I can reproduce the leaks locally). It happens with multiple tests (not sure of everything that triggers it yet), and is not only related to the thread bindings. For example, here's the reported leak after running
test-fs.lua
(with luajit):This can also be reproduced with a minimal file:
However, I cannot reproduce it with a theoretically similar test case when using Libuv directly:
I'm really unsure what's happening here, or why it's just showing up now. For now, I've added a suppression for these possible leaks in the CI just to get that passing, but it would be nice to figure out if this is a real leak that we should be addressing.