emilk / loguru

A lightweight C++ logging library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Thread local variable (thread_ec_ptr) prevents library to be unloaded

ypujante opened this issue · comments

I have this scenario where I build an audio plugin on macOS as a dynamic library (.dylib). The host loads the plugin via dlopen and unloads it via dlclose.

My plugin uses loguru for logging.

After spending quite a bit of time figuring out an issue with the plugin I was able to track it down to this line https://github.com/emilk/loguru/blob/master/loguru.cpp#L1559

static LOGURU_THREAD_LOCAL ECPtr thread_ec_ptr = nullptr;

Simply compiling the plugin with loguru (even if not using it) causes the following issue: dlclose no longer unloads the plugin. Which means it is no longer possible to change the plugin on the fly and the entire host has to be brought down and back up again to get the new changes.

I believe the fact that the variable is tread local is what is causing the problem as proven by the fact that removing LOGURU_THREAD_LOCAL fixes the unloading/reloading issue.

Since I do not know or understand the internal workings of loguru, I was wondering if there are cases where this variable is not used and I can simply get rid of it. For example I know for a fact that my plugin will never ever run in a multi threaded environment so a thread local variable is most likely not useful.

Otherwise do you know of a fix (maybe a compiler flag?) that would fix this problem?

If you want to reproduce the problem on your side, I wrote a little program that simulates a host:

#include <iostream>
#include <dlfcn.h>

int main()
{
  void* lib_handle = dlopen("/mnt/vault/deployment/build/re-test-plugin/Debug/TestPlugin.dylib", RTLD_LOCAL);

  if (!lib_handle) {
    std::cerr << "[" << __FILE__ << "] main: Unable to open library: "

         << dlerror() << "\n";

    exit(EXIT_FAILURE);
  }

  std::cout << "Before dlclose" << std::endl;

  if (dlclose(lib_handle) != 0) {

    std::cerr << "[" << __FILE__ << "] main: Unable to close library: "

              << dlerror() << "\n";
  }

  std::cout << "After dlclose" << std::endl;

  return 0;
}

and the main code/plugin can be anything you want, it just has to be compiled as a dynamic library and include loguru.cpp. If you add something like this to the main code

__attribute__((destructor))
static void finalizer3() {
  std::cout << "############### finalizer3 called on unloading..." << std::endl;
}

this will print a message when the dynamic library is unloaded.

// with thread local (wrong)
Before dlclose
After dlclose
############### finalizer3 called on unloading...

// without thread local (right)
Before dlclose
############### finalizer3 called on unloading...
After dlclose