Допустим мы хотим обернуть вызовы методов класса, что бы перед вызовом класс автоматически "блокировался", а после вызова "разблокировался". Распространенная практика.
Один подход заключается в том, что бы operator->() объекта возвращал некий прокси-объект, который в конструкторе блокирует объект, а в деструкторе разблокирует. Тут не нравится то, что прокси-объект должен быть копируемым. Т.е. надо возиться с передачей владения, и он не может содержать не копируемые вложенные объекты (boost::detail::lightweight_mutex::scoped_lock).
Второй подход заключается в том, что бы стеке руками создавать вспомогательный объект (с использованием временного объекта):
scoped_lock(x)->f();
Тут scoped_lock может (должен) быть не копируемым. Хорошо. Но тут встаёт другая проблема — тип scoped_lock должен быть свой для каждого типа пользовательского объект. Т.е. если мы хотим реализовать его в виде повторно-используемого компонента, то это будет выглядеть так:
scoped_lock<X>(x)->f();
scoped_lock<Y>(y)->g();
Не очень красиво с т.з. пользователя.
Собственно что придумалось по этому поводу.
Допустим есть 2 таких несвязанных класса:
struct X
{
void f(){}
boost::detail::lightweight_mutex mtx_;
};
struct Y
{
void g(){}
boost::detail::lightweight_mutex mtx_;
};
И мы хотим, что бы можно было писать вот так:
int main()
{
X x;
lock(x)->f();
Y y;
lock(y)->g();
}
Во-первых, я отделил прокси-объект от самого пользовательского объекта. Т.е. они не связаны по типам, и никто не содержит ни на кого ссылок.
Во-вторых, я отделил создание "стораджа" для прокси-объекта, от создания самого прокси-объекта. Сторадж создается на стеке в месте вызова lock(), а прокси-объект внутри функции lock(). Рушится прокси-объект в конце полного выражения в месте вызова.
По-моему, получилось достаточно интересное по свойствам решение.
При необходимости можно сделать прокси-объект шаблонным (зависимым от типа пользовательского объекта):
Здравствуйте, remark, Вы писали:
R>Во-первых, я отделил прокси-объект от самого пользовательского объекта. Т.е. они не связаны по типам, и никто не содержит ни на кого ссылок. R>Во-вторых, я отделил создание "стораджа" для прокси-объекта, от создания самого прокси-объекта. Сторадж создается на стеке в месте вызова lock(), а прокси-объект внутри функции lock(). Рушится прокси-объект в конце полного выражения в месте вызова.
R>По-моему, получилось достаточно интересное по свойствам решение.
Причём этот подход можно обобщить на любые ситуации с
— возвращением некопируемого временного объекта
Но увы, это не компилируется. Поэтому и пришлось задействовать отложенное конструирование.
Да, кстати!
Зачем сигнатура T* lock(T&) ? Только ради ->, или это плоды рефакторинга?
Если отвлечься от ->, то пускай она будет T& lock(T&), и использовать
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, remark, Вы писали:
R>>Во-первых, я отделил прокси-объект от самого пользовательского объекта. Т.е. они не связаны по типам, и никто не содержит ни на кого ссылок. R>>Во-вторых, я отделил создание "стораджа" для прокси-объекта, от создания самого прокси-объекта. Сторадж создается на стеке в месте вызова lock(), а прокси-объект внутри функции lock(). Рушится прокси-объект в конце полного выражения в месте вызова.
R>>По-моему, получилось достаточно интересное по свойствам решение.
К>Причём этот подход можно обобщить на любые ситуации с К>- возвращением некопируемого временного объекта
Вполне. Только надо учитывать, что этот объект можно использовать только "в пределах одной строки".
Для интереса можно даже так:
К>
template<size_t sz>
struct buf_t
{
mutable char data [sz];
};
char* foo(buf_t<64> const& buf = buf_t<64>())
{
// отъели немного стека во фрейме вызывающей функции
// ...return (char*)buf;
}
О! Самое интересное так:
char* foo(void* buf = alloca(64))
{
// отъели немного стека во фрейме вызывающей функции
// память будет жить до конца вызывающей функцииreturn strcpy((char*)buf, "Hello from dead!");
}
int main()
{
char* str = foo();
printf("%s", str);
// магия: str никаким образом удалять не надо - вся строка у нас на стеке
}
Хммм... вроде это законно, т.к. вычисление аргументов по-умолчанию синтаксически есть часть вызывающей функции.
По крайней мере в MSVC никакие ран-тайм проверки целостности стека не срабатывают.
К>что примерно соответствует встроенному оператору ',' (только тщательно спрятанному) К>
К>(scoped_lock(x.mtx_), x).member();
К>
"(scoped_lock(x.mtx_), x)" не рулит. Изначально у меня был такой вариант "lock()(x)", где lock не шаблонный класс, но operator() у него шаблонный. Но лишние скобочки сильно смущали.
К>Но увы, это не компилируется. Поэтому и пришлось задействовать отложенное конструирование.
Ну, да. Это первое, что я написал
К>Да, кстати! К>Зачем сигнатура T* lock(T&) ? Только ради ->, или это плоды рефакторинга? К>Если отвлечься от ->, то пускай она будет T& lock(T&), и использовать К>
К>lock(x).member();
К>
Да, лучше заменить на ссылку. Указатель — исторически.
Ещё одно приемущество
Переименовать lock в synchronized и прям Java получается
Здравствуйте, alsemm, Вы писали:
R>>Не могу сказать за ядро Windows, но в Linux вот: A>... A>Дык это все случае, кода надо ходить по списку вообще без блокировки или без блокировки проверить пусто/не пусто и только потом блокировать?
Когда ходить по списку вообще без блокировки. В Microsoft тоже так хотят — но всё запатентовано, поэтому приходится довольствоваться только проверкой на пустоту
Одним словом, "извращённые" методы синхронизации рулят
Здравствуйте, remark, Вы писали:
R>Непосредственно открытый мьюткс с одним названием я взял для простоты изложения.
Принято.
R>Каким-либо образом пользовательский класс всё равно должен поддерживать эту функциональность, т.к. он должен содержать мьютекс.
Принято.
R>Более приемлемо это можно записать так:
R>
R>Учитывая минимальный размер функции lock() не составляет труда просто "скопи-пастить" её под разные варианты. R>И я не вижу причин, мешающих применению такого приёма не "своих" классов, главное — что бы класс поддерживал возможность блокировки/разблокировки в каком-либо виде.
И тем не менее, я не вижу особых преимуществ перед записью
Здравствуйте, alsemm, Вы писали:
A>Здравствуйте, remark, Вы писали:
R>>>>Не могу сказать за ядро Windows, но в Linux вот: A>>>... A>>>Дык это все случае, кода надо ходить по списку вообще без блокировки или без блокировки проверить пусто/не пусто и только потом блокировать?
R>>Когда ходить по списку вообще без блокировки. В Microsoft тоже так хотят — но всё запатентовано, поэтому приходится довольствоваться только проверкой на пустоту
A>Вот не думал, что в GPL-ом ядре есть патентованный код. Что за патенты? где про это можно почитать? Было бы очень интересно.
Скажу про то, что заню. RCU (использование которого в ядре linux я приводил здесь
) запатентовано и перепатеновано слева-напрво и справа-налево. Основной автор — Paul McKenney, основной правопреемник — International Business Machines Corporation (IBM). IBM использует эту технологию в своих коммерческих серверах, а ядро линукса, как я понимаю, используется как "песочница". IBM выдаёт на все эти патенты специальное разрешение, которое разрешает использовать их в ядре линукс.
Т.е. не смотря на то, что код покрыт GPL, используемые алгоритмы защищены патентами Соединенных Штатов, что будет посерьёзнее GPL.
Где про это почитать? Не знаю. Мне кажется — нигде. Это не афишируется.
Здесь смотри список патентов (поиск по: автор — McKenney, правопреемник — International Business Machines Corporation): здесь
Так же смотри открытые заявки на патент (поиск по: автор — McKenney, правопреемник — International Business Machines Corporation): здесь
Открытый член класса, да с именем, подразумевающим, что член закрыт (_ на конце) — отличное наследство несчастым, которым "повезет" сапортить этот код.
Можно конечно сделать функцию T* lock(...) другом. Но ее объявление в теле класса будет выглядеть довольно уродливо:
class A
{
mutex mtx_;
friend A* lock<>(A& obj, lock_storage const&);
};
Да и толку-то от такого решения никакого — вы не закрыли возможность дернуть методы класса A без предварительной блокировки экземпляра.
Здравствуйте, Кодт, Вы писали:
К>Причём этот подход можно обобщить...
[длинный-длинный пост поскипан]
Товарищ Кодт наконец-то вышел на второе место в Топ 100 С/С++, сместив уважаемого, но эмигрировавшего от нас телом и, видимо, душей, MaximE.
Поприветствуем, товарищи!
Здравствуйте, Andrew S, Вы писали:
E>>>Я так понял, что все проблемы проистекают из неудобного интерыейса объекта синхронизации?
E>>Если так, то, IMHO, лучше всего подумать нужно ли оно нам? E>>А если таки нужно, то я бы отдедельно решал проблему, как сделать интерфейс удобным, и отдельно, как оформить лок из клиентского кода...
AS>Ну а куда деваться... Есть множество библиотек, где гварды реализованы именно как некопируемые объекты. Приходится извращаться AS>В этом плане, конечно, решение remark'а интересно тем, что позволяет делать inplace вызовы без каких-либо нарушений. Но вот если хочется блокирования на более долгий период и при этом сам блокируемый объект не важен (т.е. он используется неявно другими объектами, как, например, OLEDB сессия и команды), тогда без RVO, похоже, не обойтись. С соответствующими последствиями.
В такой ситуации вообще нет проблемы:
class synchronized
{
mutex mtx;
friend class lock;
...
};
class lock
{
noncopyable_scoped_lock l_;
lock(synchronized&)
...
};
class user_class : public synchronized
{
};
int main()
{
user_class x;
lock l (x);
//...
}
...
GN>Но сейчас есть острая необходимость в прикладном софте под ядро, например антивирус, что бы уметь действително что-то находить, должен работать напрямую с диском, а сейчас месяцами правятся ошибки 2го порядка из-за треша поинтеров в кривых хуках. И еще надо 2е ядро, что бы это не тормозило работу ОС. А как по-другому то получится, если надо одновременно помнить про 68 ресурсов? Если надо не С с классами, а C++ (те же функторы дают выигрыш в скорости ) — пиши километры кода. boost::? Ну да, не забудем реализовать operator new Так что если какая-то "мелочь" поможет упростить разработку, то это по моему очень хорошо А открытый мютекс можно и в интерфейсе задокументировать, это ерунда после регулярного использования: GN>
GN>//
GN>// Calculate the address of the base of the structure given its type, and an
GN>// address of a field within the structure.
GN>//
GN>#define CONTAINING_RECORD(address, type, field) ((type *)( \
GN> (PCHAR)(address) - \
GN> (ULONG_PTR)(&((type *)0)->field)))
GN>
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, remark, Вы писали:
B>Мне кажется, необходимость наличия в защищаемом классе открытого члена mtx_ несколько огланичивает область применения такой обертки.
Обёртку своего класса можно сделать своим другом...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Bell, Вы писали:
B>>Здравствуйте, remark, Вы писали:
B>>Мне кажется, необходимость наличия в защищаемом классе открытого члена mtx_ несколько огланичивает область применения такой обертки.
E>Обёртку своего класса можно сделать своим другом...
Если речь идет только о своих классах — то да, можно, спору нет.
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, Erop, Вы писали:
E>>Здравствуйте, Bell, Вы писали:
B>>>Здравствуйте, remark, Вы писали:
B>>>Мне кажется, необходимость наличия в защищаемом классе открытого члена mtx_ несколько огланичивает область применения такой обертки.
E>>Обёртку своего класса можно сделать своим другом...
B>Если речь идет только о своих классах — то да, можно, спору нет.
Непосредственно открытый мьюткс с одним названием я взял для простоты изложения.
Каким-либо образом пользовательский класс всё равно должен поддерживать эту функциональность, т.к. он должен содержать мьютекс.
Более приемлемо это можно записать так:
class sync
{
boost::mutex mtx_;
friend ...;
};
struct X : sync
{
// ...
};
struct Y : sync
{
// ...
};
Учитывая минимальный размер функции lock() не составляет труда просто "скопи-пастить" её под разные варианты.
И я не вижу причин, мешающих применению такого приёма не "своих" классов, главное — что бы класс поддерживал возможность блокировки/разблокировки в каком-либо виде.
Здравствуйте, Andrew S, Вы писали:
AS>>>>Ну, тоже вариант, в принципе. Может, даже более интересный за счет явного указания намерений. Вот только клиенту придется явно указывать тип этого прокси, ведь порождающую функцию тут применить нельзя по тем же самым причинам. А хотелось бы избежать лишних телодвижений. Библиотечный код пишется один раз, а вот пользуются им гораздо чаще
R>>>Надо подумать...
R>>http://gzip.rsdn.ru/forum/message/2937445.1.aspx
AS>Да, спасибо, посмотрел. Интересное решение, в том плане, что кажется, будто это снимает требование наличия конструктора копирования у временного объекта. На самом деле, все остается на том же уровне — ведь scoped_lock _должен_ быть сконструирован, т.е. конструктора по умолчанию у стораджа быть просто не может.
Нет! Конструктор по-умолчанию boost::optional вообще не вызывает конструктор пользовательского класса, он просто содержит неинициализированный сторадж под класс.
AS>Т.е. сам подход к конструированию интересный, но он не применим в данном конкретном случае.
Именно для этого случая я его закодировал и скомпилировал.
AS>Другой вопрос касаемо этого подхода (там, где он применим) — сможет ли оптимизатор так же хорошо разобраться во всей этой кухне, как и с RVO — это надо смотреть...
Вот набросок варианта для "нелюбителей boost'а", из которого видно, что оверхеда-то тут особо и нет:
Здравствуйте, remark, Вы писали:
R>Учитывая минимальный размер функции lock() не составляет труда просто "скопи-пастить" её под разные варианты. R>И я не вижу причин, мешающих применению такого приёма не "своих" классов, главное — что бы класс поддерживал возможность блокировки/разблокировки в каком-либо виде.
Есть и третий вариант, вообще-то.
Можно завести две функции, ну типа BeginLockedAccess( T& ) и EndLockedAccess( T& ), и звать из твоей функции эти враперы, для того, чтобы начать и закончить.
Ну а для разных параметров определить разные функции просто...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
R>Нет! Конструктор по-умолчанию boost::optional вообще не вызывает конструктор пользовательского класса, он просто содержит неинициализированный сторадж под класс.
Ну так а чем тогда все это отличается от того вариант 2, что предложил я? Фактически, получается то же самое, разница только в том, где конструируется объект...
Здравствуйте, Andrew S, Вы писали:
R>>Нет! Конструктор по-умолчанию boost::optional вообще не вызывает конструктор пользовательского класса, он просто содержит неинициализированный сторадж под класс.
AS>Ну так а чем тогда все это отличается от того вариант 2, что предложил я? Фактически, получается то же самое, разница только в том, где конструируется объект...
Ну твой вариант 2 — это вообще неработающий хак...
От варианта 1 отличается тем, что будет работать и без RVO, и пользователь не попадёт в ситуацию, когда ему линкер вдруг выдаёт непонятную ошибку. А когда пользователь поймёт, что за ошибка, то исправлять её не понятно как... если он, конечно, не читал топик "[Trick] Call wrapper"
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, remark, Вы писали:
R>>Учитывая минимальный размер функции lock() не составляет труда просто "скопи-пастить" её под разные варианты. R>>И я не вижу причин, мешающих применению такого приёма не "своих" классов, главное — что бы класс поддерживал возможность блокировки/разблокировки в каком-либо виде.
E>Есть и третий вариант, вообще-то. E>Можно завести две функции, ну типа BeginLockedAccess( T& ) и EndLockedAccess( T& ), и звать из твоей функции эти враперы, для того, чтобы начать и закончить. E>Ну а для разных параметров определить разные функции просто...
Мммм... а состояние-то где хранить. Допустим BeginLockedAccess() хочет запомнить идентификатор потока, что бы EndLockedAccess() потом мог его сверить.
R>>>Нет! Конструктор по-умолчанию boost::optional вообще не вызывает конструктор пользовательского класса, он просто содержит неинициализированный сторадж под класс.
AS>>Ну так а чем тогда все это отличается от того вариант 2, что предложил я? Фактически, получается то же самое, разница только в том, где конструируется объект...
R>Ну твой вариант 2 — это вообще неработающий хак...
Ровно аналогичный код с boost::optional. Все там работает, ну, можно еще алигмент поправить, если, конечно, для ссылки это важно (в чем я очень сомневаюсь).
R>От варианта 1 отличается тем, что будет работать и без RVO, и пользователь не попадёт в ситуацию, когда ему линкер вдруг выдаёт непонятную ошибку. А когда пользователь поймёт, что за ошибка, то исправлять её не понятно как... если он, конечно, не читал топик "[Trick] Call wrapper"
Проблема в том, что заставить "просто так" работать этот lock на скоуп довольно сложно — надо передавать свой сторадж, а значит, клиенту опять знать тип объекта. Вариант с RVO можно просто отрастить от пустой базы и присваивать константной ссылке, которая будет держать нужное время.
В общем, лично я остановился на варианте с RVO, он мне кажется более понятным как в реализации, так и по побочным эффектам...
Если интересно, могу запостить результат.
Здравствуйте, Andrew S, Вы писали:
R>>>>Нет! Конструктор по-умолчанию boost::optional вообще не вызывает конструктор пользовательского класса, он просто содержит неинициализированный сторадж под класс.
AS>>>Ну так а чем тогда все это отличается от того вариант 2, что предложил я? Фактически, получается то же самое, разница только в том, где конструируется объект...
R>>Ну твой вариант 2 — это вообще неработающий хак...
AS>Ровно аналогичный код с boost::optional.
Подожди-подожди. Я memcpy() объекты не копирую! У меня с т.з. языка всё законно.
R>>Все там работает,
А ты попробуй в объект добавить указатель внутрь себя. И потом погляди куда он будет указывать после memcpy() (если более точно, то там у тебя не memcpy(), а копирование встроенного в объект массива, но по сути это то же самое).
R>>ну, можно еще алигмент поправить, если, конечно, для ссылки это важно (в чем я очень сомневаюсь).
Ссылка в данном случае -- указатель. На Alpha словишь аппаратное прерывание. На x86 тоже словишь, если включена аппаратная проверка выравнивания.
R>>От варианта 1 отличается тем, что будет работать и без RVO, и пользователь не попадёт в ситуацию, когда ему линкер вдруг выдаёт непонятную ошибку. А когда пользователь поймёт, что за ошибка, то исправлять её не понятно как... если он, конечно, не читал топик "[Trick] Call wrapper"
AS>Проблема в том, что заставить "просто так" работать этот lock на скоуп довольно сложно — надо передавать свой сторадж, а значит, клиенту опять знать тип объекта.
Тип стораджа *не* зависит от типа пользовательского объекта. В любом случае, что страшного в этом знании как таковом?
AS>Вариант с RVO можно просто отрастить от пустой базы и присваивать константной ссылке, которая будет держать нужное время.
AS>В общем, лично я остановился на варианте с RVO, он мне кажется более понятным как в реализации, так и по побочным эффектам... AS>Если интересно, могу запостить результат.
R>>>Ну твой вариант 2 — это вообще неработающий хак...
AS>>Ровно аналогичный код с boost::optional.
R>Подожди-подожди. Я memcpy() объекты не копирую! У меня с т.з. языка всё законно.
Там не объект, а _ссылка_.
R>>>Все там работает,
R>А ты попробуй в объект добавить указатель внутрь себя. И потом погляди куда он будет указывать после memcpy() (если более точно, то там у тебя не memcpy(), а копирование встроенного в объект массива, но по сути это то же самое).
Так специально и сделан объект, а не ручками мувится. Все, что там надо добавить — алигн, как это сделано в бусте.
R>>>ну, можно еще алигмент поправить, если, конечно, для ссылки это важно (в чем я очень сомневаюсь).
R>Ссылка в данном случае -- указатель. На Alpha словишь аппаратное прерывание. На x86 тоже словишь, если включена аппаратная проверка выравнивания.
С чего бы это вдруг ссылка стала равна указателю? В данном случае максимум, что надо, макс алигн добавить. А вообще, как я уже говорил, даже 6-ка сворачивает этот код в полностью inline, никакого там копирования нет и не будет в прицнипе Эта реализация фактически остается для совсем дубовых компиляторов.
R>>>От варианта 1 отличается тем, что будет работать и без RVO, и пользователь не попадёт в ситуацию, когда ему линкер вдруг выдаёт непонятную ошибку. А когда пользователь поймёт, что за ошибка, то исправлять её не понятно как... если он, конечно, не читал топик "[Trick] Call wrapper"
AS>>Проблема в том, что заставить "просто так" работать этот lock на скоуп довольно сложно — надо передавать свой сторадж, а значит, клиенту опять знать тип объекта.
R>Тип стораджа *не* зависит от типа пользовательского объекта. В любом случае, что страшного в этом знании как таковом?
В том, что его (знание) надо ИМЕТЬ. Что клиенту бывает довольно неудобно. Например, мне гораздо удобнее написать const Lock &tmp = lock(obj), чем пытаться вспомнить, что там за тип надо отдать в lock (смысл такой операции, кстати, клиенту может быть совсем не очевиден). Везде свои плюсы и минусы...
Здравствуйте, Andrew S, Вы писали:
T>>Таким методом можно проэмулировать динамические массивы на стеке, которые умеет gcc:
AS>А теперь представим, что массив аллоцируют в цикле... Лучше так не эмулировать
alloca — это вообще страшная вещь.
Помимо того, что можно запросить слишком много, если не проверять размер. И помимио того, что нельзя выделять в цикле.
Самая прикольная засада то, что и такая функция может стать причиной переполнения стека:
void f()
{
alloca(1);
}
При условии, что она вызывается так:
void g()
{
for (int i = 0; i != 1000000; ++i)
f();
}
И f() встроится в g(). Тогда тот самый "конец функции", в котором удаляется память, выделенная alloca(1), переносится в конец функции g(). Т.е. память выделяется, выделяется, выделяется... Всё вышеописанное относится к msvc, на gcc не проверял.
Лечится только объявлением всех функций, которые используют alloca, как __declspec(noinline).
B>И тем не менее, я не вижу особых преимуществ перед записью B>
B>lock<X>(x)->f();
B>
А ты имя возьми подлиннее-подлиннее
Помимио длины имени, допустим был long_long_class_name, а стал std::auto_ptr<long_long_class_name>, а потом boost::shared_ptr<long_long_class_name>, а потом опять long_long_class_name.
Плюс ещё есть код библиотечный, а есть прикладной. В библиотечном не жалко сделать несколько телодвижений, что бы в каждом месте использования в прикладном коде элиминировать одно телодвижение. Имхо
Здравствуйте, alsemm, Вы писали:
A>Открытый член класса, да с именем, подразумевающим, что член закрыт (_ на конце) — отличное наследство несчастым, которым "повезет" сапортить этот код. A>Можно конечно сделать функцию T* lock(...) другом. Но ее объявление в теле класса будет выглядеть довольно уродливо:
<> A>Да и толку-то от такого решения никакого — вы не закрыли возможность дернуть методы класса A без предварительной блокировки экземпляра.
Ну, это ровно та же ситуация, что и при работе с интрузивными умными указателями.
Злокодер может несогласованно подёргать за ручки методов AddRef()/Release() или — если разработчик предусмотрительно упрятал их, — за внешние функции intrusive_ptr_add_ref()/intrusive_ptr_release().
Конечно, требуется некоторая культура дизайна.
Во-первых, классы объектов с мьютексами образуют некоторое семейство — в котором доступ к этим мьютексам унифицирован.
А то и унифицирован доступ к объектам в эксклюзивном режиме. И вся уродливость переедет в код библиотеки, на основе которой это семейство будет строиться — не копипастить же, правда?
Во-вторых, мой горький опыт подсказывает, что неинтрузивное управление мьютексами (когда объект лочат извне) — это достаточно короткая дорога к дедлокам.
Работа с объектом-монитором чем-то напоминает работу с апартаментами. Пока выполняется код метода объекта — он залочен. Как только управление выходит за пределы монитора (в цикл ожидания условной переменной, либо в вызов метода другого монитора), нужно сознательно переходить в согласованное состояние и разлочиваться.
Поэтому либо программист вообще должен отказаться от идеи внешнего блокирования, либо — понимая, что предметы эксклюзивного доступа пассивны, — самостоятельно, аккуратно, дисциплинированно выполнять блокировку там, где это положено.
Для укрепления дисциплины можно попробовать накладывать ограничения. Ну скажем,
class Foo
{
public:
void foo() NOT_EXCLUSIVE;
void bar() EXCLUSIVE;
};
.....
Foo x;
x.foo(); // ok
x.bar(); // error
locked(x).bar(); // ok
NOT_EXCLUSIVE — это volatile :) Ну это в порядке бреда, сам бы я не стал припахивать volatile для этих нужд.
Скорее, разделил бы класс на два интерфейса и поигрался с публичностью и дружественностью.
Здравствуйте, Andrew S, Вы писали:
R>>>>Ну твой вариант 2 — это вообще неработающий хак...
AS>>>Ровно аналогичный код с boost::optional.
R>>Подожди-подожди. Я memcpy() объекты не копирую! У меня с т.з. языка всё законно.
AS>Там не объект, а _ссылка_.
Может мы о разных вторых вариантах говорим... я об этом:
Насколько я вижу, констуктор копирования CX1 побитово копирует объект типа boost::detail::lightweight_mutex::scoped_lock.
Если я совсем туплю, то извиняюсь.
R>>>>Все там работает,
R>>А ты попробуй в объект добавить указатель внутрь себя. И потом погляди куда он будет указывать после memcpy() (если более точно, то там у тебя не memcpy(), а копирование встроенного в объект массива, но по сути это то же самое).
AS>Так специально и сделан объект, а не ручками мувится. Все, что там надо добавить — алигн, как это сделано в бусте.
Что я не так понял? Почему там не происходит побитового копирования boost::detail::lightweight_mutex::scoped_lock?
R>>>>ну, можно еще алигмент поправить, если, конечно, для ссылки это важно (в чем я очень сомневаюсь).
R>>Ссылка в данном случае -- указатель. На Alpha словишь аппаратное прерывание. На x86 тоже словишь, если включена аппаратная проверка выравнивания.
AS>С чего бы это вдруг ссылка стала равна указателю? В данном случае максимум, что надо, макс алигн добавить. А вообще, как я уже говорил, даже 6-ка сворачивает этот код в полностью inline, никакого там копирования нет и не будет в прицнипе Эта реализация фактически остается для совсем дубовых компиляторов.
R>>>>От варианта 1 отличается тем, что будет работать и без RVO, и пользователь не попадёт в ситуацию, когда ему линкер вдруг выдаёт непонятную ошибку. А когда пользователь поймёт, что за ошибка, то исправлять её не понятно как... если он, конечно, не читал топик "[Trick] Call wrapper"
AS>>>Проблема в том, что заставить "просто так" работать этот lock на скоуп довольно сложно — надо передавать свой сторадж, а значит, клиенту опять знать тип объекта.
R>>Тип стораджа *не* зависит от типа пользовательского объекта. В любом случае, что страшного в этом знании как таковом?
AS>В том, что его (знание) надо ИМЕТЬ. Что клиенту бывает довольно неудобно. Например, мне гораздо удобнее написать const Lock &tmp = lock(obj), чем пытаться вспомнить, что там за тип надо отдать в lock (смысл такой операции, кстати, клиенту может быть совсем не очевиден). Везде свои плюсы и минусы...
Я наверное опять туплю... но в моём варианте надо писать просто:
A>Открытый член класса, да с именем, подразумевающим, что член закрыт (_ на конце) — отличное наследство несчастым, которым "повезет" сапортить этот код. A>Можно конечно сделать функцию T* lock(...) другом. Но ее объявление в теле класса будет выглядеть довольно уродливо: A>
A>class A
A>{
A> mutex mtx_;
A> friend A* lock<>(A& obj, lock_storage const&);
A>};
A>
A>Да и толку-то от такого решения никакого — вы не закрыли возможность дернуть методы класса A без предварительной блокировки экземпляра.
Суть не в этом.
Куда девать мьютекс? — туда же, где он был при использовании копируемого прокси-объекта или некопируемого явносоздаваемого временного объекта (традиционные альтернативы, которые я описывал в изначальном посте). Данное решение никак не ухудшит инкапсуляции мьютекса.
R>>>>>Ну твой вариант 2 — это вообще неработающий хак...
AS>>>>Ровно аналогичный код с boost::optional.
R>>>Подожди-подожди. Я memcpy() объекты не копирую! У меня с т.з. языка всё законно.
AS>>Там не объект, а _ссылка_.
R>Может мы о разных вторых вариантах говорим... я об этом:
И я об этом.
R>Насколько я вижу, констуктор копирования CX1 побитово копирует объект типа boost::detail::lightweight_mutex::scoped_lock. R>Если я совсем туплю, то извиняюсь.
В этом объекте только и есть, что ссылка на мьютекс. Понятно, что возможны всякие реализации (в т.ч. и экзотические, которые ссылаются на свои подобъекты), но тем не менее, мне сложно представить скоп, в котором необходимо нечто большее чем ссылка на локер + пара флагов. Т.е. в данном конкретном случае это вполне себе легальное решение. Хотя лично я реализовал для себя вариант (1), т.е. насильные move ctor там не применяется.
AS>>В том, что его (знание) надо ИМЕТЬ. Что клиенту бывает довольно неудобно. Например, мне гораздо удобнее написать const Lock &tmp = lock(obj), чем пытаться вспомнить, что там за тип надо отдать в lock (смысл такой операции, кстати, клиенту может быть совсем не очевиден). Везде свои плюсы и минусы...
R>Я наверное опять туплю... но в моём варианте надо писать просто: R>
R>lock(x).f();
R>lock(y).g();
R>
Нет. Приведенный код решает только одну проблему — блокирование на время вызова lock. Довольно часто (я бы сказал даже очень часто) необходимо блокировать объект не на одну операцию, а на несколько. Причем ссылка на сам объект часто бывает не нужна — например, сессии базы данных и стейтменты, которые уже заранее привязаны к сессии и не могут исполняться параллельно. Т.е. хочется простым способом расширить скоп, при этом не вызывая непосредственно методы блокируемого объекта. В твоем случае для этого необходимо знать тип стораджа и задавать сам сторадж извне, что, во-первых, не совсем приятно клиенту (надо помнить тип стораджа и отдельно его создавать), а во-вторых, не совсем ему очевидно.
Здравствуйте, Andrew S, Вы писали:
AS>Нет. Приведенный код решает только одну проблему — блокирование на время вызова lock. Довольно часто (я бы сказал даже очень часто) необходимо блокировать объект не на одну операцию, а на несколько. Причем ссылка на сам объект часто бывает не нужна — например, сессии базы данных и стейтменты, которые уже заранее привязаны к сессии и не могут исполняться параллельно. Т.е. хочется простым способом расширить скоп, при этом не вызывая непосредственно методы блокируемого объекта. В твоем случае для этого необходимо знать тип стораджа и задавать сам сторадж извне, что, во-первых, не совсем приятно клиенту (надо помнить тип стораджа и отдельно его создавать), а во-вторых, не совсем ему очевидно.
А от чего бы не иметь два разных средства?
Одно для защищённого вызова одного метода, а другой, для защищённого вызова группы методов?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
AS>>Нет. Приведенный код решает только одну проблему — блокирование на время вызова lock. Довольно часто (я бы сказал даже очень часто) необходимо блокировать объект не на одну операцию, а на несколько. Причем ссылка на сам объект часто бывает не нужна — например, сессии базы данных и стейтменты, которые уже заранее привязаны к сессии и не могут исполняться параллельно. Т.е. хочется простым способом расширить скоп, при этом не вызывая непосредственно методы блокируемого объекта. В твоем случае для этого необходимо знать тип стораджа и задавать сам сторадж извне, что, во-первых, не совсем приятно клиенту (надо помнить тип стораджа и отдельно его создавать), а во-вторых, не совсем ему очевидно.
E>А от чего бы не иметь два разных средства? E>Одно для защищённого вызова одного метода, а другой, для защищённого вызова группы методов?
Эти две проблемы связаны. Если попробуете реализовать, поймете, как
Здравствуйте, Andrew S, Вы писали:
AS>Эти две проблемы связаны. Если попробуете реализовать, поймете, как
В чём-то связаны, в чём-то нет.
Я так понял, что все проблемы проистекают из неудобного интерыейса объекта синхронизации?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
E>Я так понял, что все проблемы проистекают из неудобного интерыейса объекта синхронизации?
Если так, то, IMHO, лучше всего подумать нужно ли оно нам?
А если таки нужно, то я бы отдедельно решал проблему, как сделать интерфейс удобным, и отдельно, как оформить лок из клиентского кода...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, alsemm, Вы писали:
A>не закрыли возможность дернуть методы класса A без предварительной блокировки экземпляра.
В ядре windows это часто и требуется: проверить список, если не пуст, то заблокировать и поискать (нет, если после проверки будет добавлена нода, ничего не сломается).
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
... R>Куда девать мьютекс? — туда же, где он был при использовании копируемого прокси-объекта или некопируемого явносоздаваемого временного объекта (традиционные альтернативы, которые я описывал в изначальном посте). Данное решение никак не ухудшит инкапсуляции мьютекса.
Согласен. Но при наличии внешней блокировки в виде не копируемого явно создаваемого временного объекта обычно все в одном месте сложено:
class A
{
mutex mtx_;
void a();
void b();
void c();
public:
class Lock;
friend class Lock;
class Lock
{
mutex::lock guard_;
A& a_;
public:
explicit Lock(A& a): guard_(a.mtx_), a_(a) {}
void a() { a_.a(); }
void b() { a_.b(); }
void c() { a_.c(); }
};
};
А как прокомментируете утверждение, что ваше решение позволяет вызывать методы класса без блокировки (забыл случайно кто-то lock дернуть, например)?
...
К>Ну, это ровно та же ситуация, что и при работе с интрузивными умными указателями. К>Злокодер может несогласованно подёргать за ручки методов AddRef()/Release() или — если разработчик предусмотрительно упрятал их, — за внешние функции intrusive_ptr_add_ref()/intrusive_ptr_release().
Подергать руками AddRef()/Release() — это надо еще додуматься до такого. AddRef()/Release() обычно в базовом классе сидят и узнать, что, напрмер, в иерархии:
class Counted { public: void AddRef(); void Release(); };
class Base: public Counted {};
class ABase: public Base {};
class A: public ABase {};
у класса A есть эти методы можно только полазив по исходникам изрядно.
Так что не стоит путать злобного буратину и вменяемого программиста средней квалификации, который, просто может не знаком со спецификой конкретного проекта (не все публичные методы можно вызывать "в лоб").
К>Конечно, требуется некоторая культура дизайна.
К>Во-первых, классы объектов с мьютексами образуют некоторое семейство — в котором доступ к этим мьютексам унифицирован. К>А то и унифицирован доступ к объектам в эксклюзивном режиме. И вся уродливость переедет в код библиотеки, на основе которой это семейство будет строиться — не копипастить же, правда?
К>Во-вторых, мой горький опыт подсказывает, что неинтрузивное управление мьютексами (когда объект лочат извне) — это достаточно короткая дорога к дедлокам.
...
Конечно, если мьютекс торчит наружу из класса и его надо явно лочить то будут проблемы. Если, как в моем примере (http://rsdn.ru/forum/message/2938296.1.aspx
) то никаких проблем с дедлоками не вижу. Плюс есть возможность вызывать несколько методов за одну блокировку (что-то типа транзакций):
A aInst;
// заблокировать aInst один раз и подергать его методы
{
A::Lock l(aInst);
l.a();
l.b();
}
// заблокировать aInst столько раз, сколько методов вызвано
A::Lock(aInst).a();
A::Lock(aInst).b();
}
К>Для укрепления дисциплины можно попробовать накладывать ограничения. Ну скажем, К>
К>NOT_EXCLUSIVE — это volatile :) Ну это в порядке бреда, сам бы я не стал припахивать volatile для этих нужд. К>Скорее, разделил бы класс на два интерфейса и поигрался с публичностью и дружественностью.
imho все методы класса должны быть или синхронизированными или нет, так что 100% поддерживаю ваше предложение растащить класс на два.
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, alsemm, Вы писали:
A>>не закрыли возможность дернуть методы класса A без предварительной блокировки экземпляра.
GN>В ядре windows это часто и требуется: проверить список, если не пуст, то заблокировать и поискать (нет, если после проверки будет добавлена нода, ничего не сломается).
Я в ядре не разбираюсь, поделитесь знанием про хотя бы пару ядерных списков, которые:
1. обычно пустые;
2. проверять их на наличие элементов надо таким хитрым способом;
3. проверки делать надо очень часто.
Здравствуйте, Andrew S, Вы писали:
R>>Насколько я вижу, констуктор копирования CX1 побитово копирует объект типа boost::detail::lightweight_mutex::scoped_lock. R>>Если я совсем туплю, то извиняюсь.
AS>В этом объекте только и есть, что ссылка на мьютекс. Понятно, что возможны всякие реализации (в т.ч. и экзотические, которые ссылаются на свои подобъекты), но тем не менее, мне сложно представить скоп, в котором необходимо нечто большее чем ссылка на локер + пара флагов. Т.е. в данном конкретном случае это вполне себе легальное решение. Хотя лично я реализовал для себя вариант (1), т.е. насильные move ctor там не применяется.
А понятно, мы о разных вещах говорим — я позиционирую Call wrapper как общее решение.
AS>>>В том, что его (знание) надо ИМЕТЬ. Что клиенту бывает довольно неудобно. Например, мне гораздо удобнее написать const Lock &tmp = lock(obj), чем пытаться вспомнить, что там за тип надо отдать в lock (смысл такой операции, кстати, клиенту может быть совсем не очевиден). Везде свои плюсы и минусы...
R>>Я наверное опять туплю... но в моём варианте надо писать просто: R>>
R>>lock(x).f();
R>>lock(y).g();
R>>
AS>Нет. Приведенный код решает только одну проблему — блокирование на время вызова lock. Довольно часто (я бы сказал даже очень часто) необходимо блокировать объект не на одну операцию, а на несколько. Причем ссылка на сам объект часто бывает не нужна — например, сессии базы данных и стейтменты, которые уже заранее привязаны к сессии и не могут исполняться параллельно. Т.е. хочется простым способом расширить скоп, при этом не вызывая непосредственно методы блокируемого объекта. В твоем случае для этого необходимо знать тип стораджа и задавать сам сторадж извне, что, во-первых, не совсем приятно клиенту (надо помнить тип стораджа и отдельно его создавать), а во-вторых, не совсем ему очевидно.
Просто в топике MT guard, any thoughts? ты подчёркивал как раз необходимость вызывать inplace. Отсюда у меня недопонимание.
Здравствуйте, alsemm, Вы писали:
A>А как прокомментируете утверждение, что ваше решение позволяет вызывать методы класса без блокировки (забыл случайно
кто-то lock дернуть, например)?
Да никак не прокомментирую. Я бы так синхронизацию делать не стал.
Мало того, что логику блокирования/разблокирования (в подавляющем большинстве случаев) не стоит выносить наружу. Так ещё и "многопоточный" класс и его "однопоточный" аналог в общем случае имеют вообще разные интерфейсы.
Вот например интерфейс std::queue<>
back
empty
front
pop
push
size
Многопоточная очередь зачастую имеет интерфейс:
pop
push
И никакие лишние операции туда никто не добавляет просто потому, что их можно туда добавить.
Поэтому обеспечение потокобезопасности методом "оборачивания", тем более "внешнего", я лично считаю плохой практикой. Не зависимо того, насколько инкапсулирован мьютекс и есть ли возможность сделать что-то не так.
Здравствуйте, alsemm, Вы писали:
GN>>В ядре windows это часто и требуется: проверить список, если не пуст, то заблокировать и поискать (нет, если после проверки будет добавлена нода, ничего не сломается).
В ядре Linux ещё круче: поискать по списку, не блокируя
A>Я в ядре не разбираюсь, поделитесь знанием про хотя бы пару ядерных списков, которые: A>1. обычно пустые; A>2. проверять их на наличие элементов надо таким хитрым способом; A>3. проверки делать надо очень часто.
E>>Я так понял, что все проблемы проистекают из неудобного интерыейса объекта синхронизации?
E>Если так, то, IMHO, лучше всего подумать нужно ли оно нам? E>А если таки нужно, то я бы отдедельно решал проблему, как сделать интерфейс удобным, и отдельно, как оформить лок из клиентского кода...
Ну а куда деваться... Есть множество библиотек, где гварды реализованы именно как некопируемые объекты. Приходится извращаться
В этом плане, конечно, решение remark'а интересно тем, что позволяет делать inplace вызовы без каких-либо нарушений. Но вот если хочется блокирования на более долгий период и при этом сам блокируемый объект не важен (т.е. он используется неявно другими объектами, как, например, OLEDB сессия и команды), тогда без RVO, похоже, не обойтись. С соответствующими последствиями.
Здравствуйте, alsemm, Вы писали:
A>поделитесь знанием про хотя бы пару ядерных списков, которые: A>1. обычно пустые; A>2. проверять их на наличие элементов надо таким хитрым способом; A>3. проверки делать надо очень часто.
"Обычно" и "очень часто" — это неопределённые величины. Есть, например, правило: "Minimizing the time that a driver holds spin locks can significantly improve both the performance of the driver and of the system overall".
Драйвер — суть набор обработчиков событий (которые вызываются произвольным юзермодным потоком). Обработчики вызываются асинхронно, но последовательность вызовов всегда подчиняется некоторой логике.
Простейший пример (я немного погарячился, пропустив это после двоеточия в посте выше, но сути это не меняет) есть 2 обработчика: create и close. В первом обработчике нечто может быть занесено в список. Если позднее вызван хендлер close, не важно, были ли другие вызовы create после проверки — они занесут в список нечто ненужное.
Помимо явной работы с объектами синхронизации, есть и неявные механизмы (впрочем, порядок вызова create\close тоже к ним относится) вроде IRQL, либо лок делается явно до вызова метода.
Поэтому возможность тонко управлять локами — важна. Возможность при этом использовать С++ обёртки — еще важнее, учитывая классическую ошибку:
if ( some )
p = RemoveHeadList(&list_head);
/*
#define RemoveHeadList(ListHead) \
(ListHead)->Flink;\
{RemoveEntryList((ListHead)->Flink)}
*/
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Здравствуйте, remark, Вы писали:
R>Здравствуйте, alsemm, Вы писали:
A>>А как прокомментируете утверждение, что ваше решение позволяет вызывать методы класса без блокировки (забыл случайно R>кто-то lock дернуть, например)?
R>Да никак не прокомментирую. Я бы так синхронизацию делать не стал.
Тогда я не понял, а про что был пример в первом сообщении этой темы?
R>Не могу сказать за ядро Windows, но в Linux вот:
...
Дык это все случае, кода надо ходить по списку вообще без блокировки или без блокировки проверить пусто/не пусто и только потом блокировать?
Самому ядерные исходники читать совсем нет желания
Здравствуйте, alsemm, Вы писали:
A>Здравствуйте, remark, Вы писали:
R>>Здравствуйте, alsemm, Вы писали:
A>>>А как прокомментируете утверждение, что ваше решение позволяет вызывать методы класса без блокировки (забыл случайно R>>кто-то lock дернуть, например)?
R>>Да никак не прокомментирую. Я бы так синхронизацию делать не стал.
A>Тогда я не понял, а про что был пример в первом сообщении этой темы?
Если я так не делаю, это не значит, что никто так не делает. Некоторые считают это удобным.
Здравствуйте, gear nuke, Вы писали:
GN>Здравствуйте, alsemm, Вы писали:
A>>поделитесь знанием про хотя бы пару ядерных списков, которые: A>>1. обычно пустые; A>>2. проверять их на наличие элементов надо таким хитрым способом; A>>3. проверки делать надо очень часто.
GN>"Обычно" и "очень часто" — это неопределённые величины. Есть, например, правило: "Minimizing the time that a driver holds spin locks can significantly improve both the performance of the driver and of the system overall".
"Обычно" — в > 50% обращений;
"очень часто" — скажем каждые 50мс.
Если надо список лочить, то куда ж вы денетесь-то Если список в > 50% обращений к нему пустой, то оптимизация имеет смысл. Если нет — толку от нее мало, все равно вам хватать spin lock или чего там еще.
...
GN>Поэтому возможность тонко управлять локами — важна. Возможность при этом использовать С++ обёртки — еще важнее, учитывая классическую ошибку:
GN>
GN>if ( some )
GN> p = RemoveHeadList(&list_head);
GN>/*
GN> #define RemoveHeadList(ListHead) \
GN> (ListHead)->Flink;\
GN> {RemoveEntryList((ListHead)->Flink)}
GN>*/
GN>
У этой ошибки я вижу две причины:
1. кривое объявление ядерного макроса — ну что ж поделать, косяки у всех бывают;
2. лень драйверописателей писать
if ( some )
{
foo();
}
вместо
if ( some )
foo();
А на чем сейчас ядра пишут? Мне всегда казалось, что как и сто лет назад на голом C Ядрописателям все эти шаблонные оберки-припарки до лампочки.
Драйвера открытые тоже, наверное, на С делают, чтоб на всяких экзотических железках работали. А виндовые дрова, наверное, и на C++ делают, гораздо это проще и удобней, не спорю.
Здравствуйте, remark, Вы писали:
R>Здравствуйте, alsemm, Вы писали:
R>>>Не могу сказать за ядро Windows, но в Linux вот: A>>... A>>Дык это все случае, кода надо ходить по списку вообще без блокировки или без блокировки проверить пусто/не пусто и только потом блокировать?
R>Когда ходить по списку вообще без блокировки. В Microsoft тоже так хотят — но всё запатентовано, поэтому приходится довольствоваться только проверкой на пустоту R>Одним словом, "извращённые" методы синхронизации рулят
Вот не думал, что в GPL-ом ядре есть патентованный код. Что за патенты? где про это можно почитать? Было бы очень интересно.
AS>>Ну а куда деваться... Есть множество библиотек, где гварды реализованы именно как некопируемые объекты. Приходится извращаться AS>>В этом плане, конечно, решение remark'а интересно тем, что позволяет делать inplace вызовы без каких-либо нарушений. Но вот если хочется блокирования на более долгий период и при этом сам блокируемый объект не важен (т.е. он используется неявно другими объектами, как, например, OLEDB сессия и команды), тогда без RVO, похоже, не обойтись. С соответствующими последствиями.
R>В такой ситуации вообще нет проблемы:
Как раз проблема есть. Придется писать lock для каждого объекта, что как минимум не комильфо. Решение с RVO позволяет просто пользоваться общей базой и держать возврашаемый объект лока (который уже производного типа) константной ссылкой.
Здравствуйте, Andrew S, Вы писали:
AS>>>Ну а куда деваться... Есть множество библиотек, где гварды реализованы именно как некопируемые объекты. Приходится извращаться AS>>>В этом плане, конечно, решение remark'а интересно тем, что позволяет делать inplace вызовы без каких-либо нарушений. Но вот если хочется блокирования на более долгий период и при этом сам блокируемый объект не важен (т.е. он используется неявно другими объектами, как, например, OLEDB сессия и команды), тогда без RVO, похоже, не обойтись. С соответствующими последствиями.
R>>В такой ситуации вообще нет проблемы:
AS>Как раз проблема есть. Придется писать lock для каждого объекта, что как минимум не комильфо. Решение с RVO позволяет просто пользоваться общей базой и держать возврашаемый объект лока (который уже производного типа) константной ссылкой.
lock зависит только от базового класса synchronized, но не от конкретного пользовательского класса. Т.ч. всё должно быть ОК.
Здравствуйте, alsemm, Вы писали:
A>Если надо список лочить, то куда ж вы денетесь-то Если список в > 50% обращений к нему пустой, то оптимизация имеет смысл. Если нет — толку от нее мало, все равно вам хватать spin lock или чего там еще.
Всё верно для юзерленда, просто в ядре принято очень бережно относиться к ресурсам. К тому же из-за отсутствия понятия "один тред" от тщательного продумывания синхронизации никуда не денешся.
A>У этой ошибки я вижу две причины: A>1. кривое объявление ядерного макроса — ну что ж поделать, косяки у всех бывают;
Это косяк MS, и в общем-то понятно, что не исправляют для совместимости со старыми версиями DDK.
A>2. лень драйверописателей писать A>
A>if ( some )
A>{
A> foo();
A>}
A>
Обычно даже через строчку учат писать
A>А на чем сейчас ядра пишут? Мне всегда казалось, что как и сто лет назад на голом C Ядрописателям все эти шаблонные оберки-припарки до лампочки.
Про *nix не знаю, а MS даже недавно разработанный KMDF реализовали на C. Несмотря на
Реальные пацаны конечно пишут драйвера устройств и фильтры FS на C и сразу без ошибок.
Но сейчас есть острая необходимость в прикладном софте под ядро, например антивирус, что бы уметь действително что-то находить, должен работать напрямую с диском, а сейчас месяцами правятся ошибки 2го порядка из-за треша поинтеров в кривых хуках. И еще надо 2е ядро, что бы это не тормозило работу ОС. А как по-другому то получится, если надо одновременно помнить про 68 ресурсов? Если надо не С с классами, а C++ (те же функторы дают выигрыш в скорости ) — пиши километры кода. boost::? Ну да, не забудем реализовать operator new Так что если какая-то "мелочь" поможет упростить разработку, то это по моему очень хорошо А открытый мютекс можно и в интерфейсе задокументировать, это ерунда после регулярного использования:
//
// Calculate the address of the base of the structure given its type, and an
// address of a field within the structure.
//#define CONTAINING_RECORD(address, type, field) ((type *)( \
(PCHAR)(address) - \
(ULONG_PTR)(&((type *)0)->field)))
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
AS>>>>Ну а куда деваться... Есть множество библиотек, где гварды реализованы именно как некопируемые объекты. Приходится извращаться AS>>>>В этом плане, конечно, решение remark'а интересно тем, что позволяет делать inplace вызовы без каких-либо нарушений. Но вот если хочется блокирования на более долгий период и при этом сам блокируемый объект не важен (т.е. он используется неявно другими объектами, как, например, OLEDB сессия и команды), тогда без RVO, похоже, не обойтись. С соответствующими последствиями.
R>>>В такой ситуации вообще нет проблемы:
AS>>Как раз проблема есть. Придется писать lock для каждого объекта, что как минимум не комильфо. Решение с RVO позволяет просто пользоваться общей базой и держать возврашаемый объект лока (который уже производного типа) константной ссылкой.
R>lock зависит только от базового класса synchronized, но не от конкретного пользовательского класса. Т.ч. всё должно быть ОК.
Объекты _уже_ есть, т.е. отращивать их ниоткуда нельзя. Есть locableValue, которое суть хранит референсы на объект и объект синхронизации. Это дело возвращается пользователю. А что дальше с ним делать — ну вот, собственно, и челендж. Если использовать для гварда RVO, тогда отрастив обертку _гварда_ (а не объект) от общего пустого класса мы получаем бесплатный способ блокировки без указания типа. Пример я уже приводил.
Здравствуйте, alsemm, Вы писали:
R>>Одним словом, "извращённые" методы синхронизации рулят A>Вот не думал, что в GPL-ом ядре есть патентованный код. Что за патенты? где про это можно почитать? Было бы очень интересно.
В твоих программах он тоже, скорее всего, есть...
Здравствуйте, remark, Вы писали:
R>Т.е. не смотря на то, что код покрыт GPL, используемые алгоритмы защищены патентами Соединенных Штатов
Позволяет ли это использовать алгоритмы, защищенные патентами (да поразит аллах того, кто их придумал), в производных от Линукса продуктах? GPLv3 точно позволяет, там о патентах (да поразит аллах того, кто их придумал) сказано четко, но Linux распространяется на условиях GPLv2.
Здравствуйте, Cyberax, Вы писали:
C>Здравствуйте, alsemm, Вы писали:
R>>>Одним словом, "извращённые" методы синхронизации рулят A>>Вот не думал, что в GPL-ом ядре есть патентованный код. Что за патенты? где про это можно почитать? Было бы очень интересно. C>В твоих программах он тоже, скорее всего, есть...
Чужие исходники в свои пректы включаю внимательно прочитав лицензию и для верности спрашиваю у автора, если удается найти его координаты конечно, можно-ли его поделку использовать. Это конечно не 100% гарантия, что все чисто, но хоть что-то.
Здравствуйте, remark, Вы писали:
R>Т.е. не смотря на то, что код покрыт GPL, используемые алгоритмы защищены патентами Соединенных Штатов, что будет посерьёзнее GPL.
Типа берёшь GPL продукт, развиваешь, а что там наразвивал -- патентуешь и оба на!!!
Код хоть и открытый вроде, а переиспользовать всё равно нельзя...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, remark, Вы писали:
R>>Т.е. не смотря на то, что код покрыт GPL, используемые алгоритмы защищены патентами Соединенных Штатов, что будет посерьёзнее GPL.
E>Типа берёшь GPL продукт, развиваешь, а что там наразвивал -- патентуешь и оба на!!! E>Код хоть и открытый вроде, а переиспользовать всё равно нельзя...
Дети идут спать!
Лицензия на исходный код и патент — это совершенно разные вещи. Лицензия защищает исходный код, патент — изобретение.
Дети не могут запатентовать просто исходный код. Дети могут запатентовать только новые, нетривиальные и полезные алгоритмы.
Плюс к этому, если дети запатентуют како-либо алгоритм на территории России, то большинство людей в мире просто глубоко положет на это.
Здравствуйте, remark, Вы писали:
R>Плюс к этому, если дети запатентуют како-либо алгоритм на территории России, то большинство людей в мире просто глубоко положет на это.
Ну так и что? Патентуешь (в США) идеи и алгоритмы, а кто переиспользует код, на этих алгоритмах базирующийся -- тот нарушит твой патент и будет ему а-та-та от слепой амерской Фемиды. А то что он остался, в соответсвии с лицензией GPL пофиг
p. s.
А разве в РФ алгоритмы можно патентовать?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
R>>Т.е. не смотря на то, что код покрыт GPL, используемые алгоритмы защищены патентами Соединенных Штатов, что будет посерьёзнее GPL.
E>Типа берёшь GPL продукт, развиваешь, а что там наразвивал -- патентуешь и оба на!!! E>Код хоть и открытый вроде, а переиспользовать всё равно нельзя...
Нет, конечно. Пункт 7 в GPLv2:
If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice.
This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License.
Здравствуйте, alsemm, Вы писали:
R>>>>Одним словом, "извращённые" методы синхронизации рулят :super: A>>>Вот не думал, что в GPL-ом ядре есть патентованный код. Что за патенты? где про это можно почитать? Было бы очень интересно. C>>В твоих программах он тоже, скорее всего, есть... A>Чужие исходники в свои пректы включаю внимательно прочитав лицензию и для верности спрашиваю у автора, если удается найти его координаты конечно, можно-ли его поделку использовать. Это конечно не 100% гарантия, что все чисто, но хоть что-то.
В этом-то и проблема патентов. Ты понятия не имеешь, нарушил ли один из них.
К примеру, вкладки как элемент UI запатентованы Adobe. У твоих продуктов есть GUI?
... RO>К примеру, вкладки как элемент UI запатентованы Adobe. У твоих продуктов есть GUI?
Даже если и есть, что стого? Я код, который вкладками управляет к себе в проект не втягивал, пользуюсь публичным API.
Движки для растеризации шрифтов тоже поди все патентами закрыты. Что ж теперь, текст выводить нельзя без страха получить иск от патентообладателя?
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, remark, Вы писали:
R>>Плюс к этому, если дети запатентуют како-либо алгоритм на территории России, то большинство людей в мире просто глубоко положет на это.
E>Ну так и что? Патентуешь (в США) идеи и алгоритмы, а кто переиспользует код, на этих алгоритмах базирующийся -- тот нарушит твой патент и будет ему а-та-та от слепой амерской Фемиды. А то что он остался, в соответсвии с лицензией GPL пофиг
Ничего ему не будет. Американское патентное законодательство защищает права патентодержателей от производства/распространения и т.д. *на территории США*, и от ввоза реализаций *на территорию США*. Это не глобальные вещи, они действуют только на территории конкретной страны. В отличие от лицензий на исходный код.
E>p. s. E>А разве в РФ алгоритмы можно патентовать?
Можно. Патентуются скорее на алгоритмы, а реализации алгоритмов.
Здравствуйте, alsemm, Вы писали:
A>... RO>>К примеру, вкладки как элемент UI запатентованы Adobe. У твоих продуктов есть GUI? A>Даже если и есть, что стого? Я код, который вкладками управляет к себе в проект не втягивал, пользуюсь публичным API. A>Движки для растеризации шрифтов тоже поди все патентами закрыты. Что ж теперь, текст выводить нельзя без страха получить иск от патентообладателя?
Публичное АПИ тут не при чём. Допустим ты можешь скачать бинарники какой-то свободно распространяемой библиотеки, включить их в свой дистрибутив, и, я думаю, ввоз твоего дистрибутива на территорию страны Х всё-равно может нарушать патентное законодательство страны Х.
Если движки для растеризации шрифтов распространяет держатель патента (либо с его разрешения), то всё нормально. В конце-концов, он для этого патент и получал, что б продавать библиотеки. А вот если это кто-то левый реализовал эти алгоритмы, то уже не важно, что это за библиотека, под какой лицензией, откуда она у тебя, или у тебя только бинарники, или ты сам честно реализовал не зная о патенте...
... R>Публичное АПИ тут не при чём. Допустим ты можешь скачать бинарники какой-то свободно распространяемой библиотеки, включить их в свой дистрибутив, и, я думаю, ввоз твоего дистрибутива на территорию страны Х всё-равно может нарушать патентное законодательство страны Х.
R>Если движки для растеризации шрифтов распространяет держатель патента (либо с его разрешения), то всё нормально. В конце-концов, он для этого патент и получал, что б продавать библиотеки. А вот если это кто-то левый реализовал эти алгоритмы, то уже не важно, что это за библиотека, под какой лицензией, откуда она у тебя, или у тебя только бинарники, или ты сам честно реализовал не зная о патенте...
Чем
Допустим ты можешь скачать бинарники какой-то свободно распространяемой библиотеки
отличается от
Если движки для растеризации шрифтов распространяет держатель патента (либо с его разрешения)
? Я честно (не важно за деньги или за так, если автор не против) получил бинарник. Использую его возможности только через задокументиованный api. Включил в свой дистрибутив. С какого перепугу держатель патента будет иметь ко мне претензии?
Здравствуйте, alsemm, Вы писали:
A>... R>>Публичное АПИ тут не при чём. Допустим ты можешь скачать бинарники какой-то свободно распространяемой библиотеки, включить их в свой дистрибутив, и, я думаю, ввоз твоего дистрибутива на территорию страны Х всё-равно может нарушать патентное законодательство страны Х.
R>>Если движки для растеризации шрифтов распространяет держатель патента (либо с его разрешения), то всё нормально. В конце-концов, он для этого патент и получал, что б продавать библиотеки. А вот если это кто-то левый реализовал эти алгоритмы, то уже не важно, что это за библиотека, под какой лицензией, откуда она у тебя, или у тебя только бинарники, или ты сам честно реализовал не зная о патенте... A>Чем A>
A>Допустим ты можешь скачать бинарники какой-то свободно распространяемой библиотеки
A>отличается от A>
A>Если движки для растеризации шрифтов распространяет держатель патента (либо с его разрешения)
A>? Я честно (не важно за деньги или за так, если автор не против) получил бинарник. Использую его возможности только через задокументиованный api. Включил в свой дистрибутив. С какого перепугу держатель патента будет иметь ко мне претензии?
Он будет иметь к тебе притензии, потомучто ты ввозишь в страну носитель с реализаций запантованного метода. Этого достаточно.