LINUX.ORG.RU

Перемещение pthread_mutex_t в памяти

 ,


0

2

На просторах интернетов нашел информацию, что pthread_mutex_t нельзя перемещать в памяти, когда он используется. Но у меня не получается это воспроизвести

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main ()
{
    pthread_mutex_t * mutex_1 = malloc ( sizeof ( pthread_mutex_t ) );
    pthread_mutex_t * mutex_2 = malloc ( sizeof ( pthread_mutex_t ) );
    pthread_mutex_t * mutex_3 = malloc ( sizeof ( pthread_mutex_t ) );

    if ( mutex_1 == NULL || mutex_2 == NULL || mutex_3 == NULL || pthread_mutex_init ( mutex_1, NULL ) != 0 ) {
        free ( mutex_1 ); free ( mutex_2 ); free ( mutex_3 );
        return 1;
    }

    memmove ( mutex_2, mutex_1, sizeof ( pthread_mutex_t ) );
    if ( pthread_mutex_lock ( mutex_2 ) != 0 ) {
        free ( mutex_1 ); free ( mutex_2 ); free ( mutex_3 );
        return 2;
    }

    memmove ( mutex_3, mutex_2, sizeof ( pthread_mutex_t ) );
    if ( pthread_mutex_unlock ( mutex_3 ) != 0 || pthread_mutex_destroy ( mutex_3 ) != 0 ) {
        free ( mutex_1 ); free ( mutex_2 ); free ( mutex_3 );
        return 3;
    }

    free ( mutex_1 ); free ( mutex_2 ); free ( mutex_3 );
    return 0;
}
gcc -O0 -g -std=gnu99 main.c -o main && ./main ; echo $?
>> 0
clang -O0 -g -std=c99 -fsanitize=thread -fno-omit-frame-pointer main.c -o main && ./main ; echo $?
>>
ThreadSanitizer WARNING: unlock of unlocked mutex 0x7d0c0000ef70
    #0 pthread_mutex_unlock <null>:0 (main+0x00000006321c)
    #1 main /home/puchuu/temp/main.c:24 (main+0x0000000a04f5)

0

Где найти конкретную информацию почему нельзя перемещать мьютекс? А можно ли перемещать спинлоки и семафоры?

ЗЫ точное воспроизведение неспособности мьютекса быть перемещенным - очень важно. Программа проверки может отдать сборочной системе флаг типа MUTEX_NOT_MOVABLE и программист может принять решение о костыле.

★★

Последнее исправление: punya (всего исправлений: 2)

Если у тебя возникло желание переместить pthread_mutex_t в памяти, значит, с твоей программой что-то не так.

anonymous
()

На просторах интернетов нашел информацию, что pthread_mutex_t нельзя перемещать в памяти, когда он используется. Но у меня не получается это воспроизвести

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

Где найти конкретную информацию почему нельзя перемещать мьютекс? А можно ли перемещать спинлоки и семафоры?

Грубо говоря, если в API не предусмотренно таких вызовов, то не стоит об этом даже думать, со тороны пользователя это всё объекты с неизвестным внутренним устройством. Посмотреть как это может работает внутри можно, например, в исходниках glibc. Везде это примерно две причины - сложные структуры данных со ссылками на оригинальное положение в памяти и race кондишены.

mashina ★★★★★
()

Где найти конкретную информацию почему нельзя перемещать мьютекс?

Тебе в твоей же ссылке английским по белому написали:

Some mutex implementations on Linux, for example, use the futex system call which specifically waits on the address of the mutex

точное воспроизведение неспособности мьютекса быть перемещенным - очень важно

Зачем вообще их двигать? Храните указатели на них и их и двигайте сколько влезет.

rand
()
Ответ на: комментарий от anonymous

изменение размера структур, содержащих мьютексы это конечно редкое занятие, но перемещение - иногда встречается.

punya ★★
() автор топика
Ответ на: комментарий от mashina

Это понятно, но ведь предупреждения о том, что мьютексы нельзя перемещать нигде нет (кроме stackoverflow). а в манах и примерах постоянно вставляют мьютекс в структуры по ссылке, а не по указателю.

punya ★★
() автор топика
Ответ на: комментарий от punya

Потому что это настолько очевидно, что даже не нуждается в дополнительном обозначении :)

vzzo ★★★
()
Ответ на: комментарий от punya

в манах и примерах постоянно вставляют мьютекс в структуры по ссылке, а не по указателю.

Ссылку на ман - в студию. Заодно посмотрим, чем «по указателю» отличается от «по ссылке».

tailgunner ★★★★★
()
Ответ на: комментарий от punya

Это понятно, но ведь предупреждения о том, что мьютексы нельзя перемещать нигде нет (кроме stackoverflow).

4.2.

POSIX (pthread_mutex_init):

Only mutex itself may be used for performing synchronization. The result of referring to copies of mutex in calls to pthread_mutex_lock(), pthread_mutex_trylock(), pthread_mutex_unlock(), and pthread_mutex_destroy() is undefined.

Sorcerer ★★★★★
()
Ответ на: комментарий от punya

Это понятно, но ведь предупреждения о том, что мьютексы нельзя перемещать нигде нет (кроме stackoverflow)

Значит, ничего не понтяно. Если бы было понятно, то искал бы в мане где разрешено так делать.

mashina ★★★★★
()
Ответ на: комментарий от tailgunner

я не силен в терминах. я имею в виду

struct {
  pthread_mutex_t m;
  int b;
} a;

m и b будут расположены в памяти последовательно и перемещая a ты переместишь m.

вот например пример из мана http://unixhelp.ed.ac.uk/CGI/man-cgi?pthread_mutex_init

struct obj {
  pthread_mutex_t om;
  int refcnt;
  ...
};

ну я специально искать не буду. я видел много раз такую запись.

punya ★★
() автор топика
Ответ на: комментарий от mv

В токсах создаешь тему - приходят и трахают мозги «почему создал в токсах», создаешь не в токсах - «почему не в токсах». Да где создать тему, чтобы ваша кодла нос не сувала?

punya ★★
() автор топика
Ответ на: комментарий от mv

давай так. я всегда буду постить треды в токсы и тебя кастовать. и перемещай там куда тебе усрется. ок?

punya ★★
() автор топика

Хм, всегда думал что pthread_mutex_t это указатель на opaque тип, и с его копированием/перемещением никаких проблем не возникает. Оказывается так только в фряхе, в linux же как всегда сделали через жопу.

slovazap ★★★★★
()
Ответ на: комментарий от slovazap

. Оказывается так только в фряхе, в linux же как всегда сделали через жопу.

Через жопу как раз сделали в бзде если действительно так сделали, ~ очередная «фича», которой нельзя пользоваться.

mashina ★★★★★
()
Ответ на: комментарий от slovazap

pthread_mutex_t уже opaque тип и копировать его нельзя даже если в конкретной реализации это указатель. Если забить на этот очевидный факт, то всё равно это практически ненужная фича, в подавляющем большинстве юз кейсов мьютекс и так лежит в объекте.

mashina ★★★★★
()
Ответ на: комментарий от mashina

Т.е. аргумент - «в linux этого нет, но мне это не нужно, значит всё сделано правильно». Так я и думал.

slovazap ★★★★★
()
Ответ на: комментарий от mashina

А с какой жопы pthread_mutex_t нельзя двигать? Хранить указатели на него в рантайме даунизм, ибо указатель итак передаётся во все функции рантайма.

В ведре указатели на юзерспейс-говно не сохраняются.

Тест пацана конечно фекалия, но вот норм:

#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>

void * get_page(void) {
  return mmap(NULL, 4096, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, 0, 0);
}

void free_page(void * ptr) {
  munmap(ptr, 4096);
}

int main(void) {
  pthread_mutex_t * mutex = get_page();

  if(pthread_mutex_init(mutex, NULL))
    return 1;

  void * last = mutex;
  mutex = memcpy(get_page(), mutex, sizeof(pthread_mutex_t));
  free_page(last);

  if(pthread_mutex_lock(mutex))
    return 2;

  last = mutex;
  mutex = memcpy(get_page(), mutex, sizeof(pthread_mutex_t));
  free_page(last);

  if(pthread_mutex_unlock(mutex) || pthread_mutex_destroy(mutex))
    return 3;
}

В нём если функция обратится туда, где до этого лежал мутекс - будет сегфаулт, у пацана же это не так и спокойно можно.

Т.е. всё работает и я не вижу причин по которым это не будет работать.

anonymous
()
Ответ на: комментарий от mashina

Ещё один чукча писатель?

Царь же (или косплеит царя).

tailgunner ★★★★★
()
Ответ на: комментарий от punya

Тут нет ни ссылок, ни указателей.

А такие структуры обычно создаются в куче, поэтому один и тот же m лежит по одному и тому же адресу.

yoghurt ★★★★★
()
Ответ на: комментарий от mashina

Т.е. ты обосрался, а я чукча? Хорошо устроился. Тыж мне опишешь хоть какую-нибудь логику, по которой pthread_mutex_t нельзя перемещать?

Я сказал - логики нет, я предоставил пруфец. А ты покурлыкал и даже копипасты не привел, и кто из нас чукча?

anonymous
()
Ответ на: комментарий от anonymous

Тыж мне опишешь хоть какую-нибудь логику, по которой pthread_mutex_t нельзя перемещать?

Укурок, тред прочитай, уже всё было написано.

mashina ★★★★★
()
Ответ на: комментарий от mashina

балаболка, ты же ссылки дашь на эти «написано», либо обосрёшься, как ты обосрался только что?

Что я там должен увидеть? Как нулёвое говно скопипастило кусок мануала даже не понимая что там написано?

Давай для ушлёпков я поясню - там написано, что юзать надо сам мутекс. Т.е. не так:

m1, m2;

lock(m1);
memcpy(m2, m1);
unlock(m2)
lock(m1);
...

Т.е. мютекс должнен быть один, ибо функции могут обновлять какие-то данные в мутексе, а копия будет не обновлена - это и есть неопределённость.

Если же ты просто меняешь местоположение мутекса и не юзаешь его старое положение - т.е. не комбинируешь устаревшие копии с текущим, то нет никакой неопределённости.

Поскольку ты нулёвое, давай я тебе покажу простой пример, чтобы до тебя дошло:

  void add10(uint64_t * sum) { *sum += 10;}

  uint64_t * sum = &(uint64_t){};
  add10(sum);add10(sum);add10(sum);//тут нет уб.
  uint64_t * sum2 = &(uint64_t){*sum};//теперь sum2 является истиной суммой, а sum (не) валидной.
  add10(sum2);add10(sum2);add10(sum2);//тут нет уб.
  add10(sum);//а вот тут уб, ибо sum является валидной до тех пор, пока мы не заюзаем любую её копию, после же юза любой копии - все копии, кроме текущей становятся не валидными.
  fprintf(stderr, "%lu\n", *sum);
  fprintf(stderr, "%lu\n", *sum2);

Это банальная особенность любого глобального состояния.

anonymous
()
Ответ на: комментарий от kvap

ага. можно пересоздавать перед перемещением. но видишь, какая тут ситуация : такое только в реализации futex и впринципе пересоздание - трудоемкая операция. получается проще всегда хранить указатель на неперемещаемую область памяти с мьютексом и все будет в ажуре.

ЗЫ я не знаю где реализовано лучше, где хуже. я в этих реализациях ничего не понимаю =)

punya ★★
() автор топика
Ответ на: комментарий от punya

я в этих реализациях ничего не понимаю =)

Само желание перемещать мютексы говорит о непонимании простых базовых вещей.

tailgunner ★★★★★
()
Ответ на: комментарий от punya

получается проще всегда хранить указатель на неперемещаемую область памяти с мьютексом и все будет в ажуре.

Так или иначе получается состояние гонки. Нельзя просто так взять и порезать объект за который идёт конкуренция доступа (мьютекс обычно используется как средство синхронизации этого доступа). Чтобы проводить какие-то деструктивные действия над самим объектом его нужно сначала как-то гарантированно вывести из употребления (если удалось поймать мьютекс в незалоченном состоянии, то это ешё не значит, что в следующие несколько тактов его никто не залочит снова).

И делается либо через глобальный лок, либо методом создания новго объекта, переноса в него ресурсов из старого и пометкой страрого объекта как невалидного. Мьютекс в невалидном объекте продолжает жить своей жизнью пока сам объект не будет утилизирован GC (например, через счётчик ссылок, ~ shared_ptr).

Работа с мьютексом в отрыве от объекта к которому он привязан подкидывает ещё больше проблем. Мьютекс не нужно перемещать (при условии наличия такой возможности, которой у pthreads нет) и не нужно с ним работать по указателю.

mashina ★★★★★
()
Ответ на: комментарий от mashina

Ну ты разогнался. То что ты говоришь, справедливо вообще для любого объекта. Если юзер из 2 параллельных потоков делает перемещение или ресайз общих данных - то он сам виноват. Я имел в виду перемещение, когда условия возможности этого перемещения для обычных объектов соблюдены. Но вы говорите, что даже в этом случае перемещать мьютекс нельзя. Я понял вас.

punya ★★
() автор топика
Ответ на: комментарий от punya

Если юзер из 2 параллельных потоков делает перемещение или ресайз общих данных - то он сам виноват.

Не данных, а объекта к которому эти данные привязаны. Т.е. одно дело выделить под сам объект и данные один кусок памяти порезав его соответствующим образом и другое данные, которые могут меняться в объёме, держать отдельно от объекта. Во втором случае никаких проблем нет, т.к. объект всегда будет защищён мьютексом, а в первом случае ещё отдельно нужно защищать обращение к самому объекту. Суть моего посыла в том, что создать эти условия именно для самого объекта (первый случай) достаточно геморно.

mashina ★★★★★
()
Ответ на: комментарий от mashina

Я понял. У меня ситуация, когда сам объект защищен и никто его и его мьютекс трогать во время перемещения не сможет.

punya ★★
() автор топика
Ответ на: комментарий от punya

Тыж видишь, что это ламо обосралось и начало копипастить мой пост. Зачем ты с ним говоришь? Тебе не ясно, что оно бесполезное и нулёвое?

Твой мутекс перемещать в памяти можно. А будешь пытаться говорить со всякими удотами скатишься в говном и в башке кроме мифов и легенд ничего не будет. Мне жаль, что ты его «понял». Похоже это не лечится.

anonymous
()
Ответ на: комментарий от mashina

Ха, эка ты обосралось, ничтожество. Тыж ответишь мне? Либо так и будешь валятся в говне?

Работа с мьютексом в отрыве от объекта к которому он привязан подкидывает ещё больше проблем. Мьютекс не нужно перемещать (при условии наличия такой возможности, которой у pthreads нет) и не нужно с ним работать по указателю.

Ещё раз, говно, ты же пруфец приведёшь где написано, что pthread-мутекс нельзя перемещать?

Ты обосрался и пытаешься свичнуть тему на «вообще перемещать нельзя», чтобы как можно быстрее уйти от темы, где ты сел в лужу. Т.е. ты пытаешься сделать не важным возможность перемещения у pthread-мутекса. Как можно быть такой жалкой?

anonymous
()
Ответ на: комментарий от anonymous

пока ты тут парашу свою лил, тебя уже все залупами раздавили. проваливай отсюда, вонючая каналья :)

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.