Is a file available to be opened? - objective-c

Short version: I think I'm asking for a file too soon, but it's pretending like it's ready. Am I missing something?
Slightly longer version: I am writing files to disk. Before I do so, I have the user add some meta data, including the new file name. Once the user is done, the screen goes away and the program writes the file to disk. The user can then look at a list of files. That list is generated by reading the contents of a folder. The new file is in the list of files, but when I try to extract info from the file to display (e.g. file size) the program crashes. As best as I can tell, the crash occurs because, while the file is there in name, it's not available to be read. (By the way, these are small files - a few hundred k.)
First, is it possible that a file shows up in the directory but isn't all there yet?
a
And second, if so, how do I check to see if the file is ready to be read?
Thanks much.
UPDATE:
Thanks. I'll try to add more info. I'm recording an audio file with AVAudioRecorder. The init line is:
soundrecording = [[AVAudioRecorder alloc] initWithURL:url settings:recordSettings error:&error];
The program goes through it's UI updates and metering and all that. When the audio is stopped, I call:
[soundrecording stop];
and when everything else is updated and ready to move on, I call:
[soundrecording release];
soundrecording=NULL;
As far as I understand, this should take care of releasing the file, yes?
Thanks again.

The first thing I would do is confirm that you're right about the file not being ready yet. To do that, sleep your program for a second or two after writing and before reading. A few hundred KB should not take longer than that to be ready.
If it still fails, my guess is that you haven't closed the file handle that you used to write it. It may be unready for reading because the file system thinks you might keep writing.
Usually, the way to check to see if a file is ready is to attempt to open it. If that succeeds, you can read it. Or if it fails with an error, you can handle the error gracefully:
In a command-line utility, you might print the error and quit, and the user could try again.
If it's a background program that should not quit, like a server, you could log the error. You might also try again automatically after a delay. If it's a big deal kind of error, you might want to have the program email you about it.
In an GUI window app, you probably want to show an error dialog or panel, and then give the user an opportunity to retry.

Now that you have added sample code, I can say some more.
First, the class reference seems to say that the stop method will close the file. However it also seems to suggest that there is an underlying audio session going on, and possibly some conversion. I think I recall that the iPhone's Voice Notes app, which probably uses this API, has to do some work to compress a long recording after it's completed.
So I support your hunch. I think that your file may not be closed yet, but on another thread that is processing the recorded data into a proper format to save.
You probably want to set a NSTimer to attempt to open the file every second or so, so that your user interface can perk up when it's done. You probably want to show a "Please wait" sort of message in the meantime, or otherwise let the user know it's working.

Related

Sandboxed Mac app exhausting security scoped URL resources

I am developing a Mac application that prompts the user for files using the NSOpenPanel. The application is sandboxed (testing on OSX 10.9.4). I noticed that if I open a large amount of files (~3000), the open panel starts to emit errors to the log. This also happens if I try to open less amount of files in chucks for several times.
After the errors start to appear the first time, every time the NSOpenPanel is used again to open files, no matter for how many files, these errors will be generated again (until the application is closed).
The error message looks like this:
TestPanel[98508:303] __41+[NSSavePanel _consumeSandboxExtensions:]_block_invoke: sandbox_consume_fs_extension failed
One line for each file I try to open.
I managed to reproduce this behavior with a simple app: A sandboxed application with a single button invoking the following code:
NSOpenPanel* panel = [NSOpenPanel openPanel];
[panel setAllowsMultipleSelection:YES];
[panel setCanChooseDirectories:NO];
[panel setCanChooseFiles:YES];
[panel beginSheetModalForWindow:[self window] completionHandler:^(NSInteger result) {
NSLog(#"%lu", [panel.URLs count]);
}];
The errors appear before the code reaches the completion handler.
It seems that I can still get the URLs from the panel in the completion handler but it really pollutes the system log.
EDIT:
Seems that this problem is not directly related to the NSOpenPanel/NSSavePanel panels. A very similar thing happens when using drap/drop with files. Something like this:
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender {
...
NSPasteboard *pboard = [sender draggingPasteboard];
if ([[pboard types] containsObject:NSURLPboardType]) {
NSArray *urls = [pboard readObjectsForClasses:#[[NSURL class]] options:nil];
}
...
}
This will generate the following log messages when dragging a large amount of files (the "magic" number seems to be somewhere around 2900):
Consume sandbox extension for itemIdentifier (2937) from pasteboard failed!
As with the NSOpenPanel, after the first occurrence of this, every single file dropped will generate the same error in the log.
EDIT 2:
#mahal tertin's reply pointed me to the right direction. The problem is indeed with the number of files and the fact that security scoped URL resources are limited.
However, there seems to be no reasonable solution found. The problem is that when the user clicks "OK" on the NSOpenPanel (or drops the files on a drag&drop aware control), behind the scenes the OS already attempts to create these security scoped URLs and implicitly calls startAccessingSecurityScopedResource for you. So if the user attempts to open more files than the limit, the resources are exhausted and the only option is to close and restart the application.
Calling stopAccessingSecurityScopedResource on the returned URLs seem to free the resources however this solution was discouraged by Apple's representative on the official developers forums (link is behind login).
It seems that the app is at the mercy of the user not to open too many files. And that is not even at once, since there is no approved way to release these resources. You can warn the user in documentation or even with an in-app alert but there is no way to prevent them from messing up the app and forcing a restart.
So if the app runs long enough and the user keeps opening files, the app will eventually become unusable.
Still looking for a reasonable solution for this.
After searching high and low and asking in various places, I am going to close this question and conclude there is no answer or solution to this. I am posting the known information on this for future reference.
All the solutions suggested are just workarounds that may minimize the problem and try to guide the user toward not trying to open too many files. But there nothing that can be done to actually solve this.
Here are the known facts about this issue:
No matter what you do, the user can attempt to open too many files in the NSOpenPanel dialog and exhaust the security scoped URL resources
Once these resources are exhausted, it is not possible to open any more files for reading/writing. The application needs to be closed and reopened
Even if the user doesn't attempt to open too many files at once, the application may still exhaust these resources if it runs long enough and the user opens enough files over time since startAccessingSecurityScopedResource is called automatically for files opened with NSOpenPanel (or the drag/drop mechanism) and nothing ever closes these resources
Calling stopAccessingSecurityScopedResource on all URL retrieved by the open panel will free these resources but this practice is discouraged by Apple, saying it might not be compatible with future solutions
When you receive the list of URLs from NSOpenPanel (or drag/drop), there is no way to tell if all URLs were successfully accessed or if there are URLs that are over the limit and therefore invalid.
Apple is aware of this and may fix it in the future. It is still not fixed in 10.10 and of course, that will not help current applications running on current/previous OSX version.
It seems Apple has really dropped the ball on this one, the Sandbox implementation seems very sloppy and short sighted.
The behavior you experience is because the security scoped resources are limited:
NSURL - (BOOL)startAccessingSecurityScopedResource tells
If sufficient kernel resources are leaked, your app loses its ability
to add file-system locations to its sandbox...
The current limit is roughly what you experienced. See:
What are the current kernel resource limits on security-scoped bookmarks?
To prevent it:
only start accessing those SSBs you need at a given time and subsequently stop accessing them
start access not files but enclosing folders: ask the user not to choose files but a full folder. This will grant you access to the whole tree beneath that directory
on draggingEntered: show a NSOpenPanel with the enclosing directory(ies) to grant access

NSTask subprocess stuck in _dyld_start

I use NSTask to run my helper application. On 99% one my customer systems this works fine, but two got back to me letting me know it doesn't. One of them was nice enough to let me look into the issue per remote desktop.
I tried a lot of different NSPipe/NSFileHandle combination for StandardOutput/StandardError to make sure the problem is not related filling up these buffers. Example 1 and 2. My guess is that it is not related because it works fine on so many systems and _dyld_start is too early on in the application lifecycle to fill up StandardOutput/StandardError.
Other notes about the problem:
Launching the helper app from the terminal works fine.
Attaching and detaching the gdb on the stuck process and after-worth it works fine and when it finished NSTask picks up work after -waitUntilExit.
Using fork(2) and execv(3) instead of NSTask is able to launch and run the helper fine.
The parent process is sandboxed but I think previous reports where non-sandboxed on Mac OS X 10.6/10.7.
Screenshot of the process Sample from Activity Monitor:
Any clues or debugging tips to figure out why the helper is stuck in _dyld_start are welcome!
Since nobody answered, I am throwing a few ideas. Maybe one of them is the answer – only guessing – but since clues and tips are welcome, you could take a look at:
the list of the loaded libraries in the crash dump (there may be a clue there)
any error that would happen in the child process (after the fork). However, I see why it could be difficult to get back any post-fork error.
If I recall correctly, NSTask calls posix_spawn(2). This may be a clue, since using fork(2) and execv(3) seems working, you could focus on the differences between NSTask and the non-blocking alternative. Clearly, something is happening at the very beginning that prevents the child from executing properly.
Are you sure it is stuck and not crashed? As far as the user could tell, your app your app wouldn't look like it crashed. Only the child process would crash.
As a last resort, you could try to look for any Mach exception occuring (if
any, that would mean an error, which you wouldn't be able to
recover anyway. But it would provide valuable clues nonetheless).
You can tell willing custommers to send you their sysdiagnose. To this goal, ask them to hit Command + Option + Control + . + Shift to wait a few minutes. Soon after, their finder should pop a window to reveal a file named: sysdiagnose_timestamp_.tar.gz. Kindly ask them to mail it to you. Mine is around 5 MB. More details on the sysdiagnose man page.

Only receiving "File Written To" notifications from VDKQueue regardless of activity

I am trying to implement VDKQueue but only get ‘VDKQueueFileWrittenToNotification’ back as the notification regardless of the file activity in the watched folder. Deletes, file size changes all report back as this same message.
I think everything is set up OK, but maybe not…
[self.theQueueWatcher setDelegate:self];
self.theQueueWatcher.alwaysPostNotifications=YES;
[self.theQueueWatcher addPath:self.hotFolderPath notifyingAbout:VDKQueueNotifyDefault];
This is on 10.8.2.
Does anyone know if anything underlying in the OS has changed which would cause this? Or what I am missing?
After contacting the author of VDKQueue, he helpfully(seems like a nice guy) pointed out the purpose of kQueue, and therefore VDKQueue, was to watch an individual file for changes etc, not a folder as I was doing. So now starts the voyage into FSEvents which Bryan recommended was the best way to achieve this task.
Thanks Bryan.
Hope someone else finds this useful.

Obj-C, how can I log to a file how long a method took in seconds?

I envisage I'll run into problems as i haven't done this before.
I'm thinking that I can either define a date at the start of the method or initialise a class.
Then at the end of the method, call the commit method, which will write the time taken about with some sort of code to determine where the measurement was made.
Since you're crashing before the app finishes launching, so no code is going to fix this. If TestFlightApp isn't working, any other code-based solutions are likely to have the same problem.
As #dasblinkenlight noted, NSLog timestamps, so that's a really easy first step. Then you need to get the logs.
If possible, have your user install and run the iPhone Configuration Utility. Have her connect her device and select it from the Devices list. Then select Console and "Save Console As..." She can then mail it to you.

VB.NET - Read lines from command line to Windows form possible?

Hey Overflow, I have an application which serves as a user interface for a spartan/command line program.
I have the program running on a separate process, and my application monitors it to see if it is responding and how mush CPU it is utilising.
Now I have a list of files in my program (listbox) which are to be sent to the application, which happens fine. But I want to be able to read text from the com-line so as to determine when the first file has been processed.
Com-line says one of "selecting settings", "unsupported format" and "cannot be fixed".
What I want to be able to do is when it says one of these three things, remove item(0) in listbox1.
Is this possible?
I thought of programming an event which handles com_exe.print or something or other, if possible.
Read the standard output from the process.
MSDN Article
Theres an example of synchronous reading from the process in that article.
You might be able to do what you want using the AttachConsole API function as described here. However, maybe an easier alternative would be if you could pipe the output of the command line app to a text file and then your app could just parse the text file (assuming that the command line file wouldn't lock the file completely, I'm not sure about that).
If you don't know how to pipe the output, this page has quite a bit of information.