Странный тупик со статической функцией или нет

У меня есть два дня, чтобы исследовать связанные с чтением-записью-блокировкой вещи.
И я столкнулся с проблемой из-за ограничения виртуального потока GCD: Dead Lock с ‘ dispatch_barrier`

Затем я пытаюсь использовать pthread_rwlock_tдля реализации rwlock. It’s running ok like the end of the article on.

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

Потом долго думаю и отлаживаю, нахожу странную вещь:
Когда я перемещаю init-код (также содержащий dispatch_once) из staticфункции. Все в порядке. это так странно, и я думаю, что это не из-за ограничения виртуального потока GCD. @originaluser2

#import <pthread.h>

#define THREAD_ASSERT_ON_ERROR(x_) do { 
_Pragma("clang diagnostic push"); 
_Pragma("clang diagnostic ignored "-Wunused-variable""); 
volatile int res = (x_); 
assert(res == 0); 
_Pragma("clang diagnostic pop"); 
} while (0)

static pthread_rwlock_t kRWLock(){
    static pthread_rwlock_t _rwlock;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        THREAD_ASSERT_ON_ERROR(pthread_rwlock_init(&_rwlock, NULL));
    });
    return _rwlock;
}

//#define WILLDEADLOCK  //define it will see the deadlock demo

- (void)test
{
    dispatch_queue_t queue =  dispatch_queue_create("com.test.testasync", DISPATCH_QUEUE_CONCURRENT);
    for (NSInteger i=0; i<5000; i++) {
        dispatch_async(queue, ^{
#ifdef WILLDEADLOCK
            pthread_rwlock_t rwlock = kRWLock();
#else
            static pthread_rwlock_t rwlock;
            static dispatch_once_t onceToken;
            dispatch_once(&onceToken, ^{
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_init(&rwlock, NULL));
            });
#endif
            THREAD_ASSERT_ON_ERROR(pthread_rwlock_rdlock(&rwlock));
            NSLog(@"rlock1");
            NSLog(@"runlock1");
            THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(&rwlock));

            if (i%100==0) {
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_wrlock(&rwlock));
                NSLog(@"wlock1");
                NSLog(@"wunlock1");
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(&rwlock));
            }
        });
    }

    dispatch_barrier_sync(queue, ^{});
    NSLog(@"completed");
}

1 ответ

  1. Примечание: автору этого вопроса не разрешили опубликовать свой ответ в качестве ответа, поэтому я сделал это за него.


    Это глупый вопрос. Я забыл pthread_rwlock_tis a structи он будет скопирован в новую память после return.

    Правильный код ниже:

    #import <pthread.h>
    
    #define THREAD_ASSERT_ON_ERROR(x_) do { \
    _Pragma("clang diagnostic push"); \
    _Pragma("clang diagnostic ignored \"-Wunused-variable\""); \
    volatile int res = (x_); \
    assert(res == 0); \
    _Pragma("clang diagnostic pop"); \
    } while (0)
    
    static pthread_rwlock_t *kRWLock(){
        static pthread_rwlock_t _rwlock;
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            THREAD_ASSERT_ON_ERROR(pthread_rwlock_init(&_rwlock, NULL));
        });
        return &_rwlock;
    }
    
    - (void)test
    {
        dispatch_queue_t queue =  dispatch_queue_create("com.test.testasync", DISPATCH_QUEUE_CONCURRENT);
        for (NSInteger i=0; i<5000; i++) {
            dispatch_async(queue, ^{
                pthread_rwlock_t *rwlock = kRWLock();
    
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_rdlock(rwlock));
                NSLog(@"rlock1");
                NSLog(@"runlock1");
                THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(rwlock));
    
                if (i%100==0) {
                    THREAD_ASSERT_ON_ERROR(pthread_rwlock_wrlock(rwlock));
                    NSLog(@"wlock1");
                    NSLog(@"wunlock1");
                    THREAD_ASSERT_ON_ERROR(pthread_rwlock_unlock(rwlock));
                }
            });
        }
    
        dispatch_barrier_sync(queue, ^{});
        NSLog(@"completed");
    }