I'm not sure if this is the correct forum for this question, but it's a problem with FileManager used in a swift library opened in xcode (by opening the package file itself, rather than as a xcode framework) . I have an absolute path, and although the actual string describing it is irrelevant, the path does exist. If I change directory into the path in a terminal and do a find, I get the following:
However, if I run the following test code as a test for the library, I get an empty response:
let fm = FileManager.default
let url = URL(fileURLWithPath: path)
if let enumerator = fm.enumerator(at: url, includingPropertiesForKeys: nil) {
while let suburl = enumerator.nextObject() {
if suburl is URL {
print((suburl as! URL).path)
}
}
}
No files show up in the enumeration. However, if I put a sleep(4) immediately after the FileManager.default, then all the files and directories show up. It's almost as if the instance of fm returns immediately, but whatever is going on in the background has not finished.
Has anyone seen this problem before? Am I misunderstanding something, or is there a work around? Thanks in advance.
I added an "else" to the "if" which printed out a message for the case where the enumerator returns something other than a URL, and nothing is printed, so the enumerator returns null right away. Normally, I write the while statement as "while let suburl = enumerator.nextObject() as? URL", but I wanted to make sure the enumerator didn't return something else for this test.
Then I added a block that does the contentsOfDirectory(at: url, includingPropertiesForKeys: nil). Call the original block the "enumerator block" and this new block the "contents block".
If I put the contents block first, right after the fm = FileManager.default line, it throws an error saying the path couldn't be opened, but the enumerator block succeeds. If I put the contents block second, after the enumerator block, the enumerator block shows nothing and the contents block succeeds. All this is without the sleep of course, but it does seem consistent with that case. Pretty strange.
Well, I'm not sure exactly what that means in this case. I've got a swift library package that I had created before xcode enabled opening
the package file as package. It's just a flat library to be used in a command line executable, not in an app. Moreover, all this testing has been done through the xcode test procedures, and they can yield the previously noted random results, something that might not be expected in some kind of sandbox type of situation.
However, you're comment motivated me to do a bit more experimentation, so I went back to the command line and did a "swift test", where things are built in the local .build directory, and lo and behold, all the tests ran normally. This seems to be an xcode problem rather than a swift foundation problem, but it does make it difficult to use xcode in a package development environment.
This seems to be an xcode problem rather than a swift foundation problem
If you want to dig deeper into this, run fs_usage while running the test to see which file system call is actually returning the error. See the fs_usageman page for more on that tool.
If you want to dig deeper into this, run fs_usage while running the test to see which file system call is actually returning the error.
As suggested, and after a lot of experimentation, I ran
fs_usage -w xctest
There is a lot of output, opening libraries and such, but when it comes down to the lines where the path (as in the original post) is included, it does a stat64 on the path, then two getattrlist's on the path, and then an open, which fails with return code 4. If I coax the test to succeed, for example by sleeping awhile, then the same lines appear and the open succeeds followed by open lines for all the subpaths. Not sure what the return code for the open signifies (perhaps EINTR?).
Error 4 is indeed EINTR. That would explain the very odd symptoms you’re seeing (the fact that you can avoid the problem by adding a delay, the fact it only shows up under test, and so on).
You should definitely file a bug about this. Either the kernel needs to stop failing an open with EINTR [1] or Foundation needs to handle this error.
Please post your bug number, just for the record.
As far as workarounds are concerned, the obvious one — detecting this error as well as you can and retrying — should be fine IMO.
Share and Enjoy
Quinn “The Eskimo!” @ DTS @ Apple
[1] If I had a time machine, I’d go back in time and kill EINTR’s grandfather.
One thing that’d help is the actual fs_usage log that shows the EINTR error. Please add that as an attachment, and then note the line number of the EINTR in the bug report.