Мне всегда было интересно, как написать следующий код, чтобы использовать его для модульного тестирования:
Можно ли расширить NSThread с помощью метода, который бы проверял, заблокирован ли какой-либо поток?
Сейчас я работаю с NSCondition: Xcode показывает мне цепочку, вызываемую -wait, чтобы заблокировать поток:
[NSCondition wait]
pthread_cond_wait$UNIX2003
_pthread_cond_wait
__psynch_cvwait
Помимо проверки блокировок, выполняемых NSCondition, если это даже возможно, я бы очень признателен за то, что метод работает и для любых других возможностей блокировки (отправка семафоров, блокировок условий, спаренных потоков и т.д.). Я понятия не имею о Objective-C, если возможно, они могут быть схвачены одним методом или каждый из них нуждается в своих собственных.
Вот простой пример того, чего я хотел бы достичь. Таинственный метод называется isBlocked.
// Some test case
// ...
__block NSThread *thread;
NSCondition *condition = [NSCondition alloc] init];
dispatch_async(someQueue(), ^{
thread = NSThread.currentThread;
[condition lock];
[condition wait];
[condition unlock];
});
while(1) {
NSLog(@"Thread is blocked: %d", thread.isBlocked);
}
Примечание: Я плохо разбираюсь в C и всех этих низкоуровневых POSIX-материалах, поэтому, пожалуйста, будьте подробными.
Примечание 2:. Я также заинтересован в решениях, работающих с диспетчерскими очередями: если кто-то может показать мне, как проверить факт, что someQueue() заблокирован - [NSCondition wait] (а не факт, что он будет заблокирован (fx взломает какой-то код раньше - [условие wait] запущено, и блок установлен), но тот факт, что поток/очередь заблокирован), я соглашусь с этим как ответ, как я бы сделал с работой - [NSThread isBlocked].
Примечание 3: Подозревая плохие новости, такие как "это невозможно", я утверждаю, что любые идеи о том, чтобы поймать факт, что - [условие ожидания], были выполнены, и поток был заблокирован (см. Примечание 2) оцениваются и могут быть также приняты в качестве ответа!
ОБНОВЛЕНИЕ 1 в адресе к приятному ответу Ричарда Дж. Росса III. К сожалению, его ответ не работает в моем оригинальном примере, версия, которая ближе к моей реальной работе (хотя она не сильно отличается от примера, который я изначально предоставил, извините, что я не включил его в первый выпуск вопрос):
// Example
// Here I've bootstrapped Richard isLocking categories for both NSThread and NSCondition
// ...
// somewhere in SenTesting test case...
__block NSThread *thread;
NSCondition *condition = [NSCondition alloc] init];
__block BOOL wePassedBlocking = NO;
dispatch_async(someQueue(), ^{
thread = NSThread.currentThread;
[condition lock];
[condition wait];
[condition unlock];
wePassedBlocking = YES; // (*) This line is occasionally never reached!
});
while(!thread.isWaitingOnCondition); // I want this loop to exit after the condition really locks someQueue() and _thread_ __.
// sleep(1);
[condition lock];
[condition broadcast]; // BUT SOMETIMES this line is called before -[condition wait] is called inside someQueue() so the entire test case becomes blocked!
[condition unlock];
while(!wePassedBlocking); // (*) And so this loop occasionally never ends!
Если я раскомментирую sleep (1), тест начинает работать очень стабильно без каких-либо случайных блокировок!
Это приводит нас к проблеме, что категория Ричарда устанавливает состояние ровно на одну строку до фактической блокировки, что означает, что основной поток иногда отслеживает это новое состояние прежде чем мы действительно заблокируем someQueue/thread, потому что код Richard не содержит механизмов синхронизации: @synchronized, NSLock или что-то в этом роде! Надеюсь, я даю ясное объяснение этому сложному делу. Для тех, кто сомневается в том, что я здесь разместил, я бы сказал, что я также экспериментировал с несколькими очередями и даже более сложными делами, и при необходимости я готов предоставить больше примеров. Ричард, еще раз спасибо за ваши усилия, подумайте больше, если вы поймете эти мои моменты!
ОБНОВЛЕНИЕ 2
Я вижу парадокс тупика: очевидно, чтобы действительно установить состояние waitingOnCondition, нам нужно обернуть это изменение состояния внутри некоторых закрытий синхронизации, но проблема в том, что закрытие, разблокировка блокировка синхронизации, должна вызываться после - [условие wait], но она не может, потому что поток уже заблокирован. Опять же, я надеюсь, что описываю это довольно ясно.