使用Grand Central Dispatch时何时可以安全地调用dispatch_io_close()?

时间:2015-08-20 19:06:55

标签: macos

我的应用使用Apple在OSX 10.10上提供的Grand Central Dispatch提供的dispatch_io_read()dispatch_io_write()方法复制文件。

我向用户提供了取消'按钮。我已经使用dispatch_io_close(channel, DISPATCH_IO_STOP)以各种方式实现了这一点,但是我尝试的每种方法都会导致偶尔崩溃,例如" BUG IN LIBDISPATCH:过度释放对象"," BUIB IN LIBDISPATCH:一个物体的复活"或与保留/释放问题相关的类似崩溃。

如果我只是close()用户点击“取消”时正在阅读/写入的文件的文件描述符,或者如果我调用dispatch_io_close(channel, DISPATCH_IO_STOP),我会遇到相同类型的崩溃来自dispatch_io_read()dispatch_io_write()的清理处理程序。

如何安全取消GCD IO操作?

2 个答案:

答案 0 :(得分:0)

以下是使用dispatch io编写cat的简单替换的示例:

#include <dispatch/dispatch.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main() {
    dispatch_io_t stdin_io = dispatch_io_create(DISPATCH_IO_STREAM, STDIN_FILENO, dispatch_get_main_queue(), ^(int error) {
        fprintf(stderr, "Error opening stdin: %d\n", error);
        exit(error);
    });

    dispatch_io_t stdout_io = dispatch_io_create(DISPATCH_IO_STREAM, STDOUT_FILENO, dispatch_get_main_queue(), ^(int error) {
        fprintf(stderr, "Error opening stdout: %d\n", error);
        exit(error);
    });

    dispatch_group_t io_completion_group = dispatch_group_create();

    dispatch_group_enter(io_completion_group);
    dispatch_io_read(stdin_io, 0, SIZE_MAX, dispatch_get_main_queue(), ^(bool done, dispatch_data_t data, int error) {
        if (error) {
            fprintf(stderr, "Read error: %d\n", error);
            exit(error);
        }

        dispatch_group_enter(io_completion_group);
        dispatch_io_write(stdout_io, 0, data, dispatch_get_main_queue(), ^(bool done, dispatch_data_t data, int error) {
            if (error) {
                fprintf(stderr, "Write error: %d\n", error);
                exit(error);
            }

            if (done) {
                dispatch_group_leave(io_completion_group);
            }
        });

        if (done) {
            dispatch_group_leave(io_completion_group);
        }
    });

    dispatch_group_notify(io_completion_group, dispatch_get_main_queue(), ^{
        dispatch_io_close(stdin_io, 0);
        dispatch_release(stdin_io);

        dispatch_io_close(stdout_io, 0);
        dispatch_release(stdout_io);

        dispatch_release(io_completion_group);

        exit(0);
    });

    dispatch_main();
}

希望这可以作为你的起点。

答案 1 :(得分:0)

在读取操作完成之前,您需要使用信号量来阻止关闭;你可以安全地关闭频道。这是一个片段,向您展示如何为写操作执行此操作(尽管它可以很容易地以相同的方式用于读取操作):

size_t length = width * height * 4;
void *tdata = malloc(length);
[texture getBytes:tdata bytesPerRow:rowBytes fromRegion:MTLRegionMake2D(0.0, 0.0, width, height) mipmapLevel:0];
            dispatch_data_t data = dispatch_data_create(tdata, length, NULL, DISPATCH_DATA_DESTRUCTOR_DEFAULT);
NSString *outputTextureFileName = [NSString stringWithFormat:@"%lu", frame];
NSString *outputTextureFilePath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:@"/PinHoleTemp/%@", [outputTextureFileName stringByAppendingPathExtension:@"dat"]]];

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

dispatch_io_t write_channel = dispatch_io_create_with_path(DISPATCH_IO_STREAM, [outputTextureFilePath UTF8String], 1, O_WRONLY, dispatch_get_global_queue(0, 0), ^(int error) {

                                                                 });

dispatch_io_write(write_channel, 0, data, dispatch_get_global_queue(0, 0), ^(bool done, dispatch_data_t  _Nullable data, int error) {
        if (done) NSLog(@"Finished writing data to file");
        dispatch_semaphore_signal(semaphore);
        });

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);


    dispatch_io_close(write_channel, 0);

此示例在每次摄像机获取视频帧时将包含Metal纹理的数据文件写入指定URL的文件。创建纹理后,它将转换为dispatch_data_t对象。然后,创建一个信号量,用于阻止任何创建和打开新通道并写入操作直到完成。在dispatch_io_write完成处理程序发出信号量信号后,代码将执行dispatch_io_close块。