jni — c ++ счетчик Шварца с thread_local

Могу ли я использовать Счетчик Шварца (иначе Счетчик Nifty) идиома, с thread_local? (Предполагая, что я заменяю все static с thread_local)

Мне нужно это (помощник для Java JNI потоков):

class ThisThread{
JNIEnv* jni_env{nullptr};
public:
JNIEnv* getEnv(){
if (!jni_env){
// Attach thread
java_vm->GetEnv((void**)&jni_env, JNI_VERSION);
java_vm->AttachCurrentThread(&jni_env, NULL);
}

return jni_env;
}

~ThisThread(){
if (!jni_env) return;
// Deattach thread
java_vm->DetachCurrentThread();
}
};

static thread_local ThisThread this_thread;

Должен быть построен первым и уничтожен последним в каждом потоке.
Я могу позвонить this_thread->getEnv() от деструктора / конструктора другого статического объекта или объекта thread_local.

ОБНОВИТЬ

https://stackoverflow.com/a/30200992 — здесь стандарт говорит, что деструкторы thread_local называются ДО статическими, и мне нужен этот после.

1

Решение

Я думаю, что лучшее решение — реализовать счетчик Шварца как обычно, но реализовать ThisThread класс с точки зрения thread_local статический Impl,

Полный пример с выводами:

// header file
#include <memory>
#include <mutex>
#include <iostream>
#include <thread>

std::mutex emit_mutex;

template<class...Ts>
void emit(Ts&&...ts)
{
auto action = [](auto&&x) { std::cout << x; };
auto lock = std::unique_lock<std::mutex>(emit_mutex);

using expand = int[];
expand{ 0,
(action(std::forward<Ts>(ts)), 0)...
};
}struct ThisThread
{
struct Impl
{
Impl()
{
emit("ThisThread created on thread ", std::this_thread::get_id(), '\n');
}
~Impl()
{
emit("ThisThread destroyed on thread ", std::this_thread::get_id(), '\n');
}
void foo()
{
emit("foo on thread ", std::this_thread::get_id(), '\n');
}
};

decltype(auto) foo() { return get_impl().foo(); }

private:
static Impl& get_impl() { return impl_; }
static thread_local Impl impl_;
};

struct ThisThreadInit
{

ThisThreadInit();
~ThisThreadInit();

static int initialised;
};

extern ThisThread& thisThread;
static ThisThreadInit thisThreadInit;// cppfile

static std::aligned_storage_t<sizeof(ThisThread), alignof(ThisThread)> storage;
ThisThread& thisThread = *reinterpret_cast<ThisThread*>(std::addressof(storage));
int ThisThreadInit::initialised;
thread_local ThisThread::Impl ThisThread::impl_;

ThisThreadInit::ThisThreadInit()
{
if (0 == initialised++)
{
new (std::addressof(storage)) ThisThread ();
}
}

ThisThreadInit::~ThisThreadInit()
{
if (0 == --initialised)
{
thisThread.~ThisThread();
}
}// now use the object

#include <thread>

int main()
{
thisThread.foo();

auto t = std::thread([]{ thisThread.foo(); });
t.join();
}

пример вывода:

ThisThread created on thread 140475785611072
foo on thread 140475785611072
ThisThread created on thread 140475768067840
foo on thread 140475768067840
ThisThread destroyed on thread 140475768067840
ThisThread destroyed on thread 140475785611072
1

Другие решения

Это не ответ, как сделать счетчик Шварца для thread_local static «s (поэтому я не принимаю это как ответ). Но, в конце концов, я придумал это решение, зависящее от платформы (Linux / Android).

#include <jni.h>
#include <cassert>
#include "JavaVM.h"
namespace jni_interface{

class ThisThread{
inline static thread_local pthread_key_t p_key;

static void pthread_dstr(void *arg){
if (!jni_env) return;
java_vm->DetachCurrentThread();
jni_env = nullptr;

pthread_setspecific(p_key, NULL);
pthread_key_delete(p_key);
}

static void register_dstr(void *arg){
{
const int res = pthread_key_create(&p_key, pthread_dstr);
assert(res != EAGAIN);
assert(res != ENOMEM);
assert(res == 0);
}
{
const int res = pthread_setspecific(p_key, arg);
assert(res == 0);
}
}

inline static thread_local JNIEnv* jni_env{nullptr};
public:
JNIEnv* getEnv(){
if (!jni_env){
assert(java_vm);
java_vm->GetEnv((void**)&jni_env, JNI_VERSION);
java_vm->AttachCurrentThread(&jni_env, NULL);       // safe to call in main thread

register_dstr(jni_env);
}

return jni_env;
}
};

static thread_local ThisThread this_thread;
}

Даже если по какой-то причине pthread_dstr будет вызываться перед статическим thread_locals (или с чередованием) в C ++ [другими словами ThisThread уничтожено до последнего использования], при следующем вызове объекта (getEnv()) мы как-то заново инициируем / воссоздаем его и регистрируем pthread_dstr для другого раунда.

Нотабене В целом максимум мы можем иметь PTHREAD_DESTRUCTOR_ITERATIONS раундов, что равно 4. Но мы всегда окажемся на втором, в худшем случае (если реализация C ++ thread_local будет использовать деструкторы p_thread [что будет означать, что OUR pthread_dstr не может называться последним в первом раунде]).

0

По вопросам рекламы [email protected]