среда, 30 ноября 2011 г.

Для "почитать"

Что-то зачастил я с подобными постами - но ведь интересно же, "как это делают люди".

Richard Jones: A Million-user Comet Application with Mochiweb, Part 3.
Even so, I think this could be generalized in such a way that would allow you to use Erlang for all the interesting stuff, and have a C+libevent process act as a dumb connection-pool. With a bit more wrapper code and callbacks into Erlang, you’d hardly need to know this was going on - the C program could be run as a driver or a C-node, and an Erlang wrapper could give you a decent api built on top of libevent.

Urban Airship Blog: C500k in Action at Urban Airship.
... we pressed on with a Java + Pure NIO implementation.

вторник, 11 октября 2011 г.

Чтиво

$3M инвестиций в NGINX.
Deep C (and C++) (сегодня запостили на RSDN) - каждый слайд, что называется, "в яблочко".
Overload 105 (см. Picking Patterns for Parallel Programs). Asio samples == almost C++ actors based on task based (fork/join) parallelism.

пятница, 30 сентября 2011 г.

Весёленький год

Мда. Этот год, наверное, поставил рекорд по остроте новостей с IT-фронта:
1. Nokia отвернулась от MeeGo и ищет спасения под крылом MS WP7
2. HP – рыба гниет с головы
3. Intel, что вы там курите? (комментарии особенно хороши)

Очень жаль, и Qt (в т.ч. соотв. подразделение Nokia) и MeeGo.

суббота, 10 сентября 2011 г.

timer revisited

В выложенной недавно версии 0.3.5 реализован "канонический" вариант работы с таймером (ma::echo::server::session). Данный вариант принципиально отличается от предыдущего (!). В прошлом я намеренно сужал API asio::deadline_timer до Java-вского ScheduledExecutorService, дабы иметь в кармане решение и для Java. Однако, с asio::deadline_timer можно работать эффективнее.

Нужно заметить, что мой варинт отличается от того, что предложил автор Asio:
1. нет таких "подвывертов", как deadline_.expires_at(boost::posix_time::pos_infin);
2. нет явных обращений к now(): deadline_.expires_at() <= deadline_timer::traits_type::now().
3. все обыгрывается за счет двух булевых флагов - их можно было заменить одной переменной с тремя состояниями, но с флагами получилось читабельнее.

P.S. А для ScheduledExecutorService можно сделать все в точности так же и никакого "сужения" не нужно было вовсе 8)

понедельник, 5 сентября 2011 г.

echo_server revisited

Наконец-то закончен "канонический вариант" активного объекта - ma::echo::server::session и ma::echo::server::session_manager.

Единственное по-настоящему важное изменение – это введение явных раздельных state machine (КА) для внешней части активного объекта (proxy) и для внутренней части (implementation). Так же явно введены КА для каждого вида internal activities. При этом код стал визуально чище и проще (на днях заглянул в код Qt - бр-р-р).

Удалось обойтись без Boost.MSM. Пока я не вижу, чем могла бы помочь данная библиотека, т.к. налицо несколько взаимосвязанных КА, либо какой-то combinatorial FSM.

P.S. Из-за загруженности на основной работе впопыхах пропустил пару глупых ошибок как в коде/логике, так и в комментариях – поэтому текущая версия уже 0.3.5.

Золотые слова

Цитата из этой статьи с Хабра:
На самом деле, принятие гибридного подхода к параллелизма, кажется, является движением вперед, если нет каких-либо противопоказаний. Ученые в области компьютерных наук из университета штата Пенсильвания обнаружили, что сочетание потоков и событий предлагает лучшее из обоих миров. Команда Scala в EPFL утверждает, что Actors объединяют программирование на основе потоков выполнения и программирование на основе событий в одну аккуратную, простую для понимания, абстракцию. Russ Cox, бывший сотрудник Bell Labs, теперь занятый проектом языка программирования Go в Google, заходит ещё дальше, утверждая, что бессмысленна сама дискуссия „потоки против событий“ (обратите внимание, что все это даже не затрагивает аспект распределения масштабирования системы; потоки — это конструкции для одного компьютера, и события, — конструкции для одного процессора; мы даже не говорим о распределении работы между машинами в простой манере; кстати, это включено в Erlang, и о нём стоит задуматься, если вы няньчите быстро растущую систему).
Именно к active object/actor (на уровне, максимально облегченном в плане прослоек) я и стремлюсь. Кто-нибудь может сравнить echo_server с Erlang-style в плане параллелизации? И что это за проект на китайском?

воскресенье, 24 июля 2011 г.

Re: The last of the Boostcon videos

Наконец-то я полностью посмотрел последний доклад Christopher Kohlhoff Why C++0x is the Awesomest Language for Network Programming на BoostCon 2011. До этого я лишь бегло пробежался по нему и сделал вывод, что ничего нового там для меня нет.

И действительно, все, что прозвучало в докладе, давно есть в блоге автора и в Boost.Asio 1.5.3 (а теперь и в Boost 1.47). Подумать только, stackless coroutines были анонсированы (dev версия, так и не вошедшая в Boost.Asio) еще в 2009 году, а о поддержке move semantic для completion handlers я просил еще с момента прочтения статьи Onward, Forward! Dave Abrahams (и реализовал ее в asio samples раньше, чем она официально появилась в Boost.Asio). Но как же здорово, что есть возможность почти вживую послушать автора и его комментарии к исходному коду (а заодно и оценить юмор как самого автора, так и его слушателей). Спасибо, Marshall Clow. Появились бы эти (известные всем подписчикам рассылок по Boost) видеозаписи докладов, если бы не он?

Что ж, BoostCon 2011 закончен. Сенсаций от автора Asio не прозвучало. Да и нужны ли они на таком мероприятии? Скорее "нет", чем "да". Судя по тем немногочисленным вопросам из зала, рассказать слушателям про возможности Boost.Asio было все же важнее. Теперь ждем BoostCon 2012 и надеемся вновь увидеть автора Asio. Учитывая объем изменений, вошедших в Boost 1.47, можно сказать, что Boost.Asio активно развивается (ну или точно не стоит на месте). Значит, Крису будет о чем рассказать нам в следующий раз. Может стоит предложить ему несколько тем? Я мог бы предложить рассказать подробнее о custom memory allocation и соответствующих hook-функциях для привязки к handler. Или раскрыть тему asio_handler_invoke. А Вы?

четверг, 7 июля 2011 г.

Client-side Boost.Asio: быть или не быть? ;)

Предлагаю читателям обсудить в комментариях к этому сообщению следующую тему: стоит ли использовать Boost.Asio на стороне клиента. С серверами все понятно - там вменяемой альтернативы (на C++) нет. А вот с клиентами как?

Для "затравки" мое мнение: то, что клиенты до сих пор не используют асинхронный ввод/вывод - "дело времени" и связано с привычкой и ленью.
Да, можно выделить весь синхронный ввод-вывод в отдельный поток (thread). Но (!) как Вы собираетесь передавать данные в этот поток и получать данные из него? Конечно же через очереди. Ага, получаем как раз тот вариант (io_service + io_service::strand), что описан в проекте nmea_client. Т.е. без Asio это будет просто очередной "велосипед".

Я могу ошибаться. Мне очень интересны (контр)аргументы противоположной стороны. Буду рад их услышать и сделать какие-то выводы для себя.

пятница, 24 июня 2011 г.

Utilizing ConnectEx with asio::windows::overlapped_ptr

Наверное многие Windows-программисты чесали репу смущались, когда узнавали, что Asio реализует асинхронный socket::async_connect при помощи select и дополнительного скрытого/внутреннего потока. Видимо, все дело в поддержке Windows 2000 - функция ConnectEx (судя по MSDN) появляется только в Windows XP.

Почему-то сегодня утром я вспомнил про интересный пример с использованием произвольной IOCP-related WinAPI функции совместно с Asio. Вспомнил и про многострадальный ConnectEx (до сегодняшнего утра ConnectEx использовать не приходилось - занимался только серверами и никогда не занимался клиентами).

Так вот - скрестить asio::windows::overlapped_ptr и ConnectEx оказалось очень просто - 2 часа и первый вариант готов.

Всех интересующихся прошу в SVN на sf.net. Самая интересная часть кода - очередной copy/paste из Asio examples (transmit_file.cpp).

Viva Asio!

Updated: проект async_connect уже включен в новый downloadable пакет.

среда, 15 июня 2011 г.

English detected

Судя по статистике, предоставляемой blogger.com (да, я посматриваю на нее), некоторые сообщения этого блога читаются зарубежными пользователями - и даже (судя по ссылкам на гугло-переводчик) не русскоговорящими.
Уважаемые читатели, я с удовольствием отвечу на comments in English (feel free to write comments/questions in English). Единственная причина, по которой данный блог пока не имеет англоязычного двойника - это нехватка времени и сомнения в необходимости/интересе со стороны читателей.

понедельник, 13 июня 2011 г.

Design Journeys with Asio

Предлагаю читателям этого блога ответить на довольно простой вопрос:

зачем в требованиях Asio к функтору-обработчику существует asio_handler_invoke?

Ответы/догадки оформлять в виде комментариев. Желательно написать что-то свое, а не просто привести ссылку на документацию Asio - в ней нет краткого обобщенного ответа.

Updated:
Я неправильно выразил вопрос. Кхм... скажем по-проще: как Вы думаете, зачем автор Asio усложнил дизайн, введя asio_handler_invoke (будем называть это invocation strategy)? Что потребовало от него именно такого похода?

Вопрос отчасти связан с тем, что многие считают Asio слишком сложной библиотекой. Этот вопрос направлен на то, чтобы объяснить, что в Asio нет ничего "случайного". Все имеет свою вескую причину.

Жаль, что на BoostCon 2011 Chris не сделал доклада с заголовком, похожим на заголовок данного сообщения.

Updated:
Когда я только начинал использовать Boost.Asio, наличие invocation strategy меня несколько смутило: что именно в моем MyHandler::operator() относится к invocation strategy?

И вот, когда я снова перечитал документацию Boost.Asio и нашел пункт X, я понял:
  1. какая часть MyHandler::operator() относилась к invocation strategy;
  2. для чего вообще было введено понятие invocation strategy.
Позже я понял, что invocation strategy лучше вынести из MyHandler::operator(), т.е. оставить ее только в соотв. asio_handler_invoke. В противном случае надо быть готовым к рекурсивному использованию invocation strategy. Например, вместо boost::mutex использовать boost::recursive_mutex (см. MA_BOOST_ASIO_HEAVY_STRAND_WRAPPED_HANDLER в asio-samples).

Так вот: ответ (и сопутствующее обсуждение) на вопрос, заданный мной в данном сообщении, дополнительно должен помочь разработчикам, использующим Boost.Asio, понять - какую часть MyHandler::operator() следует выносить в asio_handler_invoke и как выносить данный код - оставляя его же в MyHandler::operator() или нет?

Updated:
Как показало время, этот вопрос оказался либо неинтересным, либо слишком сложным.

Ну что ж, вот правильный ответ: Strands: Use Threads Without Explicit Locking.

Читаем: "if a completion handler goes through a strand, then all intermediate handlers should also go through the same strand. This is needed to ensure thread safe access for any objects that are shared between the caller and the composed operation (in the case of async_read() it's the socket, which the caller can close() to cancel the operation). This is done by having hook functions for all intermediate handlers which forward the calls to the customisable hook associated with the final handler".

Объясняю проще: единственная причина введения asio_handler_invoke - это правильная работа composed operations в свете синхронизации доступа к IoObject. Только такой hook и мог помочь в данном случае. Все остальные места в нем особо не нуждались, так как они так или иначе всегда заканчиваются вызовом Handler::operator() без необходимости повторного доступа к IoObject (могу ошибаться насчет поддержки SSL - еще не разбирался с ней).

пятница, 10 июня 2011 г.

Не пропадать же зря...

На днях просматривал forum.vingrad.ru.

Вспомнил, что в свое время я оставил один развернутый комментарий. "Развернутый" специально для дальнейшего использования в моих новых сообщениях/статьях о Boost.Asio.

Статей до сих пор нет, а тема "горячая" (ёпрст, многие, кто использует Asio, до сих пор не знают этого. Я бы вообще не стал использовать Asio, не будь в нем такой возможности, так как в этом случае raw-C-written-WinSock-based сервер давал бы ощутимое превосходство в скорости) - так что приведу выдержку из того комментария здесь:

Могу только пояснить следующее: как любая система построенная по шаблону Proactor (да и вообще - как все многопоточные системы, базирующиеся на очередях - т.е. почти все более-менее "продвинутые" многопоточные системы, например, Intel TBB), asio активно использует очереди (в случае asio - это очереди из функциональных объектов).

В связи с этим остро встает вопрос выделения памяти в таких очередях (особенно остро в свете многопоточности и кешей процессора). Intel TBB решает это своим "прокаченным-специализированным" аллокатором (и обещаниями повторно использовать выделяемые блоки).

asio использует очень легкое и гибкое решение (и это не единственный случай!): позволяет использовать allocator per async operation (начиная с dev release 1.53 - во всех асинхронных операциях). И вот какая штука: в любом вменяемом (т.е. не "hello world") приложении количество одновременных однотипных асинхронных операций над одним и тем же объектом (например, socket или deadline_timer) всегда ограничено сверху. И зачастую это ограничение равно единице (Вы уже на него нарвались недавно).

Исходя из всего вышесказанного, зачастую, Вы можете просто заранее выделить непрерывный блок памяти, который будет удерживаться Вашим handler-ом (например, при помощи shared_ptr). Затем, каждый раз когда asio будет нужна память для чего-то временного (обычно, это память для расположения в очереди - IOCP или boost::asio::io_service::strand или даже deadline_timer), что связанно с асинхронной операцией, для которой Ваш handler является completion handler-ом - asio просто попросит об этом через Ваш же handler. При этом те гарантии что дает asio относительно custom memory allocation, гарантируют, что (при достаточном размере Вашего пред-аллоцированного блока памяти) Вы всегда сможете выделить эту память из того самого, ранее выделенного, блока памяти.

Кроме того, гарантии asio позволяют удерживать этот "пред-аллоцированный блок памяти" самим же handler-ом (с небольшим ограничением на handler - см. доки по Boost.Asio 1.53, почти всегда это ограничение выполняется и уж точно всегда его можно легко обойти). Главное, хранить в handler-е не сам блок, а указатель на него (лучше -smart) - чтобы в блок помещался сам handler + что-то временное.

В моем проекте (http://sourceforge.net/projects/asio-samples) echo_server (и qt_echo_server, и, вообще, все "asio samples") вообще не выделяет память в куче при своей непосредственной работе (кроме приема новых входящих TCP-соединений, да и тут есть reuse). Custom memory allocation сводит все выделения памяти к тому коду, что я указал - т.е. никаких блокировок и даже никаких атомарных (interlocked) операций. Плюс, в теории (не могу до сих пор проверить) - это должно очень положительно сказаться на использовании CPU-кеша (Updated: и на branch prediction) (+ учтите очень правильную реализацию strand-ов в asio).

В дополнение:
  • см. ma/context_alloc_handler.hpp, ma/custom_alloc_handler.hpp и ma/handler_allocator.hpp (это практически copy-paste из примеров Chris-а и из private/details-"внутренностей" Boost.Asio, так что все лавры направляйте в сторону автора Asio).
  • см. документацию по Asio Custom Memory Allocation.

P.S. Комментарии и даже "холивар" приветствуются.

понедельник, 30 мая 2011 г.

Неудачное сочинение

Вот тут лежит неудачная статья для Хабра.

Неудачная, потом что:
1) какая-то "скомканная" получилась - особенно конец.
2) дальше песочницы не ушла.

Чтож, после доработки проекта echo_server возьмусь за нее с новыми силами. Писать документацию/описание для меня всегда было мучением.

Если кто-то (русскоговорящий) заинтересуется asio samples, то краткое пояснение он уже сможет почерпнуть по указанной выше ссылке.

среда, 13 апреля 2011 г.

Пример работы с таймером (реализация таймаута)

Сегодня в проекты "echo_server" и "qt_echo_server" (т.к. у них общая кодовая база касаемо самого сервера) введена возможность указывать для сессий (на стороне сервера, конечно) таймаут чтения, по истечении которого сессия считается неактивной (по вине клиента - мол "упал он там что ли?").

При этом оставлена возможность не задавать величину подобного таймаута. В этом случае таймаут не будет использоваться вообще (это похоже на таймаут, равный +∞).

Отличительная возможность реализации таймаута - это использование custom memory allocation для обработчика, используемого совместно с deadline_timer-ом. Эта "фишка" потребовала особого дизайна (правда, не столь уж и "мудреного") и показала то, о чем не сказано ни в одном примере Boost.Asio. - см. комментарии - там есть ссылка на пост автора Asio, где очень подробно описано более простое (и понятное) решение. Кстати, это решение уже есть в офиц. документации Asio.

Код пока только в SVN-репозитории (trunk).
После проверки всеми компиляторами появится dev-release.

Updated 14.09.2011: см. улучшенный/исправленный вариант.

пятница, 1 апреля 2011 г.

Qt Echo Server released

Всем, кому интересно, как связать многопоточный код с functor-based callbacks (например, Boost.Asio) и Qt signal/slot, смотреть проект qt_echo_server.
Решение простое и не использует никаких "хаков" - все по официальной документации Qt.
И так, Boost.Asio (+ Boost.Bind + Boost.Thread) + Qt (4.x) = qt_echo_server

среда, 23 марта 2011 г.

Boostcon 2011

Посмотрел программу boostcon 2011.

Впечатлило, что наконец-то появились темы по Boost.Asio - да еще какие!
Только от автора Boost.Asio Christopher Kohlhoff сразу несколько "вкусностей":
  • Thinking Asynchronously: Designing Applications with Boost.Asio;
  • Why C++0x is the Awesomest Language for Network Programming.

А еще:
  • Cliff Green: Design Journeys with Skoot, a Peer Oriented Generic Networking Library
  • Boris Schaeling: Creating Boost.Asio extensions

И наконец "просто отличные" темы:
  • Joel Falcou, Khaled Hamidouche: Automatic Hybrid MPI+OpenMP Code Generation
  • Justin Gottschlich: Intel's C++ Software Transactional Memory Compiler and the Draft Specification of Transactional Language Constructs for C++
  • Joel Falcou, Lionel Lacassagne, Sebastian Schaetz: Mastering the Cell Broadband Engine architecture through a Boost based parallel communication library
  • Hans Boehm: Threads and Shared Variables in C++0x
Итак, ждем мая.

вторник, 22 марта 2011 г.

WTF: Adobe or Microsoft?

Сегодня обновил Adobe Flash for IE.
Т.е. сначала удалил предыдущую версию, потом перезагрузился, потом установил новую версию и снова перезагрузился.

Вуаля - на большинстве сайтов в IE9 перестали работать вставки с видео 8(
Не впервой проблемы с IE. Youtube в IE9 работает, все неработающие в IE9 видео-вставки, отлично показываются в FF4. Что ж, посмотрел исходный код проблемных сайтов - выявил закономерность: везде ссылка на объект идет не через GUID, а через MIME (application/x-shockwave-flash).
Ага! Запускаем regedit и ищем текст application/x-shockwave-flash.
И вот оно - найден раздел на чтение которого у меня нет прав! Да, я работаю под ограниченной учетной записью. Но чтение-то должно быть доступно! Сравнил права с аналогичными разделами (например, для Silverlight) - действительно - права не те.

Решение: запускаем regedit от имени администратора, удаляем раздел (где-то в HKLM\...MIME\Database\... - искать по application/x-shockwave-flash), переустанавливаем Adobe Flash ActiveX.

Кто виноват? Думаю, кривой инсталлятор от Adobe. Хотя вполне возможно (это же ActiveX), что проблема кроется в регистраторе ActiveX, т.е. "виновата" Microsoft. В общем, FF4 я уже поставил. И так на многих сайтах сижу только через него. Похоже вскоре полностью пересяду на "лису".

Спасибо, Christopher Kohlhoff!

УРРА! (второй раз за день)
Вышедший сегодня Boost.Asio 1.5.3 (dev-ветка) обновил поддержку SSL - теперь асинхронные SSL-операции поддерживают asio custom memory allocation.

Я как раз собирался на днях попробовать изменить существующую реализацию, для поддержки asio custom memory allocation - уже отчаялся и решил, что это неинтересно не только пользователям Boost.Asio (что-то вяло они используют этот поистине великолепный отличительный механизм Boost.Asio), но и самому автору.

И тут такой подарок... Спешу "порыться" в коде - все дружно ищем тяжелые глобальные lock-и и недостаток поддержки move semantic.

async_basics2

Наконец-то! Свершилось! :)

Представлен новый проект "async_basics2", который реализует все тоже самое, что и проект "async_basics", но почти без шаблонов (т.е. совсем без шаблонов в публичной части).

Я давно планировал свести работу с Boost.Asio к традиционным вариантам с интерфейсами и реализацией (неудачные "echo_server2" и "echo_server3").

Теперь (проект "async_basics2") completion callback - это интерфейс (без шаблонов!), async_object - тоже простой интерфейс, не использующий шаблонов. Это дает больше возможностей по разделению кода и несомненно поможет многим, кто по той или иной причине не может использовать шаблоны в публичной части активного объекта.
Поддержка asio custom memory allocation никуда не исчезла - см. код "async_basics2" (main.cpp и класс do_something_handler). Гарантии asio для completion handler и возможность в completion handler "удерживать" сам активный объект тоже "на месте" (это тоже отражено в "async_basics2").

P.S. Вoзможно скорое появление аналогичного проекта "echo_server2".

понедельник, 7 марта 2011 г.

Qt Echo Server introduced

"asio samples" пополнились еще одним проектом - "qt_echo_server".
На данный момент это отражено только в SVN-репозитории.
Поддерживается сборка при помощи MSVS 2010 и Qt Creator.

Несмотря на то, что проект еще не закончен (парсинг конфигурации, задаваемой в GUI), "qt_echo_server" уже собирается и работает в:
1) Windows (Windows XP/Vista: MSVS 2010 + Qt 4.7.0 static build with static C/C++ runtime; Windows 7: MS Visual C++ 2008 + Qt Creator 2.1.0 + Nokia Qt libraries 4.7.2 for Windows);
2) Ubuntu 10.10 (Qt Creator из станд. репозиториев + установленные согласно офиц. документации Boost C++ Libraries 1.46);
3) Kubuntu 10.10 (Qt Creator из станд. репозиториев + установленные согласно офиц. документации Boost C++ Libraries 1.46 + Boost.Asio 1.5.1).

Всем, кому приходило в голову соединять boost::bind или произвольные функторы C++ с "Qt signal/slot", будет весьма интересно то, как это получилось у меня - это является одним из вопросов, решением которых выступает данный "sample" ("qt_echo_server").

пятница, 4 марта 2011 г.

Move semantic supported

Теперь "asio samples" (в основном проект "echo_server" и менее "nmea_client") поддерживают C++0x move semantic (rvalue reference):
1) для тех компиляторов, что не поддерживают еще не законченный стандрат (в части rvalue reference и move semantic) - MS Visual C++ 2008 (9.0) - все остается по-прежнему (хвала "проклятому" препроцессору :)
2) для компиляторов с "достаточной" реализацией move semantic - MS Visual C++ 2010 (10.0), Intel C++ Compiler 12.x, GCC 4.5 - везде, где это допустимо используются rvalue references, std::move и, конечно же, std::forward (спасибо статьям Dave Abrahams в блоге C++Next.

Для Intel C++ Compiler 12.x в build/intel12/boost_1_46_patches/intel_rvalue_ref_on.patch содержится патч, который "включает" поддержку rvalue reference в Boost C++ Libraries 1.46 и, соответсвенно, в "asio samples" (используется boost-вский config.hpp).

Для GCC 4.5 ключ компиляции, ответственный за поддержку rvalue reference, включается непосредственно в *.pro-файлах (проекты для Qt Creator).
Для получения "реальной пользы" от поддержки move semantic необходимо установить Boost.Asio 1.5.1 (developer release) в Boost C++ Libraries 1.46 - только тогда "срабатывает" большинство move-constructor.

P.S. Ну и сами "asio samples", кончено же, лучше брать из SVN-репозитория (trunk).

Linux support included

Начиная с этой недели проект "asio samples" поддерживает Linux.
Если быть точным, то вся поддержка заключается в том, что проекты "echo_server" и "nmea_client" собираются, запускаются и работают (протестирован только "echo_server") на Kubuntu 10.10.

Сборка и отладка производятся при помощи Qt Creator - проекты для него расположены в build/qmake/echo_server/echo_server.pro и build/qmake/nmea_client/nmea_client.pro.

Для сборки указанных проектов необходим не только Qt Creator, но и установленные Boost C++ Libraries. В теории поддерживаются все Boost C++ Libraries 1.4x, но тестирование проводилось на отдельно поставленных (согласно офиц. документации Boost) Boost C++ Libraries 1.46.

среда, 12 января 2011 г.