<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>