php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #75538 stream_set_blocking() always returning false for files on windows
Submitted: 2017-11-19 16:39 UTC Modified: 2018-11-19 09:20 UTC
Votes:1
Avg. Score:3.0 ± 0.0
Reproduced:0 of 0 (0.0%)
From: samuel dot chemla at orange dot com Assigned: ab (profile)
Status: Closed Package: Streams related
PHP Version: 7.1.11 OS: Windows
Private report: No CVE-ID: None
 [2017-11-19 16:39 UTC] samuel dot chemla at orange dot com
Description:
------------
Tested on PHP 5.6, PHP 7.0.24 & PHP 7.1.11.

stream_set_blocking() return always false for files (used with fopen()) on windows.
It seems to work on linux.

This is related to an old bug marked as "not a bug", but I prefered opening this new one. https://bugs.php.net/bug.php?id=50856
Is this really not a bug? If so, then the doc shoud be updated to clarify why.

IMHO this is a bug because non blocking local file streams is a desired feature, even on windows.
Use case: dealing with multiple files reading and/or writing in parallel

Test script:
---------------
$fileRes = fopen("foo.txt", 'wb');
if ($fileRes === false) {
    throw new Exception();
}
var_dump(stream_set_blocking($fileRes, 0));

Expected result:
----------------
bool(true)

Actual result:
--------------
bool(false)

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-11-20 19:36 UTC] ab@php.net
-Status: Open +Status: Feedback
 [2017-11-20 19:36 UTC] ab@php.net
Thanks for the report. Could you give an extended example in PHP on the topic you've mentioned?

Thanks
 [2017-11-21 09:06 UTC] samuel dot chemla at orange dot com
-Status: Feedback +Status: Open
 [2017-11-21 09:06 UTC] samuel dot chemla at orange dot com
Hi,
I'm building a server with https://github.com/reactphp/socket.
This server accepts connection from many clients and stream files to them (or receive files from them).

As it handles all clients connections in one loop, all the code must be non blocking to achieve optimal performance.

The most straightforward way to serve files is then to use https://github.com/reactphp/stream with a fopen() resource, configured in a non blocking mode.

By the way react/stream doc says fopen() may block, but I see a non documented "n" mode when using fopen (see https://github.com/php/php-src/blob/2283b6ff534e173bc56dc73137fedd19f37fc979/main/streams/plain_wrapper.c#L105)
if (strchr(mode, 'n')) {
		flags |= O_NONBLOCK;
}
It actually works on linux, but not on windows.
$fileRes = fopen("bar.txt", 'wbn');
if ($fileRes === false) {
        throw new Exception();
}
var_dump(stream_get_meta_data($fileRes));
will return :
array(9) {
  ...
  'blocked' =>
  bool(true)
  ...
}
(Maybe I should file another report on php documentation for this?)


https://github.com/reactphp/filesystem offers a non blocking filesystem acces, but it seems very experimental, complex, and require eio extention or create processes to read files.
I was looking for a simple solution with pure php (and fopen)
 [2017-11-21 10:16 UTC] samuel dot chemla at orange dot com
I made a mistake in my previous comment, you must read
array(9) {
  ...
  'blocked' =>
  bool(false)
  ...
}
(I copied/pasted the result without "n" mode)
 [2017-11-21 15:01 UTC] ab@php.net
Thanks for the further explanation. With reactphp - yep, the topic was discussed at least with Bob several times. However it regards more to standard file descriptors and pipes. Regular files are a different matter.

What i was basically asking for was a bare implementation in PHP as a show case to evaluate crossplatform. A flag being true/false is a bit insufficient :) Regular files are always available. The I/O performance depends on quite some factors like caching, compression, encryption, etc. Libs like libeven or libuv use also threading besidses the libc APIs. Just by changing a flag passed to the C API likely won't guarantee anything. Even if done, the system can still decide to go synchronously. An option here could be to cache those files in RAM, if possible.

So in first place - it is barely a bug in PHP, disregard the OS. The socket APIs support non-blocking crossplatform. It might be for sure a significant chunk of work to spread this onto regular files, standard descriptors and pipes. For what it matters, I'd be not really convinced ATM it is worth and specially is possible without significant userspace API rework :( Perhaps libuv and other bindings are a worthy option in this regard. At the current stage, even if the non-blocking flag can be changed on some platforms, it doesn't guarantee non-blocking with the regard to possible obstacles.

Thanks.
 [2017-11-22 10:04 UTC] samuel dot chemla at orange dot com
Hi,
Thanks for your answer.
I didn't realize the implication of this, and I understand it's not a priority.
I'm not C developper, but I would be happy to help on PHP side (testing?)...

Maybe we should at least update the doc to mention that non blocking file acces cannot (yet :-)) be guaranteed.
 [2018-01-23 02:53 UTC] mattficken@php.net
stream_set_blocking() for local files can't be supported on Windows.

I have updated the documentation notes to state that.

This bug can probably be closed now.
 [2018-01-26 16:47 UTC] ab@php.net
-Status: Open +Status: Closed -Assigned To: +Assigned To: ab
 [2018-01-26 16:47 UTC] ab@php.net
Closing as per request. Thanks for documenting, Matt.
 [2018-11-09 20:59 UTC] samel dot chemla at orange dot com
Hi!

I know this is an old topic, but I just saw the doc http://php.net/manual/en/function.stream-set-blocking.php was updated with that note: "On Windows, this has no affect on local files. Non-blocking IO for local files is not supported on Windows."

My understanding is that stream_set_blocking does *not* work for regular files, whatever the OS is.

Then if this is correct, then:
* the windows note should be removed from the documentation,
* this statement should be fixed: "This function works for any stream that supports non-blocking mode (currently, regular files and socket streams)."
* stream_set_blocking() should return false, whatever the OS is. IE linux implementation should be fixed because actually it returns true.

Can you clarify?
Regards.
 [2018-11-16 13:54 UTC] samuel dot chemla at orange dot com
-Status: Closed +Status: Assigned
 [2018-11-16 13:54 UTC] samuel dot chemla at orange dot com
I'm reopening this because I think it requires clarification
 [2018-11-16 17:06 UTC] requinix@php.net
-Status: Assigned +Status: Closed
 [2018-11-16 17:06 UTC] requinix@php.net
> My understanding is that stream_set_blocking does *not* work for regular files, whatever the OS is.
Linux and BSD may or may not support it, but all seem to support setting the flag.

http://man7.org/linux/man-pages/man2/open.2.html
> When possible, the file is opened in nonblocking mode.
> Note that this flag has no effect for regular files and block devices

https://linux.die.net/man/2/open
> When possible, the file is opened in nonblocking mode.

https://www.freebsd.org/cgi/man.cgi?sektion=2&query=open
> O_NONBLOCK   do not block on open

But Windows' _open doesn't have a "_O_NONBLOCK" or other non-blocking flag.
https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx

In fact as far as I can tell, it doesn't have an fcntl (though winsock has something equivalent for sockets?) or O_NONBLOCK flag anywhere. I think that's the problem: not that Windows doesn't allow setting non-blocking on files but that it can't.
 [2018-11-19 09:02 UTC] samuel dot chemla at orange dot com
Well, in http://man7.org/linux/man-pages/man2/open.2.html:
>O_NONBLOCK or O_NDELAY
              When possible, the file is opened in nonblocking mode.
              Neither the open() nor any subsequent operations on the file
              descriptor which is returned will cause the calling process to
              wait.

              Note that this flag has no effect for regular files and block
              devices; 

"This flag has no effect for regular files".

I've done some tests on linux, reading files.
* stream_set_blocking($handle, false) return true, so i expected it to work.
* then i tries fread($handle, $length) with many different $length sizes, and fread always returned the required length, event on a busy system, with a slow rotating drive
--> So I assume stream_set_blocking() do **not** work on linux either.

Am I wrong?
Can you provide a working example of non blocking file access on linux?
 [2018-11-19 09:20 UTC] requinix@php.net
> Can you provide a working example of non blocking file access on linux?
Feel free to submit a bug report on how stream_set_blocking() with a local file, on Linux, appears to work when ultimately the operating system is not respecting the setting. Because that's not what this report was about.
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Dec 26 19:01:30 2024 UTC