высскажитесь о реализации
От: niXman Ниоткуда https://github.com/niXman
Дата: 03.10.14 09:05
Оценка:
приветствую!

проблема:
в сборках MinGW-W64, которые я произвожу, и которые используют POSIX можель потоков, libstdc++.dll зависит от winpthreads.dll.
некоторым юзерам не нравится то, что им приходится "тащить" winpthreads.dll даже в том случае, когда их код не использует потоков.
мне, по правде говоря, не понятна проблема, ведь winpthreads.dll имеет объем 60-70 Кб, если мне не изменяет память.

в общем, хочу сделать так, чтоб winpthreads.dll связывалась динамически, если она нужна.

тут лежит код GCC'шной обертки над Pthreads.
если вырезать все лишнее, что получится что-то типа:

static inline int
__gthread_create (__gthread_t *__threadid, void *(*__func) (void*), void *__args)
{
  return __gthrw_(pthread_create) (__threadid, NULL, __func, __args);
}

static inline int
__gthread_join (__gthread_t __threadid, void **__value_ptr)
{
  return __gthrw_(pthread_join) (__threadid, __value_ptr);
}

static inline int
__gthread_detach (__gthread_t __threadid)
{
  return __gthrw_(pthread_detach) (__threadid);
}

static inline int
__gthread_equal (__gthread_t __t1, __gthread_t __t2)
{
  return __gthrw_(pthread_equal) (__t1, __t2);
}

static inline __gthread_t
__gthread_self (void)
{
  return __gthrw_(pthread_self) ();
}

static inline int
__gthread_yield (void)
{
  return __gthrw_(sched_yield) ();
}

static inline int
__gthread_once (__gthread_once_t *__once, void (*__func) (void))
{
  if (__gthread_active_p ())
    return __gthrw_(pthread_once) (__once, __func);
  else
    return -1;
}

static inline int
__gthread_key_create (__gthread_key_t *__key, void (*__dtor) (void *))
{
  return __gthrw_(pthread_key_create) (__key, __dtor);
}

static inline int
__gthread_key_delete (__gthread_key_t __key)
{
  return __gthrw_(pthread_key_delete) (__key);
}

static inline void *
__gthread_getspecific (__gthread_key_t __key)
{
  return __gthrw_(pthread_getspecific) (__key);
}

static inline int
__gthread_setspecific (__gthread_key_t __key, const void *__ptr)
{
  return __gthrw_(pthread_setspecific) (__key, __ptr);
}

static inline void
__gthread_mutex_init_function (__gthread_mutex_t *__mutex)
{
  if (__gthread_active_p ())
    __gthrw_(pthread_mutex_init) (__mutex, NULL);
}

static inline int
__gthread_mutex_destroy (__gthread_mutex_t *__mutex)
{
  if (__gthread_active_p ())
    return __gthrw_(pthread_mutex_destroy) (__mutex);
  else
    return 0;
}

static inline int
__gthread_mutex_lock (__gthread_mutex_t *__mutex)
{
  if (__gthread_active_p ())
    return __gthrw_(pthread_mutex_lock) (__mutex);
  else
    return 0;
}

static inline int
__gthread_mutex_trylock (__gthread_mutex_t *__mutex)
{
  if (__gthread_active_p ())
    return __gthrw_(pthread_mutex_trylock) (__mutex);
  else
    return 0;
}

#if _GTHREAD_USE_MUTEX_TIMEDLOCK
static inline int
__gthread_mutex_timedlock (__gthread_mutex_t *__mutex,
               const __gthread_time_t *__abs_timeout)
{
  if (__gthread_active_p ())
    return __gthrw_(pthread_mutex_timedlock) (__mutex, __abs_timeout);
  else
    return 0;
}
#endif

static inline int
__gthread_mutex_unlock (__gthread_mutex_t *__mutex)
{
  if (__gthread_active_p ())
    return __gthrw_(pthread_mutex_unlock) (__mutex);
  else
    return 0;
}

#if !defined( PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP) \
  || defined(_GTHREAD_USE_RECURSIVE_MUTEX_INIT_FUNC)
static inline int
__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *__mutex)
{
  if (__gthread_active_p ())
    {
      pthread_mutexattr_t __attr;
      int __r;

      __r = __gthrw_(pthread_mutexattr_init) (&__attr);
      if (!__r)
    __r = __gthrw_(pthread_mutexattr_settype) (&__attr,
                           PTHREAD_MUTEX_RECURSIVE);
      if (!__r)
    __r = __gthrw_(pthread_mutex_init) (__mutex, &__attr);
      if (!__r)
    __r = __gthrw_(pthread_mutexattr_destroy) (&__attr);
      return __r;
    }
  return 0;
}
#endif

static inline int
__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *__mutex)
{
  return __gthread_mutex_lock (__mutex);
}

static inline int
__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex)
{
  return __gthread_mutex_trylock (__mutex);
}

#if _GTHREAD_USE_MUTEX_TIMEDLOCK
static inline int
__gthread_recursive_mutex_timedlock (__gthread_recursive_mutex_t *__mutex,
                     const __gthread_time_t *__abs_timeout)
{
  return __gthread_mutex_timedlock (__mutex, __abs_timeout);
}
#endif

static inline int
__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *__mutex)
{
  return __gthread_mutex_unlock (__mutex);
}

static inline int
__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex)
{
  return __gthread_mutex_destroy (__mutex);
}

#ifdef _GTHREAD_USE_COND_INIT_FUNC
static inline void
__gthread_cond_init_function (__gthread_cond_t *__cond)
{
  if (__gthread_active_p ())
    __gthrw_(pthread_cond_init) (__cond, NULL);
}
#endif

static inline int
__gthread_cond_broadcast (__gthread_cond_t *__cond)
{
  return __gthrw_(pthread_cond_broadcast) (__cond);
}

static inline int
__gthread_cond_signal (__gthread_cond_t *__cond)
{
  return __gthrw_(pthread_cond_signal) (__cond);
}

static inline int
__gthread_cond_wait (__gthread_cond_t *__cond, __gthread_mutex_t *__mutex)
{
  return __gthrw_(pthread_cond_wait) (__cond, __mutex);
}

static inline int
__gthread_cond_timedwait (__gthread_cond_t *__cond, __gthread_mutex_t *__mutex,
              const __gthread_time_t *__abs_timeout)
{
  return __gthrw_(pthread_cond_timedwait) (__cond, __mutex, __abs_timeout);
}

static inline int
__gthread_cond_wait_recursive (__gthread_cond_t *__cond,
                   __gthread_recursive_mutex_t *__mutex)
{
  return __gthread_cond_wait (__cond, __mutex);
}

static inline int
__gthread_cond_destroy (__gthread_cond_t* __cond)
{
  return __gthrw_(pthread_cond_destroy) (__cond);
}

как вы могли заметить, в некоторых обертках производится тест 'if( __gthread_active_p() )'. для архитектур, не поддерживающих weak references(типа windows), функция '__gthread_active_p()' имеет такое определение:
static inline int
__gthread_active_p (void)
{
  return 1;
}

так вот, я хочу переделать все эти обертки так, чтоб в каждой из них использовалась функция '__gthread_active_p()', но которая в своем теле будет производить проверку, на предмет подгружена ли winpthreads.dll, и если не подгружена — производить ее подгрузку и резолвить нужные символы. символы резолвится будут в некоторую структуру, мемберы которой и будут использоваться в обертках.

да, я понимаю что это даст некоторую просадку в производительности из-за постоянной проверки некоторого флага. в качестве проверки флага думаю использовать атомарные операции.



в общем, что думаете об этом?



спасибо!
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: высскажитесь о реализации
От: watchmaker  
Дата: 03.10.14 11:00
Оценка: 25 (3) +1
Здравствуйте, niXman, Вы писали:

X>так вот, я хочу переделать все эти обертки так, чтоб в каждой из них использовалась функция '__gthread_active_p()', но которая в своем теле будет производить проверку, на предмет подгружена ли winpthreads.dll, и если не подгружена — производить ее подгрузку и резолвить нужные символы. символы резолвится будут в некоторую структуру, мемберы которой и будут использоваться в обертках.


X> в качестве проверки флага думаю использовать атомарные операции.

Не стоит бросаться сразу использовать атомарные операции — подход double check работает быстрее — будет лишь небольшая проверка при первом вызове (всё равно незаметная на фоне инициализации библиотеки), а все остальные вызовы даже атомарные операции для проверки использовать не будут — лишь одно неатомарное чтение флага, что всё же менее накладно.

X>да, я понимаю что это даст некоторую просадку в производительности из-за постоянной проверки некоторого флага.

X>в общем, что думаете об этом?
Да и без самого флага можно обойтись. Смотри, при загрузке библиотеки у тебя появляется переменная, куда сохраняется адрес функции. Соответственно, когда тебе нужно вызвать функцию, то происходит переход по адресу, записанному в этой переменной. И это по сути неуменьшаемые расходы по вызову функции из dll загруженных после старта программы (за исключением хаков вроде самодифицирующегося кода).
Так вот, обычно эта самая переменная с адресом функции не инициализирована при старте программы ничем интересным (там либо мусор, либо null). А ты просто запиши в неё указатель на функцию, делающую загрузку библиотеки:
using sched_yield_ptr = int (*)(void);
sched_yield_ptr sched_yield = sched_yield_delayed_load;

int sched_yield_delayed_load(void) {
    delayed_load();
    return sched_yield();
}

void delayed_load() {
   mutex.lock();
   load_pthread_library();
   sched_yield = load_real_sched_yield();
   pthread_self = load_real_pthread_self();
   // и все остальные функции из pthread
   mutex.unlock();
}

Теперь у тебя вызов функций (очевидно, за исключением первого) стоит столько же, сколько и обычный вызов функции из dll — никаких проверок флагов.



Также этот механизм уже доступен кое-где из коробки, то есть без модификации исходного кода. Правда, в таких автоматических решениях иногда, как нас предупреждают, бывают проблемы с потоками и tls, так что на первый взгляд несколько опрометчиво было бы загружать pthreads через этот механизм, хотя может и будет работать.
Отредактировано 03.10.2014 11:38 watchmaker . Предыдущая версия . Еще …
Отредактировано 03.10.2014 11:36 watchmaker . Предыдущая версия .
Re[2]: высскажитесь о реализации
От: niXman Ниоткуда https://github.com/niXman
Дата: 03.10.14 11:33
Оценка:
Здравствуйте, watchmaker, Вы писали:

по поводу 'double-checked' — да, это и лучше и проще чем мой вариант. так и сделаю. спасибо!
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: высскажитесь о реализации
От: Хон Гиль Дон Россия  
Дата: 03.10.14 11:41
Оценка:
Здравствуйте, niXman, Вы писали:


X>проблема:

X>в сборках MinGW-W64, которые я произвожу, и которые используют POSIX можель потоков, libstdc++.dll зависит от winpthreads.dll.
X>некоторым юзерам не нравится то, что им приходится "тащить" winpthreads.dll даже в том случае, когда их код не использует потоков.
X>мне, по правде говоря, не понятна проблема, ведь winpthreads.dll имеет объем 60-70 Кб, если мне не изменяет память.

X>в общем, хочу сделать так, чтоб winpthreads.dll связывалась динамически, если она нужна.


А что, аналогов MS'овского /DELAYLOAD в MinGW нет?
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[2]: высскажитесь о реализации
От: niXman Ниоткуда https://github.com/niXman
Дата: 03.10.14 11:42
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

ХГД>А что, аналогов MS'овского /DELAYLOAD в MinGW нет?

знать бы еще, о чем речь...
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[3]: высскажитесь о реализации
От: niXman Ниоткуда https://github.com/niXman
Дата: 03.10.14 11:44
Оценка:
Здравствуйте, niXman, Вы писали:

X>знать бы еще, о чем речь...

аа, понял.
нет, этого нет.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[4]: высскажитесь о реализации
От: Хон Гиль Дон Россия  
Дата: 03.10.14 12:02
Оценка:
Здравствуйте, niXman, Вы писали:

X>>знать бы еще, о чем речь...

X>аа, понял.
X>нет, этого нет.

Так может лучше сразу delayload имплементировать? Там, насколько я помню идея была простая — в IAT вместо jmp на нужную функцию из dll помещались jmp на вспомогательную функцию, которая и подгружала dll.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[5]: высскажитесь о реализации
От: niXman Ниоткуда https://github.com/niXman
Дата: 03.10.14 12:25
Оценка:
Здравствуйте, Хон Гиль Дон, Вы писали:

ХГД>Так может лучше сразу delayload имплементировать? Там, насколько я помню идея была простая — в IAT вместо jmp на нужную функцию из dll помещались jmp на вспомогательную функцию, которая и подгружала dll.

вот скоро у нас появится официальный "стол заказов" фитчей на реализацию за вознаграждение — тогда, глядишь, кто-то и пронспонсирует реализацию этой фитчи

а сейчас нужно сделать это попроще.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 03.10.2014 14:32 niXman . Предыдущая версия . Еще …
Отредактировано 03.10.2014 12:37 niXman . Предыдущая версия .
Re[6]: высскажитесь о реализации
От: niXman Ниоткуда https://github.com/niXman
Дата: 03.10.14 12:49
Оценка: 19 (2)
сейчас у нас в перспективе реализации:
1. SEH for 32bit
2. ability to output PDB
3. porting address/thread/UB -sanitezers
4. implementation of gcc threads backend without pthreads
5. compiler plugin for visual studio
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.