diff --git a/ext-src/swoole_http_client_coro.cc b/ext-src/swoole_http_client_coro.cc index d63cb82708..19b84f0f65 100644 --- a/ext-src/swoole_http_client_coro.cc +++ b/ext-src/swoole_http_client_coro.cc @@ -126,8 +126,8 @@ class Client { bool in_callback = false; bool has_upload_files = false; - std::shared_ptr download_file; // save http response to file - zend::String download_file_name; // unlink the file on error + int download_file = 0; // save http response to file + zend::String download_file_name; // unlink the file on error zend_long download_offset = 0; /* safety zval */ @@ -366,6 +366,42 @@ static const zend_function_entry swoole_http_client_coro_methods[] = // clang-format on +static inline int http_client_open_file(const char *pathname, int flags, mode_t mode) { +#ifdef SW_USE_IOURING + return swoole_coroutine_iouring_open(pathname, flags, mode); +#else + return swoole_coroutine_open(pathname, flags, mode); +#endif +} + +static inline int http_client_close_file(int fd) { +#ifdef SW_USE_IOURING + return swoole_coroutine_iouring_close_file(fd); +#else + return swoole_coroutine_close_file(fd); +#endif +} + +static inline int http_client_truncate_file(int fd, off_t length) { + return swoole_coroutine_ftruncate(fd, length); +} + +static inline int http_client_lseek_file(int fd, off_t offset) { +#ifdef SW_USE_IOURING + return swoole_coroutine_iouring_lseek(fd, offset, SEEK_SET); +#else + return swoole_coroutine_lseek(fd, offset, SEEK_SET); +#endif +} + +static inline ssize_t http_client_co_write(int sockfd, const void *buf, size_t count) { +#ifdef SW_USE_IOURING + return swoole_coroutine_iouring_write(sockfd, buf, count); +#else + return swoole_coroutine_write(sockfd, buf, count); +#endif +} + void php_swoole_http_parse_set_cookies(const char *at, size_t length, zval *zcookies, zval *zset_cookie_headers) { const char *p, *eof = at + length; size_t key_len = 0, value_len = 0; @@ -486,14 +522,6 @@ static int http_parser_on_headers_complete(llhttp_t *parser) { return 0; } -static inline ssize_t http_client_co_write(int sockfd, const void *buf, size_t count) { -#ifdef SW_USE_IOURING - return swoole_coroutine_iouring_write(sockfd, buf, count); -#else - return swoole_coroutine_write(sockfd, buf, count); -#endif -} - static int http_parser_on_body(llhttp_t *parser, const char *at, size_t length) { auto *http = static_cast(parser->data); if (http->write_func) { @@ -523,28 +551,28 @@ static int http_parser_on_body(llhttp_t *parser, const char *at, size_t length) } } if (http->download_file_name.get() && http->body->length > 0) { - if (http->download_file == nullptr) { - char *download_file_name = http->download_file_name.val(); - std::shared_ptr fp = std::make_shared(download_file_name, O_CREAT | O_WRONLY, 0664); - if (!fp->ready()) { + if (http->download_file == 0) { + const char *download_file_name = http->download_file_name.val(); + int fd = http_client_open_file(download_file_name, O_CREAT | O_WRONLY, 0664); + if (sw_unlikely(fd == -1)) { swoole_sys_warning("open(%s, O_CREAT | O_WRONLY) failed", download_file_name); return -1; } + if (http->download_offset == 0) { - if (!fp->truncate(0)) { + if (http_client_truncate_file(fd, 0) < 0) { swoole_sys_warning("ftruncate(%s) failed", download_file_name); return -1; } } else { - if (!fp->set_offset(http->download_offset)) { + if (http_client_lseek_file(fd, http->download_offset) < 0) { swoole_sys_warning("fseek(%s, %jd) failed", download_file_name, (intmax_t) http->download_offset); return -1; } } - http->download_file = fp; + http->download_file = fd; } - if (http_client_co_write(http->download_file->get_fd(), SW_STRINGL(http->body)) != - (ssize_t) http->body->length) { + if (http_client_co_write(http->download_file, SW_STRINGL(http->body)) != (ssize_t) http->body->length) { return -1; } http->body->clear(); @@ -564,7 +592,7 @@ static int http_parser_on_message_complete(llhttp_t *parser) { zend_update_property_long( swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("statusCode"), parser->status_code); - if (http->download_file == nullptr) { + if (http->download_file == 0) { zend_update_property_stringl( swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("body"), SW_STRINGL(http->body)); } else { @@ -1651,8 +1679,9 @@ void Client::reset() { if (has_upload_files) { zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("uploadFiles")); } - if (download_file != nullptr) { - download_file.reset(); + if (download_file != 0) { + http_client_close_file(download_file); + download_file = 0; download_file_name.release(); download_offset = 0; zend_update_property_null(swoole_http_client_coro_ce, SW_Z8_OBJ_P(zobject), ZEND_STRL("downloadFile")); diff --git a/include/swoole_coroutine_c_api.h b/include/swoole_coroutine_c_api.h index 2477792492..0779430773 100644 --- a/include/swoole_coroutine_c_api.h +++ b/include/swoole_coroutine_c_api.h @@ -64,6 +64,7 @@ int swoole_coroutine_statvfs(const char *path, struct statvfs *buf); int swoole_coroutine_close_file(int fd); int swoole_coroutine_fsync(int fd); int swoole_coroutine_fdatasync(int fd); +int swoole_coroutine_ftruncate(int fd, off_t length); /** * io_uring */ @@ -72,6 +73,7 @@ int swoole_coroutine_iouring_open(const char *pathname, int flags, mode_t mode); int swoole_coroutine_iouring_close_file(int fd); ssize_t swoole_coroutine_iouring_read(int sockfd, void *buf, size_t count); ssize_t swoole_coroutine_iouring_write(int sockfd, const void *buf, size_t count); +off_t swoole_coroutine_iouring_lseek(int fd, off_t offset, int whence); int swoole_coroutine_iouring_rename(const char *oldpath, const char *newpath); int swoole_coroutine_iouring_mkdir(const char *pathname, mode_t mode); int swoole_coroutine_iouring_unlink(const char *pathname); diff --git a/include/swoole_file_hook.h b/include/swoole_file_hook.h index 4d8cabc17d..0bd8e8f380 100644 --- a/include/swoole_file_hook.h +++ b/include/swoole_file_hook.h @@ -24,6 +24,7 @@ #define close_file(fd) swoole_coroutine_iouring_close_file(fd) #define read(fd, buf, count) swoole_coroutine_iouring_read(fd, buf, count) #define write(fd, buf, count) swoole_coroutine_iouring_write(fd, buf, count) +#define lseek(fd, offset, whence) swoole_coroutine_iouring_lseek(fd, offset, whence) #define rename(oldpath, newpath) swoole_coroutine_iouring_rename(oldpath, newpath) #define mkdir(pathname, mode) swoole_coroutine_iouring_mkdir(pathname, mode) #define unlink(pathname) swoole_coroutine_iouring_unlink(pathname) @@ -36,13 +37,14 @@ #define read(fd, buf, count) swoole_coroutine_read(fd, buf, count) #define write(fd, buf, count) swoole_coroutine_write(fd, buf, count) #define lseek(fd, offset, whence) swoole_coroutine_lseek(fd, offset, whence) -#define readlink(fd, buf, size) swoole_coroutine_readlink(fd, buf, size) +#define readlink(pathname, buf, size) swoole_coroutine_readlink(pathname, buf, size) #define unlink(pathname) swoole_coroutine_unlink(pathname) #define mkdir(pathname, mode) swoole_coroutine_mkdir(pathname, mode) #define rmdir(pathname) swoole_coroutine_rmdir(pathname) #define rename(oldpath, newpath) swoole_coroutine_rename(oldpath, newpath) #define fsync(fd) swoole_coroutine_fsync(fd) #define fdatasync(fd) swoole_coroutine_fdatasync(fd) +#define ftruncate(fd, length) swoole_coroutine_ftruncate(fd, length) #endif #ifdef HAVE_IOURING_STATX diff --git a/src/coroutine/hook.cc b/src/coroutine/hook.cc index fdbfcbf6e2..7fc54ca4b0 100644 --- a/src/coroutine/hook.cc +++ b/src/coroutine/hook.cc @@ -45,6 +45,51 @@ using swoole::Iouring; static std::unordered_map> socket_map; static std::mutex socket_map_lock; +static std::unordered_map fd_to_coroutine; + +// clang-format off +#define SW_COROUTINE_FD_PROTECT(fd) \ + do { \ + auto coroutine = Coroutine::get_current(); \ + fd_to_coroutine[fd] = coroutine->get_cid(); \ + } while (0) + +#define SW_COROUTINE_FD_UNPROTECT(fd, close_function) \ + do { \ + ON_SCOPE_EXIT { \ + auto iter = fd_to_coroutine.find(fd); \ + auto coroutine = Coroutine::get_current(); \ + if (sw_likely(iter != fd_to_coroutine.end() && iter->second == coroutine->get_cid())) { \ + fd_to_coroutine.erase(iter); \ + } \ + }; \ + { close_function; } \ + } while (0); + +#define SW_COROUTINE_FD_CHECK_START(fd) \ + do { \ + auto coroutine = Coroutine::get_current(); \ + long cid = coroutine->get_cid(); \ + auto iter = fd_to_coroutine.find(fd); \ + \ + if (sw_likely(iter != fd_to_coroutine.end())) { \ + if (sw_unlikely(iter->second != cid)) { \ + swoole_fatal_error(SW_ERROR_CO_HAS_BEEN_BOUND, \ + "fd#%d has already been bound to another coroutine#%ld, " \ + "sharing the same fd across multiple coroutines is not allowed.", \ + fd, \ + iter->second); \ + } \ + } else { \ + fd_to_coroutine.emplace(fd, coroutine->get_cid()); \ + } \ + \ + do { + +#define SW_COROUTINE_FD_CHECK_END() \ + } while (0); \ + } while (0) +// clang-format on static sw_inline bool is_no_coro() { return SwooleTG.reactor == nullptr || !Coroutine::get_current(); @@ -196,9 +241,12 @@ int swoole_coroutine_open(const char *pathname, int flags, mode_t mode) { return open(pathname, flags, mode); } - int ret = -1; - async([&]() { ret = open(pathname, flags, mode); }); - return ret; + int fd = -1; + async([&]() { fd = open(pathname, flags, mode); }); + if (sw_likely(fd > 0)) { + SW_COROUTINE_FD_PROTECT(fd); + } + return fd; } int swoole_coroutine_close_file(int fd) { @@ -207,7 +255,9 @@ int swoole_coroutine_close_file(int fd) { } int ret = -1; - async([&]() { ret = close(fd); }); + SW_COROUTINE_FD_CHECK_START(fd) + SW_COROUTINE_FD_UNPROTECT(fd, async([&]() { ret = close(fd); })) + SW_COROUTINE_FD_CHECK_END(); return ret; } @@ -260,7 +310,10 @@ ssize_t swoole_coroutine_read(int sockfd, void *buf, size_t count) { .nonblock = 1, .read_timeout = -1, }; + + SW_COROUTINE_FD_CHECK_START(sockfd) async([&]() { ret = sock.read_sync(buf, count); }); + SW_COROUTINE_FD_CHECK_END(); return ret; } @@ -280,7 +333,10 @@ ssize_t swoole_coroutine_write(int sockfd, const void *buf, size_t count) { .nonblock = 1, .write_timeout = -1, }; + + SW_COROUTINE_FD_CHECK_START(sockfd) async([&]() { ret = sock.write_sync(buf, count); }); + SW_COROUTINE_FD_CHECK_END(); return ret; } @@ -290,7 +346,9 @@ off_t swoole_coroutine_lseek(int fd, off_t offset, int whence) { } off_t retval = -1; + SW_COROUTINE_FD_CHECK_START(fd) async([&]() { retval = lseek(fd, offset, whence); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -300,7 +358,9 @@ int swoole_coroutine_fstat(int fd, struct stat *statbuf) { } int retval = -1; + SW_COROUTINE_FD_CHECK_START(fd) async([&]() { retval = fstat(fd, statbuf); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -401,6 +461,10 @@ FILE *swoole_coroutine_fopen(const char *pathname, const char *mode) { FILE *retval = nullptr; async([&]() { retval = fopen(pathname, mode); }); + if (sw_likely(retval)) { + int fd = fileno(retval); + SW_COROUTINE_FD_PROTECT(fd); + } return retval; } @@ -410,7 +474,9 @@ FILE *swoole_coroutine_fdopen(int fd, const char *mode) { } FILE *retval = nullptr; + SW_COROUTINE_FD_CHECK_START(fd) async([&]() { retval = fdopen(fd, mode); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -419,8 +485,15 @@ FILE *swoole_coroutine_freopen(const char *pathname, const char *mode, FILE *str return freopen(pathname, mode, stream); } + int fd = fileno(stream); FILE *retval = nullptr; - async([&]() { retval = freopen(pathname, mode, stream); }); + SW_COROUTINE_FD_CHECK_START(fd) + SW_COROUTINE_FD_UNPROTECT(fd, async([&]() { retval = freopen(pathname, mode, stream); })) + if (sw_likely(retval)) { + int fd = fileno(retval); + SW_COROUTINE_FD_PROTECT(fd); + } + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -430,7 +503,10 @@ size_t swoole_coroutine_fread(void *ptr, size_t size, size_t nmemb, FILE *stream } size_t retval = 0; + int fd = fileno(stream); + SW_COROUTINE_FD_CHECK_START(fd) async([&]() { retval = fread(ptr, size, nmemb, stream); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -440,7 +516,10 @@ size_t swoole_coroutine_fwrite(const void *ptr, size_t size, size_t nmemb, FILE } size_t retval = 0; + int fd = fileno(stream); + SW_COROUTINE_FD_CHECK_START(fd) async([&]() { retval = fwrite(ptr, size, nmemb, stream); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -450,7 +529,10 @@ char *swoole_coroutine_fgets(char *s, int size, FILE *stream) { } char *retval = nullptr; + int fd = fileno(stream); + SW_COROUTINE_FD_CHECK_START(fd) async([&]() { retval = fgets(s, size, stream); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -460,7 +542,10 @@ int swoole_coroutine_fputs(const char *s, FILE *stream) { } int retval = -1; + int fd = fileno(stream); + SW_COROUTINE_FD_CHECK_START(fd) async([&]() { retval = fputs(s, stream); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -470,7 +555,10 @@ int swoole_coroutine_feof(FILE *stream) { } int retval = -1; + int fd = fileno(stream); + SW_COROUTINE_FD_CHECK_START(fd) async([&]() { retval = feof(stream); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -480,7 +568,10 @@ int swoole_coroutine_fflush(FILE *stream) { } int retval = -1; + int fd = fileno(stream); + SW_COROUTINE_FD_CHECK_START(fd) async([&]() { retval = fflush(stream); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -490,7 +581,10 @@ int swoole_coroutine_fclose(FILE *stream) { } int retval = -1; - async([&]() { retval = fclose(stream); }); + int fd = fileno(stream); + SW_COROUTINE_FD_CHECK_START(fd) + SW_COROUTINE_FD_UNPROTECT(fd, async([&]() { retval = fclose(stream); })) + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -501,6 +595,10 @@ DIR *swoole_coroutine_opendir(const char *name) { DIR *retval = nullptr; async([&]() { retval = opendir(name); }); + if (sw_likely(retval)) { + int fd = dirfd(retval); + SW_COROUTINE_FD_PROTECT(fd); + } return retval; } @@ -510,9 +608,10 @@ struct dirent *swoole_coroutine_readdir(DIR *dirp) { } struct dirent *retval; - + int fd = dirfd(dirp); + SW_COROUTINE_FD_CHECK_START(fd) async([&retval, dirp]() { retval = readdir(dirp); }); - + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -522,7 +621,10 @@ int swoole_coroutine_closedir(DIR *dirp) { } int retval = -1; - async([&]() { retval = closedir(dirp); }); + int fd = dirfd(dirp); + SW_COROUTINE_FD_CHECK_START(fd) + SW_COROUTINE_FD_UNPROTECT(fd, async([&]() { retval = closedir(dirp); })) + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -600,7 +702,9 @@ int swoole_coroutine_fsync(int fd) { } int retval = -1; + SW_COROUTINE_FD_CHECK_START(fd) async([&]() { retval = fsync(fd); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -614,11 +718,25 @@ int swoole_coroutine_fdatasync(int fd) { } int retval = -1; + SW_COROUTINE_FD_CHECK_START(fd) #ifndef HAVE_FDATASYNC async([&]() { retval = fsync(fd); }); #else async([&]() { retval = fdatasync(fd); }); #endif + SW_COROUTINE_FD_CHECK_END(); + return retval; +} + +int swoole_coroutine_ftruncate(int fd, off_t length) { + if (sw_unlikely(is_no_coro())) { + return ftruncate(fd, length); + } + + int retval = -1; + SW_COROUTINE_FD_CHECK_START(fd) + async([&]() { retval = ftruncate(fd, length); }); + SW_COROUTINE_FD_CHECK_END(); return retval; } @@ -627,28 +745,52 @@ int swoole_coroutine_iouring_open(const char *pathname, int flags, mode_t mode) if (sw_unlikely(is_no_coro())) { return open(pathname, flags, mode); } - return Iouring::open(pathname, flags, mode); + + int fd = Iouring::open(pathname, flags, mode); + if (sw_likely(fd > 0)) { + SW_COROUTINE_FD_PROTECT(fd); + } + return fd; } int swoole_coroutine_iouring_close_file(int fd) { if (sw_unlikely(is_no_coro())) { return close(fd); } - return Iouring::close(fd); + + SW_COROUTINE_FD_CHECK_START(fd) + SW_COROUTINE_FD_UNPROTECT(fd, return Iouring::close(fd)) + SW_COROUTINE_FD_CHECK_END(); +} + +ssize_t swoole_coroutine_iouring_read(int fd, void *buf, size_t size) { + if (sw_unlikely(is_no_coro())) { + return read(fd, buf, size); + } + + SW_COROUTINE_FD_CHECK_START(fd) + return Iouring::read(fd, buf, size); + SW_COROUTINE_FD_CHECK_END(); } -ssize_t swoole_coroutine_iouring_read(int sockfd, void *buf, size_t size) { +ssize_t swoole_coroutine_iouring_write(int fd, const void *buf, size_t size) { if (sw_unlikely(is_no_coro())) { - return read(sockfd, buf, size); + return write(fd, buf, size); } - return Iouring::read(sockfd, buf, size); + + SW_COROUTINE_FD_CHECK_START(fd) + return Iouring::write(fd, buf, size); + SW_COROUTINE_FD_CHECK_END(); } -ssize_t swoole_coroutine_iouring_write(int sockfd, const void *buf, size_t size) { +off_t swoole_coroutine_iouring_lseek(int fd, off_t offset, int whence) { if (sw_unlikely(is_no_coro())) { - return write(sockfd, buf, size); + return lseek(fd, offset, whence); } - return Iouring::write(sockfd, buf, size); + + SW_COROUTINE_FD_CHECK_START(fd) + return lseek(fd, offset, whence); + SW_COROUTINE_FD_CHECK_END(); } int swoole_coroutine_iouring_rename(const char *oldpath, const char *newpath) { @@ -677,7 +819,10 @@ int swoole_coroutine_iouring_fstat(int fd, struct stat *statbuf) { if (sw_unlikely(is_no_coro())) { return fstat(fd, statbuf); } + + SW_COROUTINE_FD_CHECK_START(fd) return Iouring::fstat(fd, statbuf); + SW_COROUTINE_FD_CHECK_END(); } int swoole_coroutine_iouring_stat(const char *path, struct stat *statbuf) { @@ -707,14 +852,20 @@ int swoole_coroutine_iouring_fsync(int fd) { if (sw_unlikely(is_no_coro())) { return fsync(fd); } + + SW_COROUTINE_FD_CHECK_START(fd) return Iouring::fsync(fd); + SW_COROUTINE_FD_CHECK_END(); } int swoole_coroutine_iouring_fdatasync(int fd) { if (sw_unlikely(is_no_coro())) { return fdatasync(fd); } + + SW_COROUTINE_FD_CHECK_START(fd) return Iouring::fdatasync(fd); + SW_COROUTINE_FD_CHECK_END(); } #endif diff --git a/tests/swoole_http_client_coro/download_filename_bug.phpt b/tests/swoole_http_client_coro/download_filename_bug.phpt index 7bf17f36ab..dd482521f8 100644 --- a/tests/swoole_http_client_coro/download_filename_bug.phpt +++ b/tests/swoole_http_client_coro/download_filename_bug.phpt @@ -43,6 +43,7 @@ function download($pm, $fileName) $client->set(['timeout' => 5]); $client->download('/', $fileName); + Assert::true(file_get_contents(TEST_IMAGE) == file_get_contents($fileName)); } $pm = new SwooleTest\ProcessManager; diff --git a/tests/swoole_runtime/file_hook/coroutine_fd_protect1.phpt b/tests/swoole_runtime/file_hook/coroutine_fd_protect1.phpt new file mode 100644 index 0000000000..a50ff12316 --- /dev/null +++ b/tests/swoole_runtime/file_hook/coroutine_fd_protect1.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_runtime/file_hook: protect fd across multiple coroutines - 1 +--SKIPIF-- + +--FILE-- + 32, + 'iouring_entries' => 30000, + ]; + swoole_async_set($setting); +} + +$fd = fopen(__FILE__, 'r+'); +run(function() use ($fd) { + $waitGroup = new WaitGroup(); + go(function() use ($waitGroup, $fd) { + $waitGroup->add(); + $content = fread($fd, filesize(__FILE__)); + Assert::True(strlen($content) == filesize(__FILE__)); + $waitGroup->done(); + }); + + sleep(1); + go(function() use ($waitGroup, $fd) { + $waitGroup->add(); + fwrite($fd, 'aaaaaaaaaa'); + $waitGroup->done(); + }); + $waitGroup->wait(); +}); +?> +--EXPECTF-- +Fatal error: Uncaught Swoole\Error: fd#%d has already been bound to another coroutine#%d, sharing the same fd across multiple coroutines is not allowed. in %s:%s +Stack trace: +#0 %s(%d): fwrite(%s) +#1 %s +#2 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/tests/swoole_runtime/file_hook/coroutine_fd_protect2.phpt b/tests/swoole_runtime/file_hook/coroutine_fd_protect2.phpt new file mode 100644 index 0000000000..22eef1f71a --- /dev/null +++ b/tests/swoole_runtime/file_hook/coroutine_fd_protect2.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_runtime/file_hook: protect fd across multiple coroutines - 2 +--SKIPIF-- + +--FILE-- + 32, + 'iouring_entries' => 30000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL, + ]; + swoole_async_set($setting); +} + +$fd = fopen(__FILE__, 'r+'); +run(function() use ($fd) { + $waitGroup = new WaitGroup(); + go(function() use ($waitGroup, $fd) { + $waitGroup->add(); + $content = fread($fd, filesize(__FILE__)); + Assert::True(strlen($content) == filesize(__FILE__)); + $waitGroup->done(); + }); + + sleep(1); + go(function() use ($waitGroup, $fd) { + $waitGroup->add(); + fwrite($fd, 'aaaaaaaaaa'); + $waitGroup->done(); + }); + $waitGroup->wait(); +}); +?> +--EXPECTF-- +Fatal error: Uncaught Swoole\Error: fd#%d has already been bound to another coroutine#%d, sharing the same fd across multiple coroutines is not allowed. in %s:%s +Stack trace: +#0 %s(%d): fwrite(%s) +#1 %s +#2 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/tests/swoole_runtime/file_hook/coroutine_fd_protect3.phpt b/tests/swoole_runtime/file_hook/coroutine_fd_protect3.phpt new file mode 100644 index 0000000000..98dd1bfcb4 --- /dev/null +++ b/tests/swoole_runtime/file_hook/coroutine_fd_protect3.phpt @@ -0,0 +1,42 @@ +--TEST-- +swoole_runtime/file_hook: protect fd across multiple coroutines - 3 +--SKIPIF-- + +--FILE-- + 32, + 'iouring_entries' => 30000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL, + ]; + swoole_async_set($setting); +} + +run(function() { + $fd = fopen(__FILE__, 'r+'); + $waitGroup = new WaitGroup(); + go(function() use ($waitGroup, $fd) { + $waitGroup->add(); + $content = fread($fd, filesize(__FILE__)); + Assert::True(strlen($content) == filesize(__FILE__)); + $waitGroup->done(); + }); + $waitGroup->wait(); +}); +?> +--EXPECTF-- +Fatal error: Uncaught Swoole\Error: fd#%d has already been bound to another coroutine#%d, sharing the same fd across multiple coroutines is not allowed. in %s:%s +Stack trace: +#0 %s(%d): fread(%s) +#1 %s +#2 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/tests/swoole_runtime/file_hook/coroutine_fd_protect4.phpt b/tests/swoole_runtime/file_hook/coroutine_fd_protect4.phpt new file mode 100644 index 0000000000..ac0ea54ab0 --- /dev/null +++ b/tests/swoole_runtime/file_hook/coroutine_fd_protect4.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_runtime/file_hook: protect fd across multiple coroutines - 4 +--SKIPIF-- + +--FILE-- +add(); + while(($file = readdir($dir)) !== false) { + } + $waitGroup->done(); + }); + + sleep(1); + go(function() use ($waitGroup, $dir) { + $waitGroup->add(); + closedir($dir); + $waitGroup->done(); + }); + $waitGroup->wait(); +}); +?> +--EXPECTF-- +Fatal error: Uncaught Swoole\Error: fd#%d has already been bound to another coroutine#%d, sharing the same fd across multiple coroutines is not allowed. in %s:%s +Stack trace: +#0 %s(%d): closedir(%s) +#1 %s +#2 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/tests/swoole_runtime/file_hook/coroutine_fd_protect5.phpt b/tests/swoole_runtime/file_hook/coroutine_fd_protect5.phpt new file mode 100644 index 0000000000..59896edc09 --- /dev/null +++ b/tests/swoole_runtime/file_hook/coroutine_fd_protect5.phpt @@ -0,0 +1,38 @@ +--TEST-- +swoole_runtime/file_hook: protect fd across multiple coroutines - 5 +--SKIPIF-- + +--FILE-- + 32, + 'iouring_entries' => 30000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL, + ]; + swoole_async_set($setting); +} + +$fd = fopen(__FILE__, 'ar+'); +run(function() use ($fd) { + $waitGroup = new WaitGroup(); + go(function() use ($waitGroup, $fd) { + $waitGroup->add(); + for ($i = 0; $i < 100; $i++) { + fwrite($fd, 'aaaaaaaaaa'); + } + fclose($fd); + $waitGroup->done(); + }); + $waitGroup->wait(); +}); +?> +--EXPECTF-- diff --git a/tests/swoole_runtime/file_hook/coroutine_fd_protect6.phpt b/tests/swoole_runtime/file_hook/coroutine_fd_protect6.phpt new file mode 100644 index 0000000000..1e53673d7c --- /dev/null +++ b/tests/swoole_runtime/file_hook/coroutine_fd_protect6.phpt @@ -0,0 +1,46 @@ +--TEST-- +swoole_runtime/file_hook: protect fd across multiple coroutines - 6 +--SKIPIF-- + +--FILE-- + 32, + 'iouring_entries' => 30000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL, + ]; + swoole_async_set($setting); +} + +run(function() { + $data = 'aaaaaaa'; + $content1 = ''; + $content2 = ''; + $waitGroup = new WaitGroup(); + go(function() use ($waitGroup, &$content1) { + $waitGroup->add(); + $content1 = file_get_contents(__FILE__); + $waitGroup->done(); + }); + + sleep(1); + go(function() use ($waitGroup, &$content2, $data) { + $waitGroup->add(); + file_put_contents(__FILE__, $data, FILE_APPEND); + $content2 = file_get_contents(__FILE__); + $waitGroup->done(); + }); + $waitGroup->wait(); + Assert::true($content1 . $data == $content2); +}); +?> +--EXPECTF-- diff --git a/tests/swoole_runtime/file_hook/coroutine_fd_protect7.phpt b/tests/swoole_runtime/file_hook/coroutine_fd_protect7.phpt new file mode 100644 index 0000000000..56a50f5d9a --- /dev/null +++ b/tests/swoole_runtime/file_hook/coroutine_fd_protect7.phpt @@ -0,0 +1,40 @@ +--TEST-- +swoole_runtime/file_hook: protect fd across multiple coroutines - 7 +--SKIPIF-- + +--FILE-- +add(); + fgets($fp); + $waitGroup->done(); + }); + + sleep(1); + go(function() use ($waitGroup, $fp) { + $waitGroup->add(); + fclose($fp); + $waitGroup->done(); + }); + $waitGroup->wait(); + Assert::true($content1 . $data == $content2); +}); +?> +--EXPECTF-- +Fatal error: Uncaught Swoole\Error: fd#%d has already been bound to another coroutine#%d, sharing the same fd across multiple coroutines is not allowed. in %s:%s +Stack trace: +#0 %s(%d): fclose(%s) +#1 %s +#2 {main} + thrown in %s on line %d \ No newline at end of file diff --git a/tests/swoole_runtime/file_hook/coroutine_fd_protect8.phpt b/tests/swoole_runtime/file_hook/coroutine_fd_protect8.phpt new file mode 100644 index 0000000000..f8fe3260da --- /dev/null +++ b/tests/swoole_runtime/file_hook/coroutine_fd_protect8.phpt @@ -0,0 +1,49 @@ +--TEST-- +swoole_runtime/file_hook: protect fd across multiple coroutines - 8 +--SKIPIF-- + +--FILE-- + 32, + 'iouring_entries' => 30000, + 'iouring_flag' => SWOOLE_IOURING_SQPOLL, + ]; + swoole_async_set($setting); +} + +$fd = fopen(__FILE__, 'r+'); +run(function() use ($fd) { + $waitGroup = new WaitGroup(); + go(function() use ($waitGroup, $fd) { + $waitGroup->add(); + $content = fread($fd, filesize(__FILE__)); + Assert::True(strlen($content) == filesize(__FILE__)); + $waitGroup->done(); + }); + + go(function() use ($waitGroup, $fd) { + $waitGroup->add(); + fwrite($fd, 'aaaaaaaaaa'); + $waitGroup->done(); + }); + $waitGroup->wait(); +}); +?> +--EXPECTF-- +Fatal error: Uncaught Swoole\Error: fd#%d has already been bound to another coroutine#%d, sharing the same fd across multiple coroutines is not allowed. in %s:%s +Stack trace: +#0 %s(%d): fread(%s) +#1 %s +#2 {main} + thrown in %s on line %d \ No newline at end of file