Hungry Mind , Blog about everything in IT - C#, Java, C++, .NET, Windows, WinAPI, ...

Эталон ТП за рулем

Поворот с ул. Набежено-Крещатицкой на ул Нижний Вал. Всегда найдутся уроды, поворачивающие со второго ряда направо, где только прямо; уроды, которые проезжают перекресток и сдают задом чтобы стать перед всеми. А иногда и вот такое хуйло попадается, но чаще мужского пола. А здесь - эталон ТП за рулем. ТП - тупая пизда, за рулем женщина.

Cruel InvokeRequired

Всем давно известно, что в WinForms начиная с версии 2 появилась защита от многопоточного использования элементов управления. При попытке выполнить опасные операции библиотечный код выбрасывает InvalidOperationException с текстом Cross-thread operation not valid: Control 'xxx' accessed from a thread other than the thread it was created on. Дальше я объясню как выполняется эта проверка и о некоторых подводных камнях этого механизма.

Свойство Handle класса Control имеет нетривиальную логику, часть которой содержит проверку на безопасность использования даного кода из другого потока:

[
Browsable(false), 
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
DispId(NativeMethods.ActiveX.DISPID_HWND), 
SRDescription(SR.ControlHandleDescr) 
]
public IntPtr Handle { 
   get {
       if (checkForIllegalCrossThreadCalls &&
           !inCrossThreadSafeCall &&
           InvokeRequired) { 
           throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,
                                                            Name)); 
       } 

       if (!IsHandleCreated) 
       {
           CreateHandle();
       }

       return HandleInternal;
   } 
}

Основная часть условия - свойство InvokeRequired:

[
Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), 
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), 
SRDescription(SR.ControlInvokeRequiredDescr)
] 
public bool InvokeRequired {
   get {

       using (new MultithreadSafeCallScope()) 
       {
           HandleRef hwnd; 
           if (IsHandleCreated) { 
               hwnd = new HandleRef(this, Handle);
           } 
           else {
               Control marshalingControl = FindMarshalingControl();

               if (!marshalingControl.IsHandleCreated) { 
                   return false;
               } 

               hwnd = new HandleRef(marshalingControl, marshalingControl.Handle);
           } 

           int pid;
           int hwndThread = SafeNativeMethods.GetWindowThreadProcessId(hwnd, out pid);
           int currentThread = SafeNativeMethods.GetCurrentThreadId(); 
           return(hwndThread != currentThread);
       } 
   } 
}

Что здесь происходит? В локальную переменную hwnd записывается дескриптор текущего окна (если оно создано), иначе - дескриптор первого созданного окна в иерархии child-parent (метод FindMarshalingControl). Если ни один родитель не создан (нет дескриптора), метод InvokeRequired возвращает false. Далее используются функции GetWindowThreadProcessId и GetCurrentThreadId чтобы определить принадлежность созданного окна текущему потому. ОС Windows запоминает идентификаторы потоков в контексте которых произошли вызовы CreateWindow для создания окон.

Из этого можно сделать следующие выводы:

  1. WinForms не изобретает колесо - лишь использует доступную информация для выполнения нужных проверок
  2. Если окно не было создано (нет дексриптора), а также не были созданы все его родители - InvokeRequired возвращает false, что весьма логично - объект CLR может быть создан в любом потоке, но получит привязку к конкретному потоку лишь после создания

В результате следующий код содержит потенциальную проблему:

void handleNotificationFromOtherThread(...)
{
   if (someControl.InvokeRequired)
   {
      someControl.BeginInvoke(handleNotificationFromOtherThread, ...);
   }
   // Thread safe code here
   ...
}

Если уведомления прийдут до того, как хоть одно окно из иерархии будет создано, InvokeRequired вернет false и код выполнится в неправильном контексте. И здесь даже механизм защиты WinForms не поможет. В результате получаем многопоточный доступ к ресурсам без блокировок.

Как избежать подобного сценария? Создавая окно специально для целей синхронизации доступа:

Control sync = new Control();
sync.CreateControl();
_syncInvoke = (ISynchronizeInvoke)sync;

...

void handleNotificationFromOtherThread(...)
{
   if (_syncInvoke.InvokeRequired)
   {
      _syncInvoke.BeginInvoke(handleNotificationFromOtherThread, ...);
   }
   // Thread safe code here
   ...
}

Код выше принудительно создает окно в нужном контексте и использует его интерфейс ISynchronizeInvoke.

Но есть и более изящное решение - сохранить System.Threading.SynchronizationContext.Current как член класса и использовать для синхронизации. WinForms сам создаст по одному окну специально для маршаллинга вызовов в каждом потоке. Этот подход лучше всего подходит для простых сценариев, когда нужно отмаршаллить все выховы в один главный поток.

iPhone 4s jailbreak

Пока что я жмотюсь платить по 10+$ за нужные мне программы. Это не долбанные игрушки по 99 центов, а необходимые в жизни вещи типа Wallet и iGO. Ну, просто психологически не готов вывалить даже 10 у.е. за программу, которая просто хранит текст в шифрованном виде. А 100 у.е. за навигацию - перебор. Но мучения мои скоро закончатся, грядет A5 jailbreak.

iPhone 4s case

Решил, что чехол для роботизированной сучки нужен. Прошлый телефон изрядно пострадал из-за моего обращения с ним. Бросал в рюкзак, карманы, где ключи лежали и прочая чепуха. В результате на экране куча царапин и все такое. Бамперы и прочие подобные вещи считаю жлобством, поэтому выбор пал на мешочек (pouch по-буржуйски). Выбор пал на Sena SARACH ULTRASLIM White-Red.

Стоимость там - 40$. У нас - 60$. Заказал, привезли. На радостях отдал деньги курьеру, а лишь после заметил, что швы с одной стороны сильно перетянуты и в результате два слоя кожи не состыкованы, а слеплены. Ну, похоже на результат удара двух тектонических плит.

На упаковке - made in Turkey. Сука, как это все надоело! Кусок кожи стоимостью 60 у.е. сшит тяп-ляп в Турции. Вовремя позвонил курьеру и забрал обратно деньги, заебали.

Вывод? Просить чтобы привозили несколько экземпляров на выбор или не покупать вещи по маразматичным ценам.

iPhone 4s and SIRI

Получил я наконец свой iPhone 4S White 32Gb. Первое впечатление - говно. Но после пары часов использования, подключения к iCould и пр. - я понял, что сделано для людей. Очень удобный телефон.

  • На фотках белый выглядит гораздо лучше, чем в жизни. Черный на мой вкус смотрится более шикарно. Наверное из-за отражающего эффекта покрытия.
  • SIRI требует подключение к интернету и жрет трафик. Падла отсылает сжатый звук на сервер Apple, где происходит его анализ и генерация результатов. Транслитерацию не поддерживает, поэтому контакты должны использовать английский алфавит. С СМС-ками та же проблема.
  • После подключения симки происходило 2-3 пропадания сети Киевстар, что заставило злиться и спровоцировало поток матов. Симка то испорчена. Но после все образовалось. Не знаю почему. Возможно из-за отключения авто поиска сети и установки свежей прошивки. Перед этим я начитался о множестве проблем с пропаданием сети оператора Киевстар.
  • Батарею не сжирает. За ночь простоя ушло 3%. Если не пользоваться активно (как задрот или жлоб в метро)- жить будет 3-4 дня, полагаю. Геолокационные сервисы включены ВСЕ.
  • Камера понравилась. Хоть и сравнить не с чем, я никогда не пользовался телефоном как фотоаппаратом.
  • Работает шустро, тормоза не замечены нигде.

.NET Framework 4 Platform Update 2

А вы в курсе, что появился уже .NET Framework 4 Platform Update 2? Многие, наверное, не знают даже, что уже полгода как у многих установлен .NET Framework 4 Platform Update 1.

"Unsupported" C++ namespace names

Замечали такие вот интересные идентификаторы пространств имен: Win32Hosts.dll!<CrtImplementationDetails>::LanguageSupport::Initialize()? А вот как это делается:

namespace __identifier("<CrtImplementationDetails>")
{
...
}
Copyright 2007-2011 Chabster