Dispatch I/O

<span style="font-size:14px;color:#6600cc;">先mark一下</span>
<span style="font-size:14px;color:#6600cc;">
</span>
<span style="font-size:14px;color:#6600cc;">/*
2	 * Copyright (c) 2009-2011 Apple Inc. All rights reserved.
3	 *
4	 * @APPLE_APACHE_LICENSE_HEADER_START@
5	 *
6	 * Licensed under the Apache License, Version 2.0 (the "License");
7	 * you may not use this file except in compliance with the License.
8	 * You may obtain a copy of the License at
9	 *
10	 *     http://www.apache.org/licenses/LICENSE-2.0
11	 *
12	 * Unless required by applicable law or agreed to in writing, software
13	 * distributed under the License is distributed on an "AS IS" BASIS,
14	 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15	 * See the License for the specific language governing permissions and
16	 * limitations under the License.
17	 *
18	 * @APPLE_APACHE_LICENSE_HEADER_END@
19	 */
20	
21	#include <sys/stat.h>
22	#include <sys/types.h>
23	#include <sys/param.h>
24	#include <assert.h>
25	#include <fcntl.h>
26	#include <stdio.h>
27	#include <stdlib.h>
28	#include <unistd.h>
29	#include <errno.h>
30	#include <fts.h>
31	#include <mach/mach.h>
32	#include <mach/mach_time.h>
33	#include <libkern/OSAtomic.h>
34	#include <TargetConditionals.h>
35	
36	#include <dispatch/dispatch.h>
37	
38	#include <bsdtests.h>
39	#include "dispatch_test.h"
40	
41	#ifndef DISPATCHTEST_IO
42	#if DISPATCH_API_VERSION >= 20100226 && DISPATCH_API_VERSION != 20101110
43	#define DISPATCHTEST_IO 1
44	#if DISPATCH_API_VERSION >= 20100723
45	#define DISPATCHTEST_IO_PATH 1 // rdar://problem/7738093
46	#endif
47	#endif
48	#endif
49	
50	void
51	test_fin(void *cxt)
52	{
53	        test_ptr("test_fin run", cxt, cxt);
54	        test_stop();
55	}
56	
57	#if DISPATCHTEST_IO
58	
59	#if TARGET_OS_EMBEDDED
60	#define LARGE_FILE "/System/Library/Fonts/Cache/STHeiti-Light.ttc" // 29MB file
61	#define maxopenfiles 768
62	#else
63	#define LARGE_FILE "/System/Library/Speech/Voices/Alex.SpeechVoice/Contents/Resources/PCMWave" // 417MB file
64	#define maxopenfiles 4096
65	#endif
66	
67	static void
68	test_io_close(int with_timer, bool from_path)
69	{
70	        #define chunks 4
71	        #define READSIZE (512*1024)
72	        unsigned int i;
73	        const char *path = LARGE_FILE;
74	        int fd = open(path, O_RDONLY);
75	        if (fd == -1) {
76	                if (errno == ENOENT) {
77	                        test_skip("Large file not found");
78	                        return;
79	                }
80	                test_errno("open", errno, 0);
81	                test_stop();
82	        }
83	        if (fcntl(fd, F_GLOBAL_NOCACHE, 1) == -1) {
84	                test_errno("fcntl F_GLOBAL_NOCACHE", errno, 0);
85	                test_stop();
86	        }
87	        struct stat sb;
88	        if (fstat(fd, &sb)) {
89	                test_errno("fstat", errno, 0);
90	                test_stop();
91	        }
92	        const size_t size = sb.st_size / chunks;
93	        const int expected_error = with_timer? ECANCELED : 0;
94	        dispatch_source_t t = NULL;
95	        dispatch_group_t g = dispatch_group_create();
96	        dispatch_group_enter(g);
97	        void (^cleanup_handler)(int error) = ^(int error) {
98	                test_errno("create error", error, 0);
99	                dispatch_group_leave(g);
100	                close(fd);
101	        };
102	        dispatch_io_t io;
103	        if (!from_path) {
104	                io = dispatch_io_create(DISPATCH_IO_RANDOM, fd,
105	                                dispatch_get_global_queue(0, 0), cleanup_handler);
106	        } else {
107	#if DISPATCHTEST_IO_PATH
108	                io = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, path, O_RDONLY, 0,
109	                                dispatch_get_global_queue(0, 0), cleanup_handler);
110	#endif
111	        }
112	        dispatch_io_set_high_water(io, READSIZE);
113	        if (with_timer == 1) {
114	                dispatch_io_set_low_water(io, READSIZE);
115	                dispatch_io_set_interval(io,  2 * NSEC_PER_SEC,
116	                                DISPATCH_IO_STRICT_INTERVAL);
117	        } else if (with_timer == 2) {
118	                t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
119	                                dispatch_get_global_queue(0,0));
120	                dispatch_retain(io);
121	                dispatch_source_set_event_handler(t, ^{
122	                        dispatch_io_close(io, DISPATCH_IO_STOP);
123	                        dispatch_source_cancel(t);
124	                });
125	                dispatch_source_set_cancel_handler(t, ^{
126	                        dispatch_release(io);
127	                });
128	                dispatch_source_set_timer(t, dispatch_time(DISPATCH_TIME_NOW,
129	                                2 * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
130	                dispatch_resume(t);
131	        }
132	
133	        size_t chunk_sizes[chunks] = {}, *chunk_size = chunk_sizes, total = 0;
134	        dispatch_data_t data_chunks[chunks], *data = data_chunks;
135	        for (i = 0; i < chunks; i++) {
136	                data[i] = dispatch_data_empty;
137	                dispatch_group_enter(g);
138	                dispatch_io_read(io, i * size, size, dispatch_get_global_queue(0,0),
139	                                ^(bool done, dispatch_data_t d, int error) {
140	                        if (d) {
141	                                chunk_size[i] += dispatch_data_get_size(d);
142	                                dispatch_data_t concat = dispatch_data_create_concat(data[i], d);
143	                                dispatch_release(data[i]);
144	                                data[i] = concat;
145	                                if ((dispatch_data_get_size(d) < READSIZE && !error && !done)) {
146	                                        // The timer must have fired
147	                                        dispatch_io_close(io, DISPATCH_IO_STOP);
148	                                        return;
149	                                }
150	                        }
151	                        if (done) {
152	                                test_errno("read error", error,
153	                                                error == expected_error ? expected_error : 0);
154	                                dispatch_group_leave(g);
155	                                dispatch_release(data[i]);
156	                        }
157	                });
158	        }
159	        dispatch_io_close(io, 0);
160	        dispatch_io_close(io, 0);
161	        dispatch_io_read(io, 0, 1, dispatch_get_global_queue(0,0),
162	                        ^(bool done, dispatch_data_t d, int error) {
163	                test_long("closed done", done, true);
164	                test_errno("closed error", error, ECANCELED);
165	                test_ptr_null("closed data", d);
166	        });
167	        dispatch_release(io);
168	        test_group_wait(g);
169	        dispatch_release(g);
170	        if (t) {
171	                dispatch_source_cancel(t);
172	                dispatch_release(t);
173	        }
174	        for (i = 0; i < chunks; i++) {
175	                if (with_timer) {
176	                        test_long_less_than("chunk size", chunk_size[i], size + 1);
177	                } else {
178	                        test_long("chunk size", chunk_size[i], size);
179	                }
180	                total += chunk_size[i];
181	        }
182	        if (with_timer) {
183	                test_long_less_than("total size", total, chunks * size + 1);
184	        } else {
185	                test_long("total size", total, chunks * size);
186	        }
187	}
188	
189	static void
190	test_io_stop(void) // rdar://problem/8250057
191	{
192	        int fds[2], *fd = fds;
193	        if(pipe(fd) == -1) {
194	                test_errno("pipe", errno, 0);
195	                test_stop();
196	        }
197	        dispatch_group_t g = dispatch_group_create();
198	        dispatch_group_enter(g);
199	        dispatch_io_t io = dispatch_io_create(DISPATCH_IO_STREAM, *fd,
200	                        dispatch_get_global_queue(0, 0), ^(int error) {
201	                test_errno("create error", error, 0);
202	                close(*fd);
203	                close(*(fd+1));
204	                dispatch_group_leave(g);
205	        });
206	        dispatch_group_enter(g);
207	        dispatch_io_read(io, 0, 1, dispatch_get_global_queue(0, 0),
208	                        ^(bool done, dispatch_data_t d __attribute__((unused)), int error) {
209	                if (done) {
210	                        test_errno("read error", error, ECANCELED);
211	                        dispatch_group_leave(g);
212	                }
213	        });
214	        dispatch_source_t t = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0,
215	                        0, dispatch_get_global_queue(0,0));
216	        dispatch_retain(io);
217	        dispatch_source_set_event_handler(t, ^{
218	                dispatch_io_close(io, DISPATCH_IO_STOP);
219	                dispatch_release(io);
220	        });
221	        dispatch_source_set_timer(t, dispatch_time(DISPATCH_TIME_NOW,
222	                        2 * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
223	        dispatch_resume(t);
224	        dispatch_release(io);
225	        test_group_wait(g);
226	        dispatch_release(g);
227	        dispatch_source_cancel(t);
228	        dispatch_release(t);
229	}
230	
231	static void
232	test_io_read_write(void)
233	{
234	        const char *path_in = "/usr/share/dict/words";
235	        char path_out[] = "/tmp/dispatchtest_io.XXXXXX";
236	        const size_t siz_in = 1024 * 1024;
237	
238	        int in = open(path_in, O_RDONLY);
239	        if (in == -1) {
240	                test_errno("open", errno, 0);
241	                test_stop();
242	        }
243	        int out = mkstemp(path_out);
244	        if (out == -1) {
245	                test_errno("mkstemp", errno, 0);
246	                test_stop();
247	        }
248	        if (unlink(path_out) == -1) {
249	                test_errno("unlink", errno, 0);
250	                test_stop();
251	        }
252	        dispatch_queue_t q = dispatch_get_global_queue(0,0);
253	        dispatch_group_t g = dispatch_group_create();
254	        dispatch_group_enter(g);
255	        dispatch_io_t io_in = dispatch_io_create(DISPATCH_IO_STREAM, in,
256	                        q, ^(int error) {
257	                test_errno("dispatch_io_create", error, 0);
258	                close(in);
259	                dispatch_group_leave(g);
260	        });
261	        dispatch_io_set_high_water(io_in, siz_in/4);
262	        dispatch_group_enter(g);
263	        dispatch_io_t io_out = dispatch_io_create(DISPATCH_IO_STREAM, out,
264	                        q, ^(int error) {
265	                test_errno("dispatch_io_create", error, 0);
266	                dispatch_group_leave(g);
267	        });
268	        dispatch_io_set_high_water(io_out, siz_in/16);
269	        __block dispatch_data_t data = dispatch_data_empty;
270	        dispatch_group_enter(g);
271	        dispatch_io_read(io_in, 0, siz_in, q,
272	                        ^(bool done_in, dispatch_data_t data_in, int err_in) {
273	                test_long_less_than("read size", dispatch_data_get_size(data_in),
274	                                siz_in);
275	                if (data_in) {
276	                        dispatch_group_enter(g);
277	                        dispatch_io_write(io_out, 0, data_in, q,
278	                                        ^(bool done_out, dispatch_data_t data_out, int err_out) {
279	                                if (done_out) {
280	                                        test_errno("dispatch_io_write", err_out, 0);
281	                                        test_long("remaining write size",
282	                                                        data_out ? dispatch_data_get_size(data_out) : 0, 0);
283	                                        dispatch_group_leave(g);
284	                                } else {
285	                                        test_long_less_than("remaining write size",
286	                                                        dispatch_data_get_size(data_out), siz_in);
287	                                }
288	                        });
289	                        dispatch_data_t concat = dispatch_data_create_concat(data, data_in);
290	                        dispatch_release(data);
291	                        data = concat;
292	                }
293	                if (done_in) {
294	                        test_errno("dispatch_io_read", err_in, 0);
295	                        dispatch_release(io_out);
296	                        dispatch_group_leave(g);
297	                }
298	        });
299	        dispatch_release(io_in);
300	        test_group_wait(g);
301	        lseek(out, 0, SEEK_SET);
302	        dispatch_group_enter(g);
303	        dispatch_read(out, siz_in, q,
304	                        ^(dispatch_data_t cmp, int err_cmp) {
305	                if (err_cmp) {
306	                        test_errno("dispatch_read", err_cmp, 0);
307	                        test_stop();
308	                }
309	                close(out);
310	                size_t siz_cmp = dispatch_data_get_size(cmp);
311	                test_long("readback size", siz_cmp, siz_in);
312	                const void *data_buf, *cmp_buf;
313	                dispatch_data_t data_map, cmp_map;
314	                data_map = dispatch_data_create_map(data, &data_buf, NULL);
315	                cmp_map = dispatch_data_create_map(cmp, &cmp_buf, NULL);
316	                test_long("readback memcmp",
317	                                memcmp(data_buf, cmp_buf, MIN(siz_in, siz_cmp)), 0);
318	                dispatch_release(cmp_map);
319	                dispatch_release(data_map);
320	                dispatch_release(data);
321	                dispatch_group_leave(g);
322	        });
323	        test_group_wait(g);
324	        dispatch_release(g);
325	}
326	
327	enum {
328	        DISPATCH_ASYNC_READ_ON_CONCURRENT_QUEUE = 0,
329	        DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE,
330	        DISPATCH_READ_ON_CONCURRENT_QUEUE,
331	        DISPATCH_IO_READ_ON_CONCURRENT_QUEUE,
332	        DISPATCH_IO_READ_FROM_PATH_ON_CONCURRENT_QUEUE,
333	};
334	
335	static void
336	test_async_read(char *path, size_t size, int option, dispatch_queue_t queue,
337	                void (^process_data)(size_t))
338	{
339	        int fd = open(path, O_RDONLY);
340	        if (fd == -1) {
341	                test_errno("Failed to open file", errno, 0);
342	                test_stop();
343	        }
344	        // disable caching also for extra fd opened by dispatch_io_create_with_path
345	        if (fcntl(fd, F_GLOBAL_NOCACHE, 1) == -1) {
346	                test_errno("fcntl F_GLOBAL_NOCACHE", errno, 0);
347	                test_stop();
348	        }
349	        switch (option) {
350	                case DISPATCH_ASYNC_READ_ON_CONCURRENT_QUEUE:
351	                case DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE:
352	                        dispatch_async(queue, ^{
353	                                char* buffer = valloc(size);
354	                                ssize_t r = read(fd, buffer, size);
355	                                if (r == -1) {
356	                                        test_errno("async read error", errno, 0);
357	                                        test_stop();
358	                                }
359	                                free(buffer);
360	                                close(fd);
361	                                process_data(r);
362	                        });
363	                        break;
364	                case DISPATCH_READ_ON_CONCURRENT_QUEUE:
365	                        dispatch_read(fd, size, queue, ^(dispatch_data_t d, int error) {
366	                                if (error) {
367	                                        test_errno("dispatch_read error", error, 0);
368	                                        test_stop();
369	                                }
370	                                close(fd);
371	                                process_data(dispatch_data_get_size(d));
372	                        });
373	                        break;
374	                case DISPATCH_IO_READ_ON_CONCURRENT_QUEUE:
375	                case DISPATCH_IO_READ_FROM_PATH_ON_CONCURRENT_QUEUE: {
376	                        __block bool is_done = false;
377	                        __block dispatch_data_t d = dispatch_data_empty;
378	                        void (^cleanup_handler)(int error) = ^(int error) {
379	                                if (error) {
380	                                        test_errno("dispatch_io_create error", error, 0);
381	                                        test_stop();
382	                                }
383	                                if (!is_done) {
384	                                        test_long("dispatch_io_read done", is_done, true);
385	                                }
386	                                close(fd);
387	                                process_data(dispatch_data_get_size(d));
388	                                dispatch_release(d);
389	                        };
390	                        dispatch_io_t io = NULL;
391	                        if (option == DISPATCH_IO_READ_FROM_PATH_ON_CONCURRENT_QUEUE) {
392	#if DISPATCHTEST_IO_PATH
393	                                io = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, path,
394	                                                O_RDONLY, 0, queue, cleanup_handler);
395	#endif
396	                        } else {
397	                                io = dispatch_io_create(DISPATCH_IO_RANDOM, fd, queue,
398	                                                cleanup_handler);
399	                        }
400	                        if (!io) {
401	                                test_ptr_notnull("dispatch_io_create", io);
402	                                test_stop();
403	                        }
404	
405	                        // Timeout after 20 secs
406	                        dispatch_io_set_interval(io, 20 * NSEC_PER_SEC,
407	                                        DISPATCH_IO_STRICT_INTERVAL);
408	
409	                        dispatch_io_read(io, 0, size, queue,
410	                                        ^(bool done, dispatch_data_t data, int error) {
411	                                if (!done && !error && !dispatch_data_get_size(data)) {
412	                                        // Timer fired, and no progress from last delivery
413	                                        dispatch_io_close(io, DISPATCH_IO_STOP);
414	                                }
415	                                if (data) {
416	                                        dispatch_data_t c = dispatch_data_create_concat(d, data);
417	                                        dispatch_release(d);
418	                                        d = c;
419	                                }
420	                                if (error) {
421	                                        test_errno("dispatch_io_read error", error, 0);
422	                                        if (error != ECANCELED) {
423	                                                test_stop();
424	                                        }
425	                                }
426	                                is_done = done;
427	                        });
428	                        dispatch_release(io);
429	                        break;
430	                }
431	        }
432	}
433	
434	static int
435	test_read_dirs(char **paths, dispatch_queue_t queue, dispatch_group_t g,
436	                dispatch_semaphore_t s, volatile int32_t *bytes, int option)
437	{
438	        FTS *tree = fts_open(paths, FTS_PHYSICAL|FTS_XDEV, NULL);
439	        if (!tree) {
440	                test_ptr_notnull("fts_open failed", tree);
441	                test_stop();
442	        }
443	        unsigned int files_opened = 0;
444	        size_t size, total_size = 0;
445	        FTSENT *node;
446	        while ((node = fts_read(tree)) &&
447	                        !(node->fts_info == FTS_ERR || node->fts_info == FTS_NS)) {
448	                if (node->fts_level > 0 && node->fts_name[0] == '.') {
449	                        fts_set(tree, node, FTS_SKIP);
450	                } else if (node->fts_info == FTS_F) {
451	                        dispatch_group_enter(g);
452	                        dispatch_semaphore_wait(s, DISPATCH_TIME_FOREVER);
453	                        size = node->fts_statp->st_size;
454	                        total_size += size;
455	                        files_opened++;
456	                        test_async_read(node->fts_path, size, option, queue, ^(size_t len){
457	                                OSAtomicAdd32(len, bytes);
458	                                dispatch_semaphore_signal(s);
459	                                dispatch_group_leave(g);
460	                        });
461	                }
462	        }
463	        if ((!node && errno) || (node && (node->fts_info == FTS_ERR ||
464	                        node->fts_info == FTS_NS))) {
465	                test_errno("fts_read failed", !node ? errno : node->fts_errno, 0);
466	                test_stop();
467	        }
468	        if (fts_close(tree)) {
469	                test_errno("fts_close failed", errno, 0);
470	                test_stop();
471	        }
472	        test_group_wait(g);
473	        test_long("total size", *bytes, total_size);
474	        return files_opened;
475	}
476	
477	extern __attribute__((weak_import)) void
478	_dispatch_iocntl(uint32_t param, uint64_t value);
479	
480	enum {
481	        DISPATCH_IOCNTL_CHUNK_PAGES = 1,
482	        DISPATCH_IOCNTL_LOW_WATER_CHUNKS,
483	        DISPATCH_IOCNTL_INITIAL_DELIVERY,
484	};
485	
486	static void
487	test_read_many_files(void)
488	{
489	        char *paths[] = {"/usr/lib", NULL};
490	        dispatch_group_t g = dispatch_group_create();
491	        dispatch_semaphore_t s = dispatch_semaphore_create(maxopenfiles);
492	        uint64_t start;
493	        volatile int32_t bytes;
494	        unsigned int files_read, i;
495	
496	        const dispatch_queue_t queues[] = {
497	                [DISPATCH_ASYNC_READ_ON_CONCURRENT_QUEUE] =
498	                                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
499	                [DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE] =
500	                                dispatch_queue_create("read", NULL),
501	                [DISPATCH_READ_ON_CONCURRENT_QUEUE] =
502	                                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
503	                [DISPATCH_IO_READ_ON_CONCURRENT_QUEUE] =
504	                                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
505	#if DISPATCHTEST_IO_PATH
506	                [DISPATCH_IO_READ_FROM_PATH_ON_CONCURRENT_QUEUE] =
507	                                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0),
508	#endif
509	        };
510	        dispatch_set_target_queue(queues[DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE],
511	                        dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));
512	        static const char *names[] = {
513	                [DISPATCH_ASYNC_READ_ON_CONCURRENT_QUEUE] =
514	                        "dispatch_async(^{read();}) on concurrent queue",
515	                [DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE] =
516	                        "dispatch_async(^{read();}) on serial queue",
517	                [DISPATCH_READ_ON_CONCURRENT_QUEUE] =
518	                        "dispatch_read() on concurrent queue",
519	                [DISPATCH_IO_READ_ON_CONCURRENT_QUEUE] =
520	                        "dispatch_io_read() on concurrent queue",
521	                [DISPATCH_IO_READ_FROM_PATH_ON_CONCURRENT_QUEUE] =
522	                        "dispatch_io_read() from path on concurrent queue",
523	        };
524	
525	        if (_dispatch_iocntl) {
526	                const size_t chunk_pages = 3072;
527	                _dispatch_iocntl(DISPATCH_IOCNTL_CHUNK_PAGES, (uint64_t)chunk_pages);
528	        }
529	        struct rlimit l;
530	        if (!getrlimit(RLIMIT_NOFILE, &l) && l.rlim_cur < 2 * maxopenfiles + 256) {
531	                l.rlim_cur = 2 * maxopenfiles + 256;
532	                setrlimit(RLIMIT_NOFILE, &l);
533	        }
534	        for (i = 0; i < sizeof(queues)/sizeof(dispatch_queue_t); ++i) {
535	                fprintf(stdout, "%s:\n", names[i]);
536	                bytes = 0;
537	                start = mach_absolute_time();
538	                files_read = test_read_dirs(paths, queues[i], g, s, &bytes, i);
539	                double elapsed = (double)(mach_absolute_time() - start) / NSEC_PER_SEC;
540	                double throughput = ((double)bytes / elapsed)/(1024 * 1024);
541	                fprintf(stdout, "Total Files read: %u, Total MBytes %g, "
542	                                "Total time: %g s, Throughput: %g MB/s\n", files_read,
543	                                (double)bytes / (1024 * 1024), elapsed, throughput);
544	        }
545	        dispatch_release(queues[DISPATCH_ASYNC_READ_ON_SERIAL_QUEUE]);
546	        dispatch_release(s);
547	        dispatch_release(g);
548	}
549	
550	static void
551	test_io_from_io(void) // rdar://problem/8388909
552	{
553	#if DISPATCH_API_VERSION >= 20101012
554	        const size_t siz_in = 10240;
555	        dispatch_queue_t q = dispatch_get_global_queue(0, 0);
556	        char path[] = "/tmp/dispatchtest_io.XXXXXX/file.name";
557	        char *tmp = strrchr(path, '/');
558	        *tmp = '\0';
559	        if (!mkdtemp(path)) {
560	                test_ptr_notnull("mkdtemp failed", path);
561	                test_stop();
562	        }
563	        // Make the directory immutable
564	        if (chflags(path, UF_IMMUTABLE) == -1) {
565	                test_errno("chflags", errno, 0);
566	                test_stop();
567	        }
568	        *tmp = '/';
569	        dispatch_io_t io = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, path,
570	                        O_CREAT|O_RDWR, 0600, q, ^(int error) {
571	                                if (error) {
572	                                        test_errno("channel cleanup called with error", error, 0);
573	                                        test_stop();
574	                                }
575	                                test_errno("channel cleanup called", error, 0);
576	                        });
577	
578	        dispatch_group_t g = dispatch_group_create();
579	        char *foo = malloc(256);
580	        dispatch_data_t tdata;
581	        tdata = dispatch_data_create(foo, 256, NULL, DISPATCH_DATA_DESTRUCTOR_FREE);
582	        dispatch_group_enter(g);
583	        dispatch_io_write(io, 0, tdata, q, ^(bool done, dispatch_data_t data_out,
584	                        int err_out) {
585	                test_errno("error from write to immutable directory", err_out, EPERM);
586	                test_long("unwritten data", dispatch_data_get_size(data_out), 256);
587	                if (!err_out && done) {
588	                        test_stop();
589	                }
590	                if (done) {
591	                        dispatch_group_leave(g);
592	                }
593	        });
594	        dispatch_release(tdata);
595	        dispatch_release(io);
596	        test_group_wait(g);
597	        // Change the directory to mutable
598	        *tmp = '\0';
599	        if (chflags(path, 0) == -1) {
600	                test_errno("chflags", errno, 0);
601	                test_stop();
602	        }
603	        const char *path_in = "/dev/random";
604	        int in = open(path_in, O_RDONLY);
605	        if (in == -1) {
606	                test_errno("open", errno, 0);
607	                test_stop();
608	        }
609	        *tmp = '/';
610	        dispatch_group_enter(g);
611	
612	        io = dispatch_io_create_with_path(DISPATCH_IO_RANDOM, path,
613	                        O_CREAT|O_RDWR, 0600, q, ^(int error) {
614	                if (error) {
615	                        test_errno("channel cleanup called with error", error, 0);
616	                        test_stop();
617	                }
618	                test_errno("channel cleanup called", error, 0);
619	        });
620	        dispatch_read(in, siz_in, q, ^(dispatch_data_t data_in, int err_in ) {
621	                if (err_in) {
622	                        test_errno("dispatch_read", err_in, 0);
623	                        test_stop();
624	                }
625	                dispatch_io_write(io, 0, data_in, q,
626	                                ^(bool done, dispatch_data_t data_out, int err_out) {
627	                        if (done) {
628	                                test_errno("dispatch_io_write", err_out, 0);
629	                                test_long("remaining write size",
630	                                                data_out ? dispatch_data_get_size(data_out) : 0, 0);
631	                                dispatch_group_leave(g);
632	                        } else {
633	                                test_long_less_than("remaining write size",
634	                                                dispatch_data_get_size(data_out), siz_in);
635	                        }
636	                });
637	        });
638	        test_group_wait(g);
639	        dispatch_io_t io2 = dispatch_io_create_with_io(DISPATCH_IO_STREAM, io, q,
640	                        ^(int error) {
641	                if (error) {
642	                        test_errno("dispatch_io_create_with_io", error, 0);
643	                        test_stop();
644	                }
645	        });
646	        dispatch_release(io);
647	        dispatch_group_enter(g);
648	        __block dispatch_data_t data_out = dispatch_data_empty;
649	        dispatch_io_read(io2, 0, siz_in, q,
650	                        ^(bool done, dispatch_data_t d, int error) {
651	                if (d) {
652	                        dispatch_data_t concat = dispatch_data_create_concat(data_out, d);
653	                        dispatch_release(data_out);
654	                        data_out = concat;
655	                }
656	                if (done) {
657	                        test_errno("read error from channel created_with_io", error, 0);
658	                        dispatch_group_leave(g);
659	                }
660	        });
661	        dispatch_release(io2);
662	        test_group_wait(g);
663	        dispatch_release(g);
664	        test_long("readback size", dispatch_data_get_size(data_out), siz_in);
665	        dispatch_release(data_out);
666	#endif
667	}
668	
669	#endif // DISPATCHTEST_IO
670	
671	int
672	main(void)
673	{
674	        dispatch_test_start("Dispatch IO");
675	        dispatch_async(dispatch_get_main_queue(), ^{
676	#if DISPATCHTEST_IO
677	                int i; bool from_path = false;
678	                do {
679	                        for (i = 0; i < 3; i++) {
680	                                test_io_close(i, from_path);
681	                        }
682	#if DISPATCHTEST_IO_PATH
683	                        from_path = !from_path;
684	#endif
685	                } while (from_path);
686	                test_io_stop();
687	                test_io_from_io();
688	                test_io_read_write();
689	                test_read_many_files();
690	#endif
691	                test_fin(NULL);
692	        });
693	        dispatch_main();
694	}</span>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值