-
-
Save pmj/8032259 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <assert.h> | |
| #include <dispatch/dispatch.h> | |
| #include <errno.h> | |
| #include <fcntl.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <stdlib.h> | |
| #include <sys/event.h> | |
| #include <sys/event.h> | |
| #include <sys/select.h> | |
| #include <sys/stat.h> | |
| #include <sys/time.h> | |
| #include <sys/time.h> | |
| #include <sys/types.h> | |
| #include <sys/types.h> | |
| #include <sys/types.h> | |
| #include <unistd.h> | |
| #define FILENAME "/tmp/.some-tmp-file" | |
| #define MAX_FILE_SIZE 10*1000*1000 | |
| int get_fd_size(int fd) | |
| { | |
| off_t eof_pos = lseek(fd, 0, SEEK_END); | |
| return eof_pos; | |
| } | |
| void go_on(int fd) | |
| { | |
| int ret = -1; | |
| int kq = kqueue(); | |
| struct kevent changelist[1]; | |
| if (kq < 0) { | |
| /* errno set by kqueue */ | |
| ret = -1; | |
| goto out; | |
| } | |
| unsigned num_write_events = 0, num_ext_events = 0; | |
| memset(changelist, 0, sizeof(changelist)); | |
| EV_SET(&changelist[0], fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, NOTE_DELETE | NOTE_RENAME | NOTE_EXTEND | NOTE_WRITE, 0, 0); | |
| if (kevent(kq, changelist, 1, NULL, 0, NULL) < 0) { | |
| /* errno set by kevent */ | |
| ret = -1; | |
| goto out; | |
| } | |
| while (1) { | |
| { | |
| /* Step 1: Check the size */ | |
| off_t new_sz = get_fd_size(fd); | |
| //printf("new size: %lld\n", new_sz); | |
| if (new_sz >= MAX_FILE_SIZE) { | |
| //printf("Ok, success: %u write %u extend events\n", num_write_events, num_ext_events); | |
| goto out; | |
| } | |
| /*if (num_ext_events >= 10) | |
| { | |
| printf("%uth extend event\n", num_ext_events); | |
| }*/ | |
| if (num_write_events >= 10) | |
| { | |
| printf("%uth write event\n", num_write_events); | |
| } | |
| } | |
| { | |
| /* Step 2: Wait for growth */ | |
| struct timespec timeout = { .tv_sec = 1, .tv_nsec = 0 }; | |
| int suc_kev = kevent(kq, NULL, 0, changelist, 1, &timeout); | |
| if (0 == suc_kev) { | |
| /* That's a timeout */ | |
| off_t new_sz = get_fd_size(fd); | |
| printf("Timeout (%lld) %u write %u extend events\n", new_sz, num_write_events, num_ext_events); | |
| errno = ETIMEDOUT; | |
| ret = -1; | |
| goto out; | |
| } else if (suc_kev > 0) { | |
| char *type = "?"; | |
| if ( changelist[0].filter == EVFILT_VNODE && | |
| changelist[0].fflags & NOTE_EXTEND) { | |
| type = "EXTEND"; | |
| ++num_ext_events; | |
| } | |
| if ( changelist[0].filter == EVFILT_VNODE && | |
| changelist[0].fflags & NOTE_WRITE) { | |
| type = "WRITE"; | |
| ++num_write_events; | |
| } | |
| /* | |
| printf("kevent %s #%p (%p | %p)\n", type, changelist[0].ident, changelist[0].data, changelist[0].udata); | |
| */ | |
| if (changelist[0].filter == EVFILT_VNODE) { | |
| if (changelist[0].fflags & NOTE_RENAME || changelist[0].fflags & NOTE_DELETE) { | |
| /* file was deleted, renamed, ... */ | |
| printf("Renamed\n"); | |
| errno = ENOENT; | |
| ret = -1; | |
| goto out; | |
| } | |
| } | |
| } else { | |
| /* errno set by kevent */ | |
| printf("Error: %d\n", errno); | |
| ret = -1; | |
| goto out; | |
| } | |
| } | |
| } | |
| out: { | |
| int errno_save = errno; | |
| if (kq >= 0) { | |
| close(kq); | |
| } | |
| errno = errno_save; | |
| return; | |
| } | |
| } | |
| void do_writes(int xfd) | |
| { | |
| int write_fd = dup(xfd);//open(FILENAME, O_WRONLY); | |
| const off_t size_per_write = MAX_FILE_SIZE / 10; | |
| const off_t desired_size = MAX_FILE_SIZE; | |
| char *buf = malloc(size_per_write); | |
| memset(buf, 'A', size_per_write); | |
| for (int i=0; i<desired_size / size_per_write; i++) { | |
| #ifndef USE_FTRUNCATE | |
| { | |
| size_t remain = size_per_write; | |
| ssize_t written; | |
| do | |
| { | |
| written = write(write_fd, buf, remain); | |
| if (written <= 0) | |
| { | |
| fprintf(stderr, "Write failed: written = %ld, errno = %d (%s)\n", written, errno, strerror(errno)); | |
| exit(1); | |
| } | |
| remain -= written; | |
| } while (remain > 0); | |
| } | |
| #else | |
| ftruncate(write_fd, (1+i) * size_per_write); | |
| #endif | |
| //printf("wrote\n"); | |
| //fsync(write_fd); | |
| //usleep(100000); | |
| } | |
| free(buf); | |
| { | |
| struct stat stat_buf = {0}; | |
| fstat(write_fd, &stat_buf); | |
| /*printf("writing thread finished: desired: %lld, actual: %lld\n", desired_size, stat_buf.st_size);*/ | |
| } | |
| close(write_fd); | |
| /* | |
| close(fd); | |
| */ | |
| } | |
| int main() | |
| { | |
| for (unsigned i = 0; i < 100000; ++i) | |
| { | |
| FILE* f = tmpfile(); | |
| int fd = fileno(f);//open(FILENAME, O_CREAT | O_TRUNC | O_RDWR, 0777); | |
| assert(fd >= 0); | |
| pid_t p = fork(); | |
| if (p == 0) | |
| { | |
| do_writes(fd); | |
| return 0; | |
| } | |
| else if (p < 0) | |
| { | |
| perror("fork failed\n"); | |
| return 1; | |
| } | |
| go_on(fd); | |
| fclose(f); | |
| wait(NULL); | |
| if (i % 200 == 199) | |
| printf("%d attempts done\n", i + 1); | |
| } | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment