Правильная утилизация канала ввода-вывода Grand Central - PullRequest
3 голосов
/ 04 марта 2012

Я не совсем понимаю, как правильно утилизировать канал ввода-вывода Grand Central, как только я закончу с ним. Следующий (упрощенный) пример вызывает сбой в какой-либо частной очереди отправки с сообщением: BUG IN CLIENT OF LIBDISPATCH: Over-resume of an object:

- (void)beginReading {
    dispatch_io_t channel;
    channel = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, 
                                           "/Path/To/Some/File", 
                                           O_RDONLY, 
                                           0 /*mode*/,
                                           someQueue,
                                           ^(int errorCode) {
                                               // Cleanup handler; executed once channel is closed. 
                                               // (Or fails to open.)
                                           });
    // Schedule the read operation
    dispatch_io_read(channel, 0, SIZE_MAX, someQueue, ^(bool done, dispatch_data_t data, int errorCode) {
        NSError *error = (errorCode!=0) ? [NSError errorWithDomain:NSPOSIXErrorDomain code:errorCode userInfo:nil] : nil;
        [self didReadChunk:data isEOF:done error:error];
    });

    // No more read operations to come, so we can safely close the channel.
    // (Or can we?)
    dispatch_io_close(channel, 0); 

    // We don't need a reference to the channel anymore
    dispatch_release(channel); 
}

Я предполагаю, что dispatch_io_close() планирует некоторую асинхронную операцию для закрытия канала, и до тех пор, пока эта операция не завершится, вы не должны вызывать dispatch_release() на канале, иначе произойдет что-то плохое. Но это было бы довольно удивительно: другие асинхронные функции GCD, такие как dispatch_async(), не имеют этого ограничения. Кроме того, вызов dispatch_io_close() не является строго необходимым, поскольку libdispatch, по-видимому, закрывает файл с последним вызовом dispatch_release() на канале.

Из этого следует, что, если вы вызываете dispatch_io_close, вы должны позаботиться о том, чтобы не освобождать канал, пока не запустится его обработчик очистки. Это так раздражает, что мне интересно, если это ошибка. Или, может быть, я что-то упустил?

Ответы [ 2 ]

4 голосов
/ 06 марта 2012

Оказывается, это ошибка (радар № 10246694). Дальнейшие эксперименты, по-видимому, указывают на то, что они влияют только на каналы диспетчеризации на основе пути, т. Е. Каналы, созданные с dispatch_io_create_with_path(), в отличие от dispatch_io_create().

0 голосов
/ 17 апреля 2018

Объявите эти переменные, глобально:

dispatch_fd_t write_dfd, read_dfd;
dispatch_io_t write_channel, read_channel;

Чтобы открыть канал для записи текстуры металла в файл:

NSString *documentDictionary = [(NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES)) objectAtIndex:0];
    
        write_dfd = open([[NSString stringWithFormat:@"%@/textures.data", documentDictionary] UTF8String], O_CREAT | O_TRUNC | O_WRONLY | O_APPEND);
        write_channel = dispatch_io_create(DISPATCH_IO_STREAM, write_dfd, AppServices.textureQueue, ^(int error) {
            NSLog(@"Write channel to %@ open", documentDictionary);
        });
        
        dispatch_io_set_low_water(write_channel, 33177608);
        dispatch_io_set_high_water(write_channel, 33177608);

Кзапишите текстуру металла в файл:

void *write_buffer = malloc([_textureBGRA allocatedSize]);
    [_textureBGRA getBytes:write_buffer bytesPerRow:15360 fromRegion:MTLRegionMake2D(0.0, 0.0, [_textureBGRA width], [_textureBGRA height]) mipmapLevel:0];
    dispatch_data_t message_data = dispatch_data_create(write_buffer, [_textureBGRA allocatedSize], AppServices.textureQueue, ^{
        free((void *)write_buffer);
    });
    dispatch_io_write(write_channel, 0, message_data, AppServices.textureQueue, ^(bool done, dispatch_data_t  _Nullable data, int error) {
        if (done)
        {
            NSLog(@"Wrote %lu bytes",  [_textureBGRA allocatedSize]);
        }
    });

Чтобы закрыть файл:

dispatch_io_close(write_channel, 0);
close(write_dfd);

Чтобы открыть канал для чтенияТекстура металла из файла:

NSString *documentDictionary = [(NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)) objectAtIndex:0];
        
        read_dfd = open([[NSString stringWithFormat:@"%@/textures.data", documentDictionary] UTF8String], O_RDONLY | O_NONBLOCK);
        read_channel = dispatch_io_create(DISPATCH_IO_STREAM, read_dfd, dispatch_get_main_queue(), ^(int error) {
            NSLog(@"Read channel to %@ open", documentDictionary);
        });
        
        dispatch_io_set_low_water(read_channel, 33177608);
        dispatch_io_set_high_water(read_channel, 33177608);

Чтобы прочитать текстуру металла из файла:

dispatch_io_read(read_channel, [_textureBGRA allocatedSize] * textureIndex, [_textureBGRA allocatedSize], dispatch_get_main_queue(), ^(bool done, dispatch_data_t  _Nullable data, int error) {
                if ( read_channel == nil )
                    return;
                
                if ( error != 0 ) {
                    __autoreleasing NSError * nsError = nil;
                    nsError = [[NSError alloc] initWithDomain:NSPOSIXErrorDomain code:error userInfo:nil];
                    NSLog(@"Error reading from channel: %@", nsError.localizedDescription);
                    return;
                }
                
                // read data
                void *buffer = malloc([_textureBGRA allocatedSize]);
                dispatch_data_t mapped_data = dispatch_data_create_map(data, buffer, NULL);
                dispatch_data_apply(mapped_data, ^bool(dispatch_data_t region, size_t offset, const void *buffer, size_t size) {
                    NSUInteger rowBytes = 3840 * 4;
                    NSUInteger imageBytes = rowBytes * 2160;
                    MTLTextureDescriptor *textureDescriptor = [MTLTextureDescriptor texture2DDescriptorWithPixelFormat:MTLPixelFormatRGBA8Unorm
                                                                                                                 width:3840
                                                                                                                height:2160
                                                                                                             mipmapped:NO];
                    textureDescriptor.usage = MTLTextureUsageShaderRead;
                    id<MTLTexture> texture = [_device newTextureWithDescriptor:textureDescriptor];
                    [texture replaceRegion:MTLRegionMake2D(0, 0, 3840, 2160)
                               mipmapLevel:0 slice:0 withBytes:buffer
                               bytesPerRow:rowBytes bytesPerImage:imageBytes];
                    dispatch_async(dispatch_get_main_queue(), ^{
                        NSString *label = [NSString stringWithFormat:@"%lu", textureIndex];
                        const char *queue_index = [[NSString stringWithFormat:@"%lu", textureIndex] cStringUsingEncoding:NSUTF8StringEncoding];
                        dispatch_queue_set_specific(AppServices.textureQueue, queue_index, (void *)CFBridgingRetain(texture), NULL);
                        [self.mediaCollectionView insertItemForQueueDataIndex:label];
                        textureIndex++;
                    });
                    return TRUE;
                });
            });

Чтобы закрыть файл:

dispatch_io_close(read_channel, 0);
close(read_dfd);
...